diff --git a/.gitignore b/.gitignore index 67204eec..0cd63730 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ core tags .deps/ +compat/.dirstamp aclocal.m4 autom4te.cache/ config.log @@ -19,3 +20,4 @@ tmate cscope.* ctags *.log +tmate.1.* diff --git a/.mailmap b/.mailmap index a7a401ff..b4956775 100644 --- a/.mailmap +++ b/.mailmap @@ -1,24 +1,36 @@ -Bob Beck beck -Igor Sobrado sobrado -Jacek Masiulaniec jacekm -Jason McIntyre jcm +Bob Beck beck +Claudio Jeker claudio +Igor Sobrado sobrado +Ingo Schwarze schwarze +Jacek Masiulaniec jacekm +Jason McIntyre jmc Joel Sing jsing -Marc Espie espie -Matthew Dempsky matthew -Matthias Kilian kili -Matthieu Herrb matthieu -Miod Vallat miod -Nicholas Marriott nicm -Nicholas Marriott no_author - -Okan Demirmen okan +Jonathan Gray jsg +Kenneth R Westerback krw +Marc Espie espie +Matthew Dempsky matthew +Matthias Kilian kili +Matthieu Herrb matthieu +Michael McConville mmcc +Miod Vallat miod +Nicholas Marriott Nicholas Marriott +Nicholas Marriott nicm +Nicholas Marriott no_author +Okan Demirmen okan Philip Guenther guenther -Pierre-Yves Ritschard pyr -Ray Lai ray +Pierre-Yves Ritschard pyr +Ray Lai ray Ryan McBride mcbride -Stefan Sperling stsp -Stuart Henderson sthen -Ted Unangst tedu -Theo Deraadt deraadt +Sebastian Benoit benno +Stefan Sperling stsp +Stuart Henderson sthen +Ted Unangst tedu +Theo de Raadt Theo Deraadt +Theo de Raadt deraadt Thomas Adam Thomas -William Yodlowsky william +Thomas Adam Thomas Adam +Thomas Adam n6tadam +Tim van der Molen tim +Tobias Stoeckmann tobias +Todd C Miller millert +William Yodlowsky william diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..a1d7e427 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: c +matrix: + include: + - compiler: gcc + - compiler: clang + env: CFLAGS="-g -O2" +before_install: + - sudo apt-get update -qq + - sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential +script: (CFLAGS= ./autogen.sh) && ./configure --enable-debug && make diff --git a/CHANGES b/CHANGES index dba58be0..db7be25b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,179 @@ +CHANGES FROM 2.0 to 2.1 18 October 2015 + +Incompatible Changes +==================== + +* Mouse-mode has been rewritten. There's now no longer options for: + - mouse-resize-pane + - mouse-select-pane + - mouse-select-window + - mode-mouse + + Instead there is just one option: 'mouse' which turns on mouse support + entirely. +* 'default-terminal' is now a session option. Furthermore, if this is set + to 'screen-*' then emulate what screen does. If italics are wanted, this + can be set to 'tmux' but this is still new and not necessarily supported + on all platforms with older ncurses installs. +* The c0-* options for rate-limiting have been removed. Instead, a backoff + approach is used. + +Normal Changes +============== + +* New formats: + - session_activity + - window_linked + - window_activity_format + - session_alerts + - session_last_attached + - client_pid + - pid +* 'copy-selection', 'append-selection', 'start-named-buffer' now understand + an '-x' flag to prevent it exiting copying mode. +* 'select-pane' now understands '-P' to set window/pane background colours. +* 'renumber-windows' now understands windows which are unlinked. +* 'bind' now understands multiple key tables. Allows for key-chaining. +* 'select-layout' understands '-o' to undo the last layout change. +* The environment is updated when switching sessions as well as attaching. +* 'select-pane' now understands '-M' for marking a pane. This marked pane + can then be used with commands which understand src-pane specifiers + automatically. +* If a session/window target is prefixed with '=' then only an exact match + is considered. +* 'move-window' understands '-a'. +* 'update-environment' understands '-E' when attach-session is used on an + already attached client. +* 'show-environment' understands '-s' to output Bourne-compatible commands. +* New option: 'history-file' to save/restore command prompt history. +* Copy mode is exited if the history is cleared whilst in copy-mode. +* 'copy-mode' learned '-e' to exit copy-mode when scrolling to end. + +CHANGES FROM 1.9a to 2.0 6 March 2015 + +Incompatible Changes +==================== + +* The choose-list command has been removed. +* 'terminal-overrides' is now a server option, not a session option. +* 'message-limit' is now a server option, not a session option. +* 'monitor-content' option has been removed. +* 'pane_start_path' option has been removed. +* The "info" mechanism which used to (for some commands) provide feedback + has been removed, and like other commands, they now produce nothing on + success. + +Normal Changes +============== + +* tmux can now write an entry to utmp if the library 'utempter' is present + at compile time. +* set-buffer learned append mode (-a), and a corresponding + 'append-selection' command has been added to copy-mode. +* choose-mode now has the following commands which can be bound: + - start-of-list + - end-of-list + - top-line + - bottom-line + +* choose-buffer now understands UTF-8. +* Pane navigation has changed: + - The old way of always using the top or left if the choice is ambiguous. + - The new way of remembering the last used pane is annoying if the + layout is balanced and the leftmost is obvious to the user (because + clearly if we go right from the top-left in a tiled set of four we want + to end up in top-right, even if we were last using the bottom-right). + + So instead, use a combination of both: if there is only one possible + pane alongside the current pane, move to it, otherwise choose the most + recently used of the choice. +* 'set-buffer' can now be told to give names to buffers. +* The 'new-session', 'new-window', 'split-window', and 'respawn-pane' commands + now understand multiple arguments and handle quoting problems correctly. +* 'capture-pane' understands '-S-' to mean the start of the pane, and '-E-' to + mean the end of the pane. +* Support for function keys beyond F12 has changed. The following explains: + - F13-F24 are S-F1 to S-F12 + - F25-F36 are C-F1 to C-F12 + - F37-F48 are C-S-F1 to C-S-F12 + - F49-F60 are M-F1 to M-F12 + - F61-F63 are M-S-F1 to M-S-F3 + + Therefore, F13 becomes a binding of S-F1, etc. +* Support using pane id as part of session or window specifier (so % means + session-of-%1 or window-of-%1) and window id as part of session + (so @1 means session-of-@1). +* 'copy-pipe' command now understands formats via -F +* 'if-shell' command now understands formats via -F +* 'split-window' and 'join-window' understand -b to create the pane to the left + or above the target pane. + +CHANGES FROM 1.9 to 1.9a 22 February 2014 + +NOTE: This is a bug-fix release to address some important bugs which just +missed the 1.9 deadline, but were found afterwards. + +Normal Changes +============== + +* Fix crash due to uninitialized lastwp member of layout_cell +* Fix -fg/-bg/-style with 256 colour terminals. + +CHANGES FROM 1.8 to 1.9, 20 February 2014 + +NOTE: This release has bumped the tmux protocol version. It is therefore +advised that the prior tmux server is restarted when this version of tmux is +installed, to avoid protocol mismatch errors for newer clients trying to +talk to an older running tmux server. + +Incompatible Changes +==================== + +* 88 colour support has been removed. +* 'default-path' has been removed. The new-window command accepts '-c' to + cater for this. The previous value of "." can be replaced with: 'neww -c + $PWD', the previous value of '' which meant current path of the pane can + be specified as: 'neww -c "#{pane_current_path}"' + +Deprecated Changes +================== + +* The single format specifiers: #A -> #Z (where defined) have been + deprecated and replaced with longer-named equivalents, as listed in the + FORMATS section of the tmux manpage. +* The various foo-{fg,bg,attr} commands have been deprecated and replaced + with equivalent foo-style option instead. Currently this is still + backwards-compatible, but will be removed over time. + +Normal Changes +============== + +* A new environment variable TMUX_TMPDIR is now honoured, allowing the + socket directory to be set outside of TMPDIR (/tmp/ if not set). +* If -s not given to swap-pane the current pane is assumed. +* A #{pane_syncronized} format specifier has been added to be a conditional + format if a pane is in a syncronised mode (c.f. syncronize-panes) +* Tmux now runs under Cygwin natively. +* Formats can now be nested within each other and expanded accordingly. +* Added 'automatic-rename-format' option to allow the automatic rename + mechanism to use something other than the default of + #{pane_current_command}. +* new-session learnt '-c' to specify the starting directory for that session + and all subsequent windows therein. +* The session name is now shown in the message printed to the terminal when + a session is detached. +* Lots more format specifiers have been added. +* Server race conditions have been fixed; in particular commands are not run + until after the configuration file is read completely. +* Case insensitive searching in tmux's copy-mode is now possible. +* attach-session and switch-client learnt the '-t' option to accept a window + and/or a pane to use. +* Copy-mode is only exited if no selection is in progress. +* Paste key in copy-mode is now possible to enter text from the clipboard. +* status-interval set to '0' now works as intended. +* tmux now supports 256 colours running under fbterm. +* Many bug fixes! + CHANGES FROM 1.7 to 1.8, 26 March 2013 Incompatible Changes @@ -17,7 +193,7 @@ Normal Changes * run-shell learnt '-t' to specify the pane to use when displaying output. * Support for middle-click pasting. * choose-tree learns '-u' to start uncollapsed. -* select-window learnt '-T; to toggle to the last window if it's already +* select-window learnt '-T' to toggle to the last window if it's already current. * New session option 'assume-paste-time' for pasting text versus key-binding actions. @@ -37,9 +213,9 @@ Normal Changes the 'source-file' command. * 'copy-pipe' mode command to copy selection and pipe the selection to a command. -* Changes panes can now emit focus notifications for certain applications +* Panes can now emit focus notifications for certain applications which use those. -* run-shell and if-shell now accept format placeholders. +* run-shell and if-shell now accept formats. * resize-pane learnt '-Z' for zooming a pane temporarily. * new-session learnt '-A' to make it behave like attach-session. * set-option learnt '-o' to prevent setting an option which is already set. @@ -1799,10 +1975,3 @@ The list of older changes is below. emacs) that don't require scrolling regions (ESC[r) mostly work fine (including mutt, emacs). No status bar yet and no key remapping or other customisation. - -$Id$ - - 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: dstidx srcname srcidx winlink lsw nabc sabc Exp Tiago Cunha dch - LocalWords: setw Chisnall renamew merdely eg Maier newname selectw neww Gass diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..9d96b3e6 --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE. + +The README, CHANGES, FAQ and TODO files are licensed under the ISC +license. Files under examples/ remain copyright their authors unless otherwise +stated in the file but permission has been received to distribute them with +tmux. All other files have a license and copyright notice at their start, +typically: + +Copyright (c) + +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. diff --git a/FAQ b/FAQ index 41b917c8..21e6167d 100644 --- a/FAQ +++ b/FAQ @@ -95,9 +95,6 @@ aware of are (bearing in mind I haven't used screen for a few years now): - 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 @@ -107,8 +104,12 @@ aware of are (bearing in mind I haven't used screen for a few years now): * I found a bug! What do I do? -Please send bug reports by email to nicm@users.sourceforge.net or -tmux-users@lists.sourceforge.net. Please include as much of the following +Check the latest version of tmux from Git to see if the problem is still +reproducible. Sometimes the length of time between releases means a lot of +fixes can be sitting in Git and the problem might already be fixed. + +Please send bug reports by email to nicholas.marriott@gmail.com or +tmux-users@googlegroups.com. Please include as much of the following information as possible: - the version of tmux you are running; @@ -122,7 +123,7 @@ information as possible: * Why doesn't tmux do $x? -Please send feature requests by email to nicm@users.sourceforge.net. +Please send feature requests by email to tmux-users@googlegroups.com. * Why do you use the screen terminal description inside tmux? It sucks. @@ -238,6 +239,31 @@ would be welcome. vim users may also want to set the "ttyfast" option inside tmux. +* How do I make ctrl and shift arrow keys work in emacs? + +The terminal-init-screen function in term/screen.el is called for new frames, +but it doesn't configure any function keys. + +If the tmux xterm-keys option is on, it is enough to define the same keys as +xterm. Add the following to init.el or .emacs to do this: + +(defadvice terminal-init-screen + ;; The advice is named `tmux', and is run before `terminal-init-screen' runs. + (before tmux activate) + ;; Docstring. This describes the advice and is made available inside emacs; + ;; for example when doing C-h f terminal-init-screen RET + "Apply xterm keymap, allowing use of keys passed through tmux." + ;; This is the elisp code that is run before `terminal-init-screen'. + (if (getenv "TMUX") + (let ((map (copy-keymap xterm-function-map))) + (set-keymap-parent map (keymap-parent input-decode-map)) + (set-keymap-parent input-decode-map map)))) + +And ensure .tmux.conf contains "set -g xterm-keys on". + +Alternatively, the screen.el file can be copied to the load path and +customized. + * 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 @@ -326,42 +352,33 @@ 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' -* vim displays reverse video instead of italics, while less displays italics - (or just regular text) instead of reverse. What's wrong? +* I don't see italics! Or less and vim show italics and reverse the wrong way round! -Screen's terminfo description lacks italics mode and has standout mode in its -place, but using the same escape sequence that urxvt uses for italics. This -means applications (like vim) looking for italics will not find it and might -turn to reverse in its place, while applications (like less) asking for -standout will end up with italics instead of reverse. To make applications -aware that tmux supports italics and to use a proper escape sequence for -standout, you'll need to create a new terminfo file with modified sgr, smso, -rmso, sitm and ritm entries: +GNU screen does not support italics and the "screen" terminfo description uses +the italics escape sequence incorrectly. - $ mkdir $HOME/.terminfo/ - $ screen_terminfo="screen" - $ infocmp "$screen_terminfo" | sed \ - -e 's/^screen[^|]*|[^,]*,/screen-it|screen with italics support,/' \ - -e 's/%?%p1%t;3%/%?%p1%t;7%/' \ - -e 's/smso=[^,]*,/smso=\\E[7m,/' \ - -e 's/rmso=[^,]*,/rmso=\\E[27m,/' \ - -e '$s/$/ sitm=\\E[3m, ritm=\\E[23m,/' > /tmp/screen.terminfo - $ tic /tmp/screen.terminfo +As of tmux 2.1, if default-terminal is set to "screen" or matches "screen-*", +tmux will behave like screen and italics will be disabled. + +To enable italics, create a new terminfo entry called "tmux" (some platforms +may already have this, you can check with "infocmp tmux"): + + $ cat </dev/null" + +Or for inside and outside copy mode with the prefix key: + + bind C-y run -b "tmux save-buffer - | xclip -i" + +On OS X, reattach-to-usernamespace lets pbcopy/pbpaste work: + + https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard -$Id$ +* Why do I see dots around a session when I attach to it? + +tmux limits the size of the window to the smallest attached session. If +it didn't do this then it would be impossible to see the entire window. +The dots mark the size of the window tmux can display. + +To avoid this, detach all other clients when attaching: + + $ tmux attach -d + +Or from inside tmux by detaching individual clients with C-b D or all +using: + + C-b : attach -d diff --git a/Makefile.am b/Makefile.am index d0a9cd40..a85c3778 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,19 +1,19 @@ -# $Id$ +# Makefile.am # Obvious program stuff. bin_PROGRAMS = tmate -dist_man1_MANS = tmate.1 +CLEANFILES = tmate.1.mdoc tmate.1.man # Distribution tarball options. EXTRA_DIST = \ - CHANGES FAQ README TODO examples compat \ - array.h compat.h tmux.h osdep-*.c + CHANGES FAQ README TODO COPYING examples compat/*.[ch] \ + array.h compat.h tmux.h tmate.h osdep-*.c mdoc2man.awk tmate.1 dist-hook: + make clean grep "^#found_debug=" configure - find $(distdir) -name .svn -type d|xargs rm -Rf # Preprocessor flags. -CPPFLAGS += @XOPEN_DEFINES@ +CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" # glibc as usual does things ass-backwards and hides useful things by default, # so everyone has to add this. @@ -21,79 +21,79 @@ if IS_GLIBC CFLAGS += -D_GNU_SOURCE endif -CFLAGS += -Wno-unused-parameter -Wno-unused-variable - -# Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly -# different flags. -if IS_GCC +if IS_LINUX CFLAGS += -rdynamic # for stack traces -CFLAGS += -std=gnu99 +endif + +# Set flags for gcc. +if IS_GCC +CFLAGS += -std=gnu99 -O2 if IS_DEBUG -CFLAGS += -O0 -g +CFLAGS += -g CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare -CFLAGS += -Wbad-function-cast -Winline -Wcast-align +CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align +CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes CPPFLAGS += -DDEBUG -else -CFLAGS += -O2 endif -if IS_GCC4 -CPPFLAGS += -iquote. -I/usr/local/include -if IS_DEBUG -CFLAGS += -Wno-pointer-sign -endif -else -CPPFLAGS += -I. -I- -I/usr/local/include +if IS_COVERAGE +CFLAGS += -g -O0 --coverage +LDFLAGS += --coverage endif +CPPFLAGS += -iquote. endif # Set flags for Solaris. if IS_SUNOS +if IS_GCC +CPPFLAGS += -D_XPG6 -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS +else CPPFLAGS += -D_XPG4_2 -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS endif +endif # Set flags for Sun CC. if IS_SUNCC CFLAGS += -erroff=E_EMPTY_DECLARATION endif +# Set _LINUX_SOURCE_COMPAT for AIX for mallocing 0 bytes +if IS_AIX +DEFS += -D_LINUX_SOURCE_COMPAT=1 +endif + # List of sources. dist_tmate_SOURCES = \ + alerts.c \ arguments.c \ attributes.c \ cfg.c \ client.c \ - clock.c \ cmd-attach-session.c \ cmd-bind-key.c \ cmd-break-pane.c \ cmd-capture-pane.c \ cmd-choose-buffer.c \ cmd-choose-client.c \ - cmd-choose-list.c \ cmd-choose-tree.c \ cmd-clear-history.c \ - cmd-clock-mode.c \ cmd-command-prompt.c \ cmd-confirm-before.c \ cmd-copy-mode.c \ - cmd-delete-buffer.c \ cmd-detach-client.c \ cmd-display-message.c \ cmd-display-panes.c \ + cmd-find.c \ cmd-find-window.c \ - cmd-has-session.c \ cmd-if-shell.c \ cmd-join-pane.c \ cmd-kill-pane.c \ cmd-kill-server.c \ cmd-kill-session.c \ cmd-kill-window.c \ - cmd-link-window.c \ cmd-list-buffers.c \ cmd-list-clients.c \ - cmd-list-commands.c \ cmd-list-keys.c \ cmd-list-panes.c \ cmd-list-sessions.c \ @@ -120,23 +120,20 @@ dist_tmate_SOURCES = \ cmd-select-pane.c \ cmd-select-window.c \ cmd-send-keys.c \ - cmd-server-info.c \ cmd-set-buffer.c \ cmd-set-environment.c \ + cmd-set-hook.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ cmd-show-options.c \ cmd-source-file.c \ cmd-split-window.c \ - cmd-start-server.c \ cmd-string.c \ - cmd-suspend-client.c \ cmd-swap-pane.c \ cmd-swap-window.c \ cmd-switch-client.c \ cmd-unbind-key.c \ - cmd-unlink-window.c \ cmd-wait-for.c \ cmd.c \ colour.c \ @@ -144,9 +141,9 @@ dist_tmate_SOURCES = \ control-notify.c \ environ.c \ format.c \ - grid-cell.c \ grid-view.c \ grid.c \ + hooks.c \ input-keys.c \ input.c \ job.c \ @@ -162,17 +159,18 @@ dist_tmate_SOURCES = \ options-table.c \ options.c \ paste.c \ + proc.c \ resize.c \ screen-redraw.c \ screen-write.c \ screen.c \ server-client.c \ server-fn.c \ - server-window.c \ server.c \ session.c \ signal.c \ status.c \ + style.c \ tmate-debug.c \ tmate-ssh-client.c \ tmate-encoder.c \ @@ -222,6 +220,9 @@ endif if NO_FGETLN nodist_tmate_SOURCES += compat/fgetln.c endif +if NO_FPARSELN +nodist_tmate_SOURCES += compat/fparseln.c +endif if NO_GETOPT nodist_tmate_SOURCES += compat/getopt.c endif @@ -240,3 +241,25 @@ endif if NO_B64_NTOP nodist_tmate_SOURCES += compat/b64_ntop.c endif +if NO_CFMAKERAW +nodist_tmate_SOURCES += compat/cfmakeraw.c +endif +if NO_OPENAT +nodist_tmate_SOURCES += compat/openat.c +endif +if NO_REALLOCARRAY +nodist_tmate_SOURCES += compat/reallocarray.c +endif + +# Install tmate.1 in the right format. +install-exec-hook: + if test x@MANFORMAT@ = xmdoc; then \ + sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmate.1 \ + >$(srcdir)/tmate.1.mdoc; \ + else \ + sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmate.1| \ + $(AWK) -f$(srcdir)/mdoc2man.awk >$(srcdir)/tmate.1.man; \ + fi + $(mkdir_p) $(DESTDIR)$(mandir)/man1 + $(INSTALL_DATA) $(srcdir)/tmate.1.@MANFORMAT@ \ + $(DESTDIR)$(mandir)/man1/tmate.1 diff --git a/README-tmux b/README-tmux index 77c6058d..acf15632 100644 --- a/README-tmux +++ b/README-tmux @@ -4,10 +4,9 @@ tmux is a "terminal multiplexer", it enables a number of terminals (or windows) to be accessed and controlled from a single terminal. tmux is intended to be a simple, modern, BSD-licensed alternative to programs such as GNU screen. -This release runs on OpenBSD, FreeBSD, NetBSD, Linux and OS X and may still -run on Solaris and AIX (although they haven't been tested in a while). +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. -Since the 1.2 release tmux depends on libevent. Download it from: +tmux depends on libevent 2.x. Download it from: http://www.monkey.org/~provos/libevent/ @@ -18,14 +17,13 @@ To build tmux from a release tarball, do: To get and build the latest from version control: - $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux + $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make -For more information see https://sourceforge.net/scm/?type=git&group_id=200378 -and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@lists.sourceforge.net. +For more information see http://git-scm.com. Patches should be sent by email to +the mailing list at tmux-users@googlegroups.com. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: @@ -41,20 +39,23 @@ directory. For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. -tmux mailing lists are available. Visit: +tmux mailing lists are available. For general discussion and bug reports: - https://sourceforge.net/mail/?group_id=200378 + https://groups.google.com/forum/#!forum/tmux-users + +And for Git commit emails: + + https://groups.google.com/forum/#!forum/tmux-git Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: - tmux-users@lists.sourceforge.net + tmux-users@googlegroups.com -This file and the CHANGES, FAQ and TODO files are licensed under the ISC -license. Files under examples/ remain copyright their authors unless otherwise -stated in the file but permission has been received to distribute them with -tmux. All other files have a license and copyright notice at their start. +This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under +the ISC license. Files under examples/ remain copyright their authors unless +otherwise stated in the file but permission has been received to distribute +them with tmux. All other files have a license and copyright notice at their +start. --- Nicholas Marriott - -$Id$ +-- Nicholas Marriott diff --git a/README.md b/README.md index 54a6d058..14bc9238 100644 --- a/README.md +++ b/README.md @@ -9,25 +9,4 @@ Tmate is a fork of tmux. It provides an instant pairing solution. License ------- -tmate is built on top of tmux, libssh and msgpack. Their respective licenses are -in the sources. tmate is MIT licensed. - -Copyright (c) 2013 Nicolas Viennot - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +tmate is built on top of tmux. tmux and tmate are BSD-licensed. diff --git a/SYNCING b/SYNCING index eba24177..52591af7 100644 --- a/SYNCING +++ b/SYNCING @@ -1,10 +1,10 @@ Preamble ======== -Tmux on SourceForge has two git repositories [1] "tmux-code" and "tmux-openbsd". +Tmux portable relies on repositories "tmux" and "tmux-openbsd". Here's a description of them: -* "tmux-code" is the portable version, the one which contains code for other +* "tmux" is the portable version, the one which contains code for other operating systems, and autotools, etc., which isn't found or needed in the OpenBSD base system. @@ -17,9 +17,6 @@ repository will take at least that long to appear in this git repository. (It might take longer, depending on the CVS mirror used to import the OpenBSD code). -It is assumed that the person doing the sync has read/write access to the -tmux-code repository on SourceForge already. - If you've never used git before, git tracks meta-data about the committer and the author, as part of a commit, hence: @@ -37,11 +34,11 @@ this information has ever been set before. Cloning repositories ==================== -This involves having both tmux-code and tmux-openbsd cloned, as in: +This involves having both tmux and tmux-openbsd cloned, as in: % cd /some/where/useful -% git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux -% git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux-openbsd +% git clone https://github.com/tmux/tmux.git +% git clone https://github.com/ThomasAdam/tmux-openbsd.git Note that you do not need additional checkouts to manage the sync -- an existing clone of either repositories will suffice. So if you already have @@ -50,56 +47,56 @@ these checkouts existing, skip that. Adding in git-remotes ===================== -Because the portable "tmux-code" git repository and the "tmux-openbsd" +Because the portable "tmux" git repository and the "tmux-openbsd" repository do not inherently share any history between each other, the history has been faked between them. This "faking of history" is something which has to be told to git for the purposes of comparing the "tmux" and "tmux-openbsd" repositories for syncing. To do this, we must reference the -clone of the "tmux-openbsd" repository from the "tmux-code" repository, as +clone of the "tmux-openbsd" repository from the "tmux" repository, as shown by the following command: -% cd /path/to/tmux-code +% cd /path/to/tmux % git remote add obsd-tmux file:///path/to/tmux-openbsd So that now, the remote "obsd-tmux" can be used to reference branches and commits from the "tmux-openbsd" repository, but from the context of the -portable "tmux-code" repository, which makes sense because it's the "tmux" +portable "tmux" repository, which makes sense because it's the "tmux" repository which will have the updates applied to them. Fetching updates ================ To ensure the latest commits from "tmux-openbsd" can be found from within -"tmux-code", we have to ensure the "master" branch from "tmux-openbsd" is -up-to-date first, and then reference that update in "tmux-code", as in: +"tmux", we have to ensure the "master" branch from "tmux-openbsd" is +up-to-date first, and then reference that update in "tmux", as in: % cd /path/to/tmux-openbsd % git checkout master % git pull -Then back in "tmux-code": +Then back in "tmux": -% cd /path/to/tmux-code -% git fetch obsd-tmux-code +% cd /path/to/tmux +% git fetch obsd-tmux Creating the necessary branches =============================== -Now that "tmux-code" can see commits and branches from "tmux-openbsd" by way +Now that "tmux" can see commits and branches from "tmux-openbsd" by way of the remote name "obsd-tmux", we can now create the master branch from -"tmux-openbsd" in the "tmux-code" repository: +"tmux-openbsd" in the "tmux" repository: % git checkout -b obsd-master obsd-tmux/master Adding in the fake history points ================================= -To tie both the "master" branch from "tmux-code" and the "obsd-master" +To tie both the "master" branch from "tmux" and the "obsd-master" branch from "tmux-openbsd" together, the fake history points added to the -"tmux-code" repository need to be added. To do this, we must add an +"tmux" repository need to be added. To do this, we must add an additional refspec line, as in: -% cd /path/to/tmux-code +% cd /path/to/tmux % git config --add remote.origin.fetch '+refs/replace/*:refs/replace/*' % git fetch origin @@ -110,7 +107,7 @@ Make sure the "master" branch is checked out: % git checkout master -The following will show commits on OpenBSD not yet synched with "tmux-code": +The following will show commits on OpenBSD not yet synched with "tmux": % git log master..obsd-master @@ -131,6 +128,15 @@ And if happy: % git push origin master +Keeping an eye on libutil in OpenBSD +==================================== + +A lot of the compat/ code in tmux comes from libutil, especially imsg. +Sometimes the API can change, etc., which might cause interesting problems +trying to run the portable version of tmux. It's worth checking +periodically for any changes to libutil in OpenBSD and syncing those files +to compat/ as and when appropriate. + Release tmux for next version ============================= @@ -145,30 +151,26 @@ Release tmux for next version 3. Tag with: - % git tag -a 1.X + % git tag -a 2.X - Where "1.X" is the next version. + Where "2.X" is the next version. Push the tag out with: - % git push 1.X + % git push 2.X -4. Build the tarball with make dist. Now that it's using autoconf there - shouldn't be any weird files (such as the original and rejection files - from patch(1)) but it doesn't hurt taking a quick look at it. +4. Build the tarball with 'make dist'. -5. Split the release changes into a new file. This should be named - tmux-$VERSION-readme to make sourceforge show it automagically in specific - parts of the project page. +5. Check the tarball. If it's good, go here to select the tag just pushed: -6. Upload the tarball and the above file. Make the tarball the default - download by selecting all operating systems under the file details. + https://github.com/tmux/tmux/tags -7. Run make update-index.html upload-index.html to replace %%VERSION%%. + Click the "Add release notes", upload the tarball and add a link in the + description field to the CHANGES file. -8. Bump version in configure.ac and uncomment "found_debug=yes" to create - a debug build by default. +7. Clone the tmux.github.io repository, and change the RELEASE version in + the Makefile. Commit it, and run 'make' to replace %%VERSION%%. Push + the result out. -9. Update freshmeat. - -[1] https://sourceforge.net/p/tmux/_list/git +8. Bump version in tmu/tmux.git configure.ac and uncomment "found_debug=yes" to + create a debug build by default. diff --git a/TODO b/TODO index a57dc932..b4b8231b 100644 --- a/TODO +++ b/TODO @@ -1,155 +1,135 @@ -NOTES -===== +- command bits and pieces: + * allow multiple targets: fnmatch for -t/-c, for example detach all + clients with -t* + * add -c for new-session like new-window + * ' and " should be parsed the same (eg "\e" vs '\e') in config + and command prompt + * last-pane across sessions + * list-keys should quote output so that bindings can just be used in + config file as-is -This file describes rough notes regarding ideas for potential future tmux -development. It's not necessarily guaranteed that items in this TODO file -will ever get implemented. +- make command sequences more usable + * don't require space after ; + * options for error handling: && and ||? -It is asked therefore, that anyone thinking of undertaking a task in this -TODO file, email tmux-users@lists.sf.net to discuss the feature. +- options bits and pieces: + * set-remain-on-exit is a complete hack + * way to set socket path from config file -Thie file is split up between tmux user interface (UI) issues, and terminal -compatibility issues. +- format improvements: + * option to quote format (#{q:session_name}) + * formats need conditions for >0 (for #P) + * some way to pad # stuff with spaces + * formats to show if a window is linked into multiple sessions, into + multiple attached sessions, and is the active window in multiple + attached sessions? -TMUX UI ISSUES -============== +- choose mode improvements: + * choose-pane command (augment choose-tree to do this?) + * choose-mode and copy-mode are very similar, make choose-mode a subset? + * flag to choose-* for sort order + * choose mode would be better per client than per window? + * two choices (first one then second, for swap-pane and join-pane) + * choose modes should ditch the key bindings and just have fixed keys, and + be more customized to their purpose (d to delete a buffer for choose-buffer, + a preview of buffer contents, etc) + +- improve monitor-*: + * straighten out rules for multiple clients + * think about what happens across sessions + * monitor changes within a region + * perhaps monitor /all/ panes in the window not just one + +- improve mouse support: + * bind commands to mouse in different areas? + * commands executed when clicking on a pattern (URL) + +- hooks! -- implicitly add exec to the commands for new windows (switch to disable it)? -- bring back detach-session to detach all clients on a session? -- 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 - in $x time -- flags to centre screen in window -- activity/bell should be per-window not per-link? what if it is cur win in - session not being watched? -- 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 -- 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? -- would be nice to be able to use "--" to mark start of command w/ neww etc - to avoid quoting -- make command sequences more usable: don't require space after ;, handle - errors better -- choice and more mode would be better per client than per window? -- hooks to which commands may be attached, for example: tmux add-hook - "new-session" if-shell "[ -e $HOME/.tmux-session.conf ]" source-file - $HOME/.tmux-session.conf -- way to set socket path from config file - warts on current naming: - - display-time but message-fg/bg/attr - - list-* vs show-* - - server-info - - up-pane/down-pane/swap-pane -U/swap-pane -D vs next-*/previous-* - - split-window -> split-pane?? -- 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) - -- idea of a "view" onto a window, need base x/y offsets for redraw -- commands should be able to succeed or fail and have || or && for command - lists -- some way to keep a command running continually and just use its last line of - output -- UTF-8 to a non-UTF-8 terminal should not be able to balls up - the terminal - www/ruby-addressable; make regress -- support esc-esc to quit in modes -- fix ctrl+F1-F4 output. to what? -- better utf8 support: window names, prompt input, message display -- option to move copy mode indicator into status line -- selection behaviour closer to vi in vi mode -- live update: server started with -U connects to server, requests sessions and - windows, receives fds -- sort out inheriting config from shell on new sessions/windows: - should pick up default-path/termios/etc from client if possible, - else leave empty/default -- link panes into multiple windows -- bells should be passed between sessions with visual-bell etc - sequence until its shell exits, to allow them to be used from the config file -- better session sharing: create-socket command to create socket somewhere (-r - flag for readonly) -- multiline status line (no?) -- support title stack, both internally and externally - http://docs.freebsd.org/cgi/getmsg.cgi?fetch=1149299+0+archive/2010/freebsd-questions/20100207.freebsd-questions -- some way to pad # stuff with spaces, #!2T maybe -- a binding to "scroll down and exit at bottom" copy mode -- some way to pass keystrokes in copy mode through to underlying window. why? -- last window update time and # replacement for it for display-message -- find-window across sessions - other ways to make session handling easier? -- ' and " should be parsed the same (eg "\e" vs '\e') in config and command - prompt? -- command to toggle selection not to move it in copy-mode -- audit of escape sequence support vs xterm -- support binding keys to mouse (mouse-select-pane -> mouse-keys or something, - mouse click == select-pane -t %%, mouse scroll up == copy-mode) -- bind commands to key sequences? -- make it so ALL keys go through a table, - first an implicit table in which C-b is the only default binding to a - command that says "next key from $othertable" and so on. means -n can - go away as well -- monitor, bell etc should monitor /all/ panes in the window not just one -- a history of commands that can be reversed (reverse member of each command, - and a buffer) -- info() when changing to same window -- way to add dest for break-pane; maybe some easier way to unbreak-pane -- case insensitive searching -- incremental searching in copy mode. -- configurable borders and empty space filler for when panes < window? -- mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the - flag on w/o checking the others before calling tty_update_mode) -- pass shell commands as argv rather than strings, allow them to be specified - in commands without quotes -- named buffers and allow gaps in the stack -- monitor-activity is broken in several ways with multiple clients -- monitor-activity should be more powerful (eg set a region) -- maybe a way to put pane names instead of window names in status line -- support for borderless panes -- wait-for command 20130222153957.GY6782@yelena.nicm.ath.cx -- last-pane across sessions -- panes should have names like windows -- command-prompt doesn't work if made read-only. why? -- option to quote format eg #{session_name:quoted} -- formats need conditions for >0 (for #P) -- fetch full command line on !Linux, and add option to strip prefixes - such as "sh " "/bin/sh " etc etc -- synchronize-windows option -- append to buffer in copy mode -- way to paste w/o trailing whitespace -- flag to switch-client to switch all clients -- history of layouts and undo/redo flags to selectl -- way to tag a layout as a number/name -- optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx -- support multibyte key strings -- allow commands to be executed when certain patterns in a screen - are clicked on with the mouse -- flag to make next/previous commands skip a window -- way to do tmux command/run-shell from mode keys -- send command to all windows -- choose-pane command (augment choose-tree to do this?) -- choose-mode and copy-mode are very similar. Perhaps make choose-mode a subset - of copy-mode in that it inherits key-bindings and other traits but not all -- add -c for new-session like new-window -- flag to choose-* for sort order (eg sort windows/sessions/clients by last - used time) - perhaps using formats (but what about numeric sort)? -- instead of separate window and session options, just one master options list - with each option having a type (window or session), then options on window, - on session, and global. for window options we look window->session->global, - and for session we look session->global -- maybe keep last layout + size around and if size reverts just put it back -- way to set hints/limits about pane size for resizing -- revamp layouts: they are too complicated, should be more closely integrated, - should support hints, layout sets should just be a special case of custom - layouts, and we should support panes that are not attached to a cell at - all. this could be the time to introduce panelink to replace layout_cell -- run-shell/if-shell should support formats -- attach should take a pane and select it as well as attaching -- attach should have a flag to create session if it doesn't exist. or better - new a flag to attach it + * display-time but message-fg/bg/attr + * list-* vs show-* + * split-window -> split-pane? -TERMINAL ISSUES -================ +- better UTF-8 support: + * message display + * prompt input + * searching in copy mode -- use a better termcap internally instead of screen, perhaps xterm -- clear window title on exit (see using xterm title stack) -- get it passing all the vttest tests that don't require resizing the terminal -- support for bce -- use screen-256color when started on 256 colour terminal? -* We need a tmux terminfo entry to document the extensions we are using in - upstream terminfo. Must NOT change (only add or remove) anything from - TERM=screen so we can fallback! +- copy/paste improvements: + * incremental searching + * paste w/o trailing whitespace + * command to toggle selection not to move it in copy-mode + * regex searching + * copy-pipe should have -x as well + +- layout stuff + * way to tag a layout as a number/name + * maybe keep last layout + size around and if size reverts just put it + back + * revamp layouts: they are too complicated, should be more closely + integrated, should support hints, layout sets should just be a + special case of custom layouts, and we should support panes that are + not attached to a cell at all. this could be the time to introduce + panelink to replace layout_cell + * way to set hints/limits about pane size for resizing + * panning over window (window larger than visible) + * a mode where one application can cross two panes (ie x|y, width = + COLUMNS/2 but height = ROWS * 2) + * general key to space cells out evenly (horiz or vert) within their + parent cell (could replace even-vert/even-horiz layouts) + * separate active panes for different clients + +- terminfo bits + * use a better termcap internally instead of screen, perhaps xterm + * use screen-256color when started on 256 colour terminal? + +- code cleanup + * instead of separate window and session options, just one master + options list with each option having a type (window or session), then + options on window, on session, and global. for window options we look + window->session->global, and for session we look session->global + * the way pane, window, session destroy is handled is too complicated + and the distinction between session.c, window.c and server-fn.c + functions is not clear. could we just have kill_pane(), + kill_window(), unlink_window(), kill_session() that fix up all data + structures (flagging sessions as dead) and return a value to say + whether clients need to be checked for dead sessions? sort of like + session_detach now but more so. or some other scheme to make it + simpler and clearer? also would be nice to remove/rename server-fn.c + * more readable way to work out the various things commands need to + know about the client, notably: + - is this the config file? (cmdq->c == NULL) + - is this a command client? (cmdq->c != NULL && + cmdq->c->session == NULL) + - is this a control client? + - can i do stdin or stdout to this client? + or even guarantee that cmdq->c != NULL and provide a better way to + tell when in the config file - then we use cmdq->c if we need a + client w/o a session else cmd_current_client + * optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx + +- miscellaneous + * way to keep a job running just read its last line of output for #() + * link panes into multiple windows + * live update: server started with -U connects to server, requests + sessions and windows, receives file descriptors + * there are inconsistencies in what we get from old shell and what + comes from config for new sessions and windows. likewise, panes and + jobs and run-shell and lock command all start with slightly different + environments + * multiline status line? + * customizable command aliases + * automatic pane logging + * BCE? We are halfway there (output side is done for pane backgrounds), + just need to change how screen/grid handles erase + * copy mode key bindings should just be a standard key table, using + something like "copy-mode start-selection"; it could use + command-prompt for search, goto, etc: + + bind -Temacs command-prompt -p'Search Up: ' 'copy-mode search-up %%' + + it'd need a separate lookup, because modes are per-pane, perhaps a + table() cb to give the table name ("vi" or "emacs"). anything in the + table fires the command, anything not in the table is injected as a + key diff --git a/alerts.c b/alerts.c new file mode 100644 index 00000000..536ea750 --- /dev/null +++ b/alerts.c @@ -0,0 +1,277 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Nicholas Marriott + * + * 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 + +#include + +#include "tmux.h" + +int alerts_fired; + +void alerts_timer(int, short, void *); +int alerts_enabled(struct window *, int); +void alerts_callback(int, short, void *); +void alerts_reset(struct window *); + +int alerts_check_all(struct session *, struct winlink *); +int alerts_check_bell(struct session *, struct winlink *); +int alerts_check_activity(struct session *, struct winlink *); +int alerts_check_silence(struct session *, struct winlink *); +void alerts_ring_bell(struct session *); + +void +alerts_timer(__unused int fd, __unused short events, void *arg) +{ + struct window *w = arg; + + log_debug("@%u alerts timer expired", w->id); + alerts_reset(w); + alerts_queue(w, WINDOW_SILENCE); +} + +void +alerts_callback(__unused int fd, __unused short events, __unused void *arg) +{ + struct window *w; + struct session *s; + struct winlink *wl; + int flags, alerts; + + RB_FOREACH(w, windows, &windows) { + RB_FOREACH(s, sessions, &sessions) { + if (s->flags & SESSION_UNATTACHED) + continue; + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window != w) + continue; + flags = w->flags; + + alerts = alerts_check_all(s, wl); + + log_debug("%s:%d @%u alerts check, alerts %#x, " + "flags %#x", s->name, wl->idx, w->id, + alerts, flags); + } + } + } + alerts_fired = 0; +} + +int +alerts_check_all(struct session *s, struct winlink *wl) +{ + int alerts; + + alerts = alerts_check_bell(s, wl); + alerts |= alerts_check_activity(s, wl); + alerts |= alerts_check_silence(s, wl); + if (alerts != 0) + server_status_session(s); + + return (alerts); +} + +void +alerts_check_session(struct session *s) +{ + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) + alerts_check_all(s, wl); +} + +int +alerts_enabled(struct window *w, int flags) +{ + if (flags & WINDOW_BELL) + return (1); + if (flags & WINDOW_ACTIVITY) { + if (options_get_number(w->options, "monitor-activity")) + return (1); + } + if (flags & WINDOW_SILENCE) { + if (options_get_number(w->options, "monitor-silence") != 0) + return (1); + } + return (0); +} + +void +alerts_reset_all(void) +{ + struct window *w; + + RB_FOREACH(w, windows, &windows) + alerts_reset(w); +} + +void +alerts_reset(struct window *w) +{ + struct timeval tv; + + w->flags &= ~WINDOW_SILENCE; + event_del(&w->alerts_timer); + + timerclear(&tv); + tv.tv_sec = options_get_number(w->options, "monitor-silence"); + + log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); + if (tv.tv_sec != 0) + event_add(&w->alerts_timer, &tv); +} + +void +alerts_queue(struct window *w, int flags) +{ + if (w->flags & WINDOW_ACTIVITY) + alerts_reset(w); + + if (!event_initialized(&w->alerts_timer)) + evtimer_set(&w->alerts_timer, alerts_timer, w); + + if (!alerts_fired) { + w->flags |= flags; + log_debug("@%u alerts flags added %#x", w->id, flags); + + if (alerts_enabled(w, flags)) { + log_debug("alerts check queued (by @%u)", w->id); + event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); + alerts_fired = 1; + } + } +} + +int +alerts_check_bell(struct session *s, struct winlink *wl) +{ + struct client *c; + struct window *w = wl->window; + int action, visual; + + if (!(w->flags & WINDOW_BELL)) + return (0); + if (s->curw != wl) { + wl->flags |= WINLINK_BELL; + w->flags &= ~WINDOW_BELL; + } + if (s->curw->window == w) + w->flags &= ~WINDOW_BELL; + + action = options_get_number(s->options, "bell-action"); + if (action == BELL_NONE) + return (0); + + visual = options_get_number(s->options, "visual-bell"); + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s || c->flags & CLIENT_CONTROL) + continue; + if (!visual) { + if ((action == BELL_CURRENT && + c->session->curw->window == w) || + (action == BELL_OTHER && + c->session->curw->window != w) || + action == BELL_ANY) + tty_putcode(&c->tty, TTYC_BEL); + continue; + } + if (action == BELL_CURRENT && c->session->curw->window == w) + status_message_set(c, "Bell in current window"); + else if (action == BELL_ANY || (action == BELL_OTHER && + c->session->curw->window != w)) + status_message_set(c, "Bell in window %d", wl->idx); + } + + return (WINDOW_BELL); +} + +int +alerts_check_activity(struct session *s, struct winlink *wl) +{ + struct client *c; + struct window *w = wl->window; + + if (s->curw->window == w) + w->flags &= ~WINDOW_ACTIVITY; + + if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) + return (0); + if (s->curw == wl) + return (0); + + if (!options_get_number(w->options, "monitor-activity")) + return (0); + + if (options_get_number(s->options, "bell-on-alert")) + alerts_ring_bell(s); + wl->flags |= WINLINK_ACTIVITY; + + if (options_get_number(s->options, "visual-activity")) { + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) + continue; + status_message_set(c, "Activity in window %d", wl->idx); + } + } + + return (WINDOW_ACTIVITY); +} + +int +alerts_check_silence(struct session *s, struct winlink *wl) +{ + struct client *c; + struct window *w = wl->window; + + if (s->curw->window == w) + w->flags &= ~WINDOW_SILENCE; + + if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) + return (0); + if (s->curw == wl) + return (0); + + if (options_get_number(w->options, "monitor-silence") == 0) + return (0); + + if (options_get_number(s->options, "bell-on-alert")) + alerts_ring_bell(s); + wl->flags |= WINLINK_SILENCE; + + if (options_get_number(s->options, "visual-silence")) { + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) + continue; + status_message_set(c, "Silence in window %d", wl->idx); + } + } + + return (WINDOW_SILENCE); +} + +void +alerts_ring_bell(struct session *s) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == s && !(c->flags & CLIENT_CONTROL)) + tty_putcode(&c->tty, TTYC_BEL); + } +} diff --git a/arguments.c b/arguments.c index 4d54ff85..16ad5110 100644 --- a/arguments.c +++ b/arguments.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott @@ -20,9 +20,31 @@ #include #include +#include #include "tmux.h" +/* + * Manipulate command arguments. + */ + +struct args_entry { + u_char flag; + char *value; + RB_ENTRY(args_entry) entry; +}; + +struct args_entry *args_find(struct args *, u_char); + +RB_GENERATE(args_tree, args_entry, entry, args_cmp); + +/* Arguments tree comparison function. */ +int +args_cmp(struct args_entry *a1, struct args_entry *a2) +{ + return (a1->flag - a2->flag); +} + /* Create an arguments set with no flags. */ struct args * args_create(int argc, ...) @@ -32,8 +54,6 @@ args_create(int argc, ...) int i; args = xcalloc(1, sizeof *args); - if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) - fatal("bit_alloc failed"); args->argc = argc; if (argc == 0) @@ -49,35 +69,36 @@ args_create(int argc, ...) return (args); } +/* Find a flag in the arguments tree. */ +struct args_entry * +args_find(struct args *args, u_char ch) +{ + struct args_entry entry; + + entry.flag = ch; + return (RB_FIND(args_tree, &args->tree, &entry)); +} + /* Parse an argv and argc into a new argument set. */ struct args * args_parse(const char *template, int argc, char **argv) { struct args *args; - char *ptr; int opt; args = xcalloc(1, sizeof *args); - if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) - fatal("bit_alloc failed"); optreset = 1; optind = 1; while ((opt = getopt(argc, argv, template)) != -1) { - if (opt < 0 || opt >= SCHAR_MAX) + if (opt < 0) continue; - if (opt == '?' || (ptr = strchr(template, opt)) == NULL) { - free(args->flags); - free(args); + if (opt == '?' || strchr(template, opt) == NULL) { + args_free(args); return (NULL); } - - bit_set(args->flags, opt); - if (ptr[1] == ':') { - free(args->values[opt]); - args->values[opt] = xstrdup(optarg); - } + args_set(args, opt, optarg); } argc -= optind; argv += optind; @@ -92,122 +113,142 @@ args_parse(const char *template, int argc, char **argv) void args_free(struct args *args) { - u_int i; + struct args_entry *entry; + struct args_entry *entry1; cmd_free_argv(args->argc, args->argv); - for (i = 0; i < SCHAR_MAX; i++) - free(args->values[i]); + RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { + RB_REMOVE(args_tree, &args->tree, entry); + free(entry->value); + free(entry); + } - free(args->flags); free(args); } -/* Print a set of arguments. */ -size_t -args_print(struct args *args, char *buf, size_t len) +/* Add to string. */ +static void printflike(3, 4) +args_print_add(char **buf, size_t *len, const char *fmt, ...) { - size_t off; - int i; - const char *quotes; + va_list ap; + char *s; + size_t slen; - /* There must be at least one byte at the start. */ - if (len == 0) - return (0); - off = 0; + va_start(ap, fmt); + slen = xvasprintf(&s, fmt, ap); + va_end(ap); + + *len += slen; + *buf = xrealloc(*buf, *len); + + strlcat(*buf, s, *len); + free(s); +} + +/* Print a set of arguments. */ +char * +args_print(struct args *args) +{ + size_t len; + char *buf; + int i; + struct args_entry *entry; + + len = 1; + buf = xcalloc(1, len); /* Process the flags first. */ - buf[off++] = '-'; - for (i = 0; i < SCHAR_MAX; i++) { - if (!bit_test(args->flags, i) || args->values[i] != NULL) + RB_FOREACH(entry, args_tree, &args->tree) { + if (entry->value != NULL) continue; - if (off == len - 1) { - buf[off] = '\0'; - return (len); - } - buf[off++] = i; - buf[off] = '\0'; + if (*buf == '\0') + args_print_add(&buf, &len, "-"); + args_print_add(&buf, &len, "%c", entry->flag); } - if (off == 1) - buf[--off] = '\0'; /* Then the flags with arguments. */ - for (i = 0; i < SCHAR_MAX; i++) { - if (!bit_test(args->flags, i) || args->values[i] == NULL) + RB_FOREACH(entry, args_tree, &args->tree) { + if (entry->value == NULL) continue; - if (off >= len) { - /* snprintf will have zero terminated. */ - return (len); - } - - if (strchr(args->values[i], ' ') != NULL) - quotes = "\""; + if (*buf != '\0') + args_print_add(&buf, &len, " -%c ", entry->flag); else - quotes = ""; - off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s", - off != 0 ? " " : "", i, quotes, args->values[i], quotes); + args_print_add(&buf, &len, "-%c ", entry->flag); + if (strchr(entry->value, ' ') != NULL) + args_print_add(&buf, &len, "\"%s\"", entry->value); + else + args_print_add(&buf, &len, "%s", entry->value); } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) { - if (off >= len) { - /* snprintf will have zero terminated. */ - return (len); - } - + if (*buf != '\0') + args_print_add(&buf, &len, " "); if (strchr(args->argv[i], ' ') != NULL) - quotes = "\""; + args_print_add(&buf, &len, "\"%s\"", args->argv[i]); else - quotes = ""; - off += xsnprintf(buf + off, len - off, "%s%s%s%s", - off != 0 ? " " : "", quotes, args->argv[i], quotes); + args_print_add(&buf, &len, "%s", args->argv[i]); } - return (off); + return (buf); } /* Return if an argument is present. */ int args_has(struct args *args, u_char ch) { - return (bit_test(args->flags, ch)); + return (args_find(args, ch) == NULL ? 0 : 1); } -/* Set argument value. */ +/* Set argument value in the arguments tree. */ void args_set(struct args *args, u_char ch, const char *value) { - free(args->values[ch]); + struct args_entry *entry; + + /* Replace existing argument. */ + if ((entry = args_find(args, ch)) != NULL) { + free(entry->value); + entry->value = NULL; + } else { + entry = xcalloc(1, sizeof *entry); + entry->flag = ch; + RB_INSERT(args_tree, &args->tree, entry); + } + if (value != NULL) - args->values[ch] = xstrdup(value); - else - args->values[ch] = NULL; - bit_set(args->flags, ch); + entry->value = xstrdup(value); } /* Get argument value. Will be NULL if it isn't present. */ const char * args_get(struct args *args, u_char ch) { - return (args->values[ch]); + struct args_entry *entry; + + if ((entry = args_find(args, ch)) == NULL) + return (NULL); + return (entry->value); } /* Convert an argument value to a number. */ long long -args_strtonum(struct args *args, - u_char ch, long long minval, long long maxval, char **cause) +args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, + char **cause) { - const char *errstr; - long long ll; + const char *errstr; + long long ll; + struct args_entry *entry; - if (!args_has(args, ch)) { + if ((entry = args_find(args, ch)) == NULL) { *cause = xstrdup("missing"); return (0); } - ll = strtonum(args->values[ch], minval, maxval, &errstr); + ll = strtonum(entry->value, minval, maxval, &errstr); if (errstr != NULL) { *cause = xstrdup(errstr); return (0); diff --git a/array.h b/array.h index 11e963bc..671bea42 100644 --- a/array.h +++ b/array.h @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2006 Nicholas Marriott @@ -39,10 +39,10 @@ fatalx("size too big"); \ if ((a)->space == 0) { \ (a)->space = ARRAY_INITIALSPACE(a); \ - (a)->list = xrealloc((a)->list, 1, (a)->space); \ + (a)->list = xrealloc((a)->list, (a)->space); \ } \ while ((a)->space <= ((a)->num + (n)) * ARRAY_ITEMSIZE(a)) { \ - (a)->list = xrealloc((a)->list, 2, (a)->space); \ + (a)->list = xreallocarray((a)->list, 2, (a)->space); \ (a)->space *= 2; \ } \ } while (0) diff --git a/attributes.c b/attributes.c index f9871108..84e4f9c6 100644 --- a/attributes.c +++ b/attributes.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Joshua Elsasser diff --git a/autogen.sh b/autogen.sh index 300b54db..cbf6df1f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,5 +1,4 @@ #!/bin/sh -# $Id$ if [ "x$(uname)" = "xOpenBSD" ]; then [ -z "$AUTOMAKE_VERSION" ] && export AUTOMAKE_VERSION=1.10 diff --git a/cfg.c b/cfg.c index 0557bca1..bdfa381f 100644 --- a/cfg.c +++ b/cfg.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -17,29 +17,95 @@ */ #include -#include #include #include #include #include #include +#include #include "tmux.h" #include "tmate.h" -struct cmd_q *cfg_cmd_q; -int cfg_finished; -int cfg_references; -struct causelist cfg_causes; +char *cfg_file; +#ifdef TMATE +char *tmate_cfg_file; +#endif +struct cmd_q *cfg_cmd_q; +int cfg_finished; +int cfg_references; +char **cfg_causes; +u_int cfg_ncauses; +struct client *cfg_client; + +void cfg_default_done(struct cmd_q *); + +void +set_cfg_file(const char *path) +{ + free(cfg_file); + cfg_file = xstrdup(path); +} + +void +start_cfg(void) +{ + char *cause = NULL; + const char *home; + + cfg_cmd_q = cmdq_new(NULL); + cfg_cmd_q->emptyfn = cfg_default_done; + + cfg_finished = 0; + cfg_references = 1; + + cfg_client = TAILQ_FIRST(&clients); + if (cfg_client != NULL) + cfg_client->references++; + + if (access(TMUX_CONF, R_OK) == 0) { + if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) + cfg_add_cause("%s: %s", TMUX_CONF, cause); + } else if (errno != ENOENT) + cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); + + if (cfg_file == NULL && (home = find_home()) != NULL) { + xasprintf(&cfg_file, "%s/.tmux.conf", home); + if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { + free(cfg_file); + cfg_file = NULL; + } + } + if (cfg_file != NULL && load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) + cfg_add_cause("%s: %s", cfg_file, cause); + free(cause); + +#ifdef TMATE + cause = NULL; + if ((home = find_home()) != NULL) { + xasprintf(&tmate_cfg_file, "%s/.tmate.conf", home); + if (access(tmate_cfg_file, R_OK) != 0 && errno == ENOENT) { + free(tmate_cfg_file); + tmate_cfg_file = NULL; + } + } + if (tmate_cfg_file != NULL && load_cfg(tmate_cfg_file, cfg_cmd_q, &cause) == -1) + cfg_add_cause("%s: %s", cfg_file, cause); + free(cause); +#endif + + cmdq_continue(cfg_cmd_q); +} int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) { FILE *f; - u_int n, found; - char *buf, *copy, *line, *cause1, *msg; - size_t len, oldlen; + char delim[3] = { '\\', '\\', '\0' }; + u_int found; + size_t line = 0; + char *buf, *cause1, *p; struct cmd_list *cmdlist; log_debug("loading %s", path); @@ -48,107 +114,121 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) return (-1); } - n = found = 0; - line = NULL; - while ((buf = fgetln(f, &len))) { - /* Trim \n. */ - if (buf[len - 1] == '\n') - len--; - log_debug("%s: %.*s", path, (int)len, buf); - - /* Current line is the continuation of the previous one. */ - if (line != NULL) { - oldlen = strlen(line); - line = xrealloc(line, 1, oldlen + len + 1); - } else { - oldlen = 0; - line = xmalloc(len + 1); - } - - /* Append current line to the previous. */ - memcpy(line + oldlen, buf, len); - line[oldlen + len] = '\0'; - n++; - - /* Continuation: get next line? */ - len = strlen(line); - if (len > 0 && line[len - 1] == '\\') { - line[len - 1] = '\0'; - - /* Ignore escaped backslash at EOL. */ - if (len > 1 && line[len - 2] != '\\') - continue; - } - copy = line; - line = NULL; + found = 0; + while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) { + log_debug("%s: %s", path, buf); /* Skip empty lines. */ - buf = copy; - while (isspace((u_char)*buf)) - buf++; - if (*buf == '\0') { - free(copy); + p = buf; + while (isspace((u_char) *p)) + p++; + if (*p == '\0') { + free(buf); continue; } /* Parse and run the command. */ - if (cmd_string_parse(buf, &cmdlist, path, n, &cause1) != 0) { - free(copy); + if (cmd_string_parse(p, &cmdlist, path, line, &cause1) != 0) { + free(buf); if (cause1 == NULL) continue; - xasprintf(&msg, "%s:%u: %s", path, n, cause1); - ARRAY_ADD(&cfg_causes, msg); + cfg_add_cause("%s:%zu: %s", path, line, cause1); free(cause1); continue; } - free(copy); + free(buf); if (cmdlist == NULL) continue; - cmdq_append(cmdq, cmdlist); + cmdq_append(cmdq, cmdlist, NULL); cmd_list_free(cmdlist); found++; } - if (line != NULL) - free(line); fclose(f); return (found); } void -cfg_default_done(unused struct cmd_q *cmdq) +cfg_default_done(__unused struct cmd_q *cmdq) { if (--cfg_references != 0) return; cfg_finished = 1; +#ifdef TMATE tmate_session_start(); +#endif if (!RB_EMPTY(&sessions)) cfg_show_causes(RB_MIN(sessions, &sessions)); cmdq_free(cfg_cmd_q); cfg_cmd_q = NULL; + + if (cfg_client != NULL) { + /* + * The client command queue starts with client_exit set to 1 so + * only continue if not empty (that is, we have been delayed + * during configuration parsing for long enough that the + * MSG_COMMAND has arrived), else the client will exit before + * the MSG_COMMAND which might tell it not to. + */ + if (!TAILQ_EMPTY(&cfg_client->cmdq->queue)) + cmdq_continue(cfg_client->cmdq); + server_client_unref(cfg_client); + cfg_client = NULL; + } +} + +void +cfg_add_cause(const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + cfg_ncauses++; + cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); + cfg_causes[cfg_ncauses - 1] = msg; +} + +void +cfg_print_causes(struct cmd_q *cmdq) +{ + u_int i; + + for (i = 0; i < cfg_ncauses; i++) { + cmdq_print(cmdq, "%s", cfg_causes[i]); + free(cfg_causes[i]); + } + + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; } void cfg_show_causes(struct session *s) { struct window_pane *wp; - char *cause; u_int i; - if (s == NULL || ARRAY_EMPTY(&cfg_causes)) + if (s == NULL || cfg_ncauses == 0) return; wp = s->curw->window->active; window_pane_set_mode(wp, &window_copy_mode); window_copy_init_for_output(wp); - for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { - cause = ARRAY_ITEM(&cfg_causes, i); - window_copy_add(wp, "%s", cause); - free(cause); + for (i = 0; i < cfg_ncauses; i++) { + window_copy_add(wp, "%s", cfg_causes[i]); + free(cfg_causes[i]); } - ARRAY_FREE(&cfg_causes); + + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; } diff --git a/client.c b/client.c index 91f47650..e24d07e8 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -26,16 +26,17 @@ #include #include #include -#include +#include #include #include #include #include "tmux.h" -struct imsgbuf client_ibuf; -struct event client_event; -struct event client_stdin; +struct tmuxproc *client_proc; +struct tmuxpeer *client_peer; +int client_flags; +struct event client_stdin; enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -46,56 +47,62 @@ enum { CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, } client_exitreason = CLIENT_EXIT_NONE; -int client_exitval; -enum msgtype client_exittype; -int client_attached; +int client_exitval; +enum msgtype client_exittype; +const char *client_exitsession; +int client_attached; +__dead void client_exec(const char *,const char *); int client_get_lock(char *); -int client_connect(char *, int); -void client_send_identify(int); -void client_send_environ(void); -void client_write_server(enum msgtype, void *, size_t); -void client_update_event(void); -void client_signal(int, short, void *); +int client_connect(struct event_base *, const char *, int); +void client_send_identify(const char *, const char *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); -void client_callback(int, short, void *); -int client_dispatch_attached(void); -int client_dispatch_wait(void *); +void client_signal(int); +void client_dispatch(struct imsg *, void *); +void client_dispatch_attached(struct imsg *); +void client_dispatch_wait(struct imsg *, const char *); const char *client_exit_message(void); /* * Get server create lock. If already held then server start is happening in - * another client, so block until the lock is released and return -1 to - * retry. Ignore other errors - just continue and start the server without the - * lock. + * another client, so block until the lock is released and return -2 to + * retry. Return -1 on failure to continue and start the server anyway. */ int client_get_lock(char *lockfile) { int lockfd; - if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) - fatal("open failed"); + log_debug("lock file is %s", lockfile); - if (flock(lockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) { + if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { + log_debug("open failed: %s", strerror(errno)); + return (-1); + } + + if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { + log_debug("flock failed: %s", strerror(errno)); + if (errno != EAGAIN) + return (lockfd); while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); - return (-1); + return (-2); } + log_debug("flock succeeded"); return (lockfd); } /* Connect client to server. */ int -client_connect(char *path, int start_server) +client_connect(struct event_base *base, const char *path, int start_server) { struct sockaddr_un sa; size_t size; - int fd, lockfd; - char *lockfile; + int fd, lockfd = -1, locked = 0; + char *lockfile = NULL; memset(&sa, 0, sizeof sa); sa.sun_family = AF_UNIX; @@ -104,32 +111,64 @@ client_connect(char *path, int start_server) errno = ENAMETOOLONG; return (-1); } + log_debug("socket is %s", path); retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); + return (-1); - if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { + log_debug("trying connect"); + if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { + log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; if (!start_server) goto failed; close(fd); - xasprintf(&lockfile, "%s.lock", path); - if ((lockfd = client_get_lock(lockfile)) == -1) + if (!locked) { + xasprintf(&lockfile, "%s.lock", path); + if ((lockfd = client_get_lock(lockfile)) < 0) { + log_debug("didn't get lock (%d)", lockfd); + + free(lockfile); + lockfile = NULL; + + if (lockfd == -2) + goto retry; + } + log_debug("got lock (%d)", lockfd); + + /* + * Always retry at least once, even if we got the lock, + * because another client could have taken the lock, + * started the server and released the lock between our + * connect() and flock(). + */ + locked = 1; goto retry; - if (unlink(path) != 0 && errno != ENOENT) + } + + if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { + free(lockfile); + close(lockfd); return (-1); - fd = server_start(lockfd, lockfile); + } + fd = server_start(base, lockfd, lockfile); + } + + if (locked && lockfd >= 0) { free(lockfile); close(lockfd); } - setblocking(fd, 0); return (fd); failed: + if (locked) { + free(lockfile); + close(lockfd); + } close(fd); return (-1); } @@ -138,12 +177,24 @@ failed: const char * client_exit_message(void) { + static char msg[256]; + switch (client_exitreason) { case CLIENT_EXIT_NONE: break; case CLIENT_EXIT_DETACHED: + if (client_exitsession != NULL) { + xsnprintf(msg, sizeof msg, "detached " + "(from session %s)", client_exitsession); + return (msg); + } return ("detached"); case CLIENT_EXIT_DETACHED_HUP: + if (client_exitsession != NULL) { + xsnprintf(msg, sizeof msg, "detached and SIGHUP " + "(from session %s)", client_exitsession); + return (msg); + } return ("detached and SIGHUP"); case CLIENT_EXIT_LOST_TTY: return ("lost tty"); @@ -161,25 +212,34 @@ client_exit_message(void) /* Client main loop. */ int -client_main(int argc, char **argv, int flags) +client_main(struct event_base *base, int argc, char **argv, int flags, + const char *shellcmd) { struct cmd *cmd; struct cmd_list *cmdlist; - struct msg_command_data cmddata; - int cmdflags, fd; + struct msg_command_data *data; + int cmdflags, fd, i; + const char *ttynam, *cwd; pid_t ppid; enum msgtype msg; - char *cause; + char *cause, path[PATH_MAX]; struct termios tio, saved_tio; + size_t size; + + /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ + signal(SIGCHLD, SIG_IGN); + + /* Save the flags. */ + client_flags = flags; /* Set up the initial command. */ cmdflags = 0; - if (shell_cmd != NULL) { + if (shellcmd != NULL) { msg = MSG_SHELL; cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; - cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST; + cmdflags = CMD_STARTSERVER; } else { msg = MSG_COMMAND; @@ -197,53 +257,63 @@ client_main(int argc, char **argv, int flags) 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) { - fprintf(stderr, "sessions should be nested with care, " - "unset $TMUX to force\n"); - return (1); - } + /* Create client process structure (starts logging). */ + client_proc = proc_start("client", base, 0, client_signal); - /* Initialise the client socket and start the server. */ - fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); + /* Initialize the client socket and start the server. */ + fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { - fprintf(stderr, "failed to connect to server\n"); + if (errno == ECONNREFUSED) { + fprintf(stderr, "no server running on %s\n", + socket_path); + } else { + fprintf(stderr, "error connecting to %s (%s)\n", + socket_path, strerror(errno)); + } return (1); } + client_peer = proc_add_peer(client_proc, fd, client_dispatch, + (void *)shellcmd); - /* Set process title, log and signals now this is the client. */ -#ifdef HAVE_SETPROCTITLE - setproctitle("client (%s)", socket_path); + /* Save these before pledge(). */ + if ((cwd = getcwd(path, sizeof path)) == NULL) { + if ((cwd = find_home()) == NULL) + cwd = "/"; + } + if ((ttynam = ttyname(STDIN_FILENO)) == NULL) + ttynam = ""; + +#ifdef __OpenBSD__ + /* + * Drop privileges for client. "proc exec" is needed for -c and for + * locking (which uses system(3)). + * + * "tty" is needed to restore termios(4) and also for some reason -CC + * does not work properly without it (input is not recognised). + * + * "sendfd" is dropped later in client_dispatch_wait(). + */ + if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) + fatal("pledge failed"); #endif - logfile("client"); - /* Create imsg. */ - imsg_init(&client_ibuf, fd); - event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); + /* Free stuff that is not used in the client. */ + options_free(global_options); + options_free(global_s_options); + options_free(global_w_options); + environ_free(global_environ); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); - if (flags & IDENTIFY_TERMIOS) { - if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { - fprintf(stderr, "tcgetattr failed: %s\n", - strerror(errno)); - return (1); - } + if (client_flags & CLIENT_CONTROLCONTROL) { + if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) + fatal("tcgetattr failed"); cfmakeraw(&tio); tio.c_iflag = ICRNL|IXANY; tio.c_oflag = OPOST|ONLCR; @@ -258,203 +328,101 @@ client_main(int argc, char **argv, int flags) tcsetattr(STDIN_FILENO, TCSANOW, &tio); } - /* Establish signal handlers. */ - set_signals(client_signal); - - /* Send initial environment. */ - if (cmdflags & CMD_SENDENVIRON) - client_send_environ(); - client_send_identify(flags); + /* Send identify messages. */ + client_send_identify(ttynam, cwd); /* Send first command. */ if (msg == MSG_COMMAND) { - /* Fill in command line arguments. */ - cmddata.pid = environ_pid; - cmddata.session_id = environ_session_id; + /* How big is the command? */ + size = 0; + for (i = 0; i < argc; i++) + size += strlen(argv[i]) + 1; + data = xmalloc((sizeof *data) + size); /* Prepare command for server. */ - cmddata.argc = argc; - if (cmd_pack_argv( - argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) { + data->argc = argc; + if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) { fprintf(stderr, "command too long\n"); + free(data); return (1); } + size += sizeof *data; - client_write_server(msg, &cmddata, sizeof cmddata); + /* Send the command. */ + if (proc_send(client_peer, msg, -1, data, size) != 0) { + fprintf(stderr, "failed to send command\n"); + free(data); + return (1); + } + free(data); } else if (msg == MSG_SHELL) - client_write_server(msg, NULL, 0); + proc_send(client_peer, msg, -1, NULL, 0); - /* Set the event and dispatch. */ - client_update_event(); - event_dispatch(); + /* Start main loop. */ + proc_loop(client_proc, NULL); /* Print the exit message, if any, and exit. */ if (client_attached) { - if (client_exitreason != CLIENT_EXIT_NONE && !login_shell) + if (client_exitreason != CLIENT_EXIT_NONE) printf("[%s]\n", client_exit_message()); ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } else if (flags & IDENTIFY_TERMIOS) { - if (flags & IDENTIFY_CONTROL) { - if (client_exitreason != CLIENT_EXIT_NONE) - printf("%%exit %s\n", client_exit_message()); - else - printf("%%exit\n"); - printf("\033\\"); - } + } else if (client_flags & CLIENT_CONTROLCONTROL) { + if (client_exitreason != CLIENT_EXIT_NONE) + printf("%%exit %s\n", client_exit_message()); + else + printf("%%exit\n"); + printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); - } + } else if (client_exitreason != CLIENT_EXIT_NONE) + fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); return (client_exitval); } -/* Send identify message to server with the file descriptors. */ +/* Send identify messages to server. */ void -client_send_identify(int flags) +client_send_identify(const char *ttynam, const char *cwd) { - struct msg_identify_data data; - char *term; - int fd; + const char *s; + char **ss; + size_t sslen; + int fd, flags = client_flags; + pid_t pid; - data.flags = flags; + proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); - if (getcwd(data.cwd, sizeof data.cwd) == NULL) - *data.cwd = '\0'; + if ((s = getenv("TERM")) == NULL) + s = ""; + proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - term = getenv("TERM"); - if (term == NULL || - strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) - *data.term = '\0'; + proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, + strlen(ttynam) + 1); + proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); - imsg_compose(&client_ibuf, - MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); - client_update_event(); -} + proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); -/* Forward entire environment to server. */ -void -client_send_environ(void) -{ - struct msg_environ_data data; - char **var; + pid = getpid(); + proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); - for (var = environ; *var != NULL; var++) { - if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var) + for (ss = environ; *ss != NULL; ss++) { + sslen = strlen(*ss) + 1; + if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) continue; - client_write_server(MSG_ENVIRON, &data, sizeof data); - } -} - -/* Write a message to the server without a file descriptor. */ -void -client_write_server(enum msgtype type, void *buf, size_t len) -{ - imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); - client_update_event(); -} - -/* Update client event based on whether it needs to read or read and write. */ -void -client_update_event(void) -{ - short events; - - event_del(&client_event); - events = EV_READ; - if (client_ibuf.w.queued > 0) - events |= EV_WRITE; - event_set( - &client_event, client_ibuf.fd, events, client_callback, shell_cmd); - event_add(&client_event, NULL); -} - -/* Callback to handle signals in the client. */ -void -client_signal(int sig, unused short events, unused void *data) -{ - struct sigaction sigact; - int status; - - if (!client_attached) { - switch (sig) { - case SIGCHLD: - waitpid(WAIT_ANY, &status, WNOHANG); - break; - case SIGTERM: - event_loopexit(NULL); - break; - } - } else { - switch (sig) { - case SIGHUP: - client_exitreason = CLIENT_EXIT_LOST_TTY; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGTERM: - client_exitreason = CLIENT_EXIT_TERMINATED; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGWINCH: - 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; - } + proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } - client_update_event(); -} - -/* Callback for client imsg read events. */ -void -client_callback(unused int fd, short events, void *data) -{ - ssize_t n; - int retval; - - if (events & EV_READ) { - if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) - goto lost_server; - if (client_attached) - retval = client_dispatch_attached(); - else - retval = client_dispatch_wait(data); - if (retval != 0) { - event_loopexit(NULL); - return; - } - } - - if (events & EV_WRITE) { - if (msgbuf_write(&client_ibuf.w) < 0) - goto lost_server; - } - - client_update_event(); - return; - -lost_server: - client_exitreason = CLIENT_EXIT_LOST_SERVER; - client_exitval = 1; - event_loopexit(NULL); + proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } /* Callback for client stdin read events. */ void -client_stdin_callback(unused int fd, unused short events, unused void *data1) +client_stdin_callback(__unused int fd, __unused short events, + __unused void *arg) { struct msg_stdin_data data; @@ -462,10 +430,9 @@ client_stdin_callback(unused int fd, unused short events, unused void *data1) if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) return; - client_write_server(MSG_STDIN, &data, sizeof data); + proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); if (data.size <= 0) event_del(&client_stdin); - client_update_event(); } /* Force write to file descriptor. */ @@ -486,178 +453,252 @@ client_write(int fd, const char *data, size_t size) } } -/* Dispatch imsgs when in wait state (before MSG_READY). */ -int -client_dispatch_wait(void *data) +/* Run command in shell; used for -c. */ +__dead void +client_exec(const char *shell, const char *shellcmd) { - struct imsg imsg; - ssize_t n, datalen; - struct msg_shell_data shelldata; - struct msg_exit_data exitdata; - struct msg_stdout_data stdoutdata; - struct msg_stderr_data stderrdata; - const char *shellcmd = data; + const char *name, *ptr; + char *argv0; - 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; + log_debug("shell %s, command %s", shell, shellcmd); - log_debug("got %d from server", imsg.hdr.type); - 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"); + ptr = strrchr(shell, '/'); + if (ptr != NULL && *(ptr + 1) != '\0') + name = ptr + 1; + else + name = shell; + if (client_flags & CLIENT_LOGIN) + xasprintf(&argv0, "-%s", name); + else + xasprintf(&argv0, "%s", name); + setenv("SHELL", shell, 1); - event_del(&client_stdin); - client_attached = 1; - client_write_server(MSG_RESIZE, NULL, 0); - break; - case MSG_STDIN: - if (datalen != 0) - fatalx("bad MSG_STDIN size"); + setblocking(STDIN_FILENO, 1); + setblocking(STDOUT_FILENO, 1); + setblocking(STDERR_FILENO, 1); + closefrom(STDERR_FILENO + 1); - event_add(&client_stdin, NULL); - break; - case MSG_STDOUT: - if (datalen != sizeof stdoutdata) - fatalx("bad MSG_STDOUT"); - memcpy(&stdoutdata, imsg.data, sizeof stdoutdata); + execl(shell, argv0, "-c", shellcmd, (char *) NULL); + fatal("execl failed"); +} - client_write(STDOUT_FILENO, stdoutdata.data, stdoutdata.size); - break; - case MSG_STDERR: - if (datalen != sizeof stderrdata) - fatalx("bad MSG_STDERR"); - memcpy(&stderrdata, imsg.data, sizeof stderrdata); +/* Callback to handle signals in the client. */ +void +client_signal(int sig) +{ + struct sigaction sigact; + int status; - client_write(STDERR_FILENO, stderrdata.data, stderrdata.size); - break; - case MSG_VERSION: - if (datalen != 0) - fatalx("bad MSG_VERSION size"); - - fprintf(stderr, "protocol version mismatch " - "(client %u, server %u)\n", PROTOCOL_VERSION, - imsg.hdr.peerid); + if (sig == SIGCHLD) + waitpid(WAIT_ANY, &status, WNOHANG); + else if (!client_attached) { + if (sig == SIGTERM) + proc_exit(client_proc); + } else { + switch (sig) { + case SIGHUP: + client_exitreason = CLIENT_EXIT_LOST_TTY; 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 */ - case MSG_DETACH: - client_write_server(MSG_EXITING, NULL, 0); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGTERM: + client_exitreason = CLIENT_EXIT_TERMINATED; + client_exitval = 1; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGWINCH: + proc_send(client_peer, MSG_RESIZE, -1, 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"); + proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); break; - case MSG_EXITED: - imsg_free(&imsg); - return (-1); - default: - fatalx("unexpected message"); } + } +} - imsg_free(&imsg); +/* Callback for client read events. */ +void +client_dispatch(struct imsg *imsg, void *arg) +{ + if (imsg == NULL) { + client_exitreason = CLIENT_EXIT_LOST_SERVER; + client_exitval = 1; + proc_exit(client_proc); + return; + } + + if (client_attached) + client_dispatch_attached(imsg); + else + client_dispatch_wait(imsg, arg); +} + +/* Dispatch imsgs when in wait state (before MSG_READY). */ +void +client_dispatch_wait(struct imsg *imsg, const char *shellcmd) +{ + char *data; + ssize_t datalen; + struct msg_stdout_data stdoutdata; + struct msg_stderr_data stderrdata; + int retval; +#ifdef __OpenBSD__ + static int pledge_applied; + + /* + * "sendfd" is no longer required once all of the identify messages + * have been sent. We know the server won't send us anything until that + * point (because we don't ask it to), so we can drop "sendfd" once we + * get the first message from the server. + */ + if (!pledge_applied) { + if (pledge("stdio unix proc exec tty", NULL) != 0) + fatal("pledge failed"); + pledge_applied = 1; + }; +#endif + + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_EXIT: + case MSG_SHUTDOWN: + if (datalen != sizeof retval && datalen != 0) + fatalx("bad MSG_EXIT size"); + if (datalen == sizeof retval) { + memcpy(&retval, data, sizeof retval); + client_exitval = retval; + } + proc_exit(client_proc); + break; + case MSG_READY: + if (datalen != 0) + fatalx("bad MSG_READY size"); + + event_del(&client_stdin); + client_attached = 1; + proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); + break; + case MSG_STDIN: + if (datalen != 0) + fatalx("bad MSG_STDIN size"); + + event_add(&client_stdin, NULL); + break; + case MSG_STDOUT: + if (datalen != sizeof stdoutdata) + fatalx("bad MSG_STDOUT size"); + memcpy(&stdoutdata, data, sizeof stdoutdata); + + client_write(STDOUT_FILENO, stdoutdata.data, + stdoutdata.size); + break; + case MSG_STDERR: + if (datalen != sizeof stderrdata) + fatalx("bad MSG_STDERR size"); + memcpy(&stderrdata, data, sizeof stderrdata); + + client_write(STDERR_FILENO, stderrdata.data, + stderrdata.size); + break; + case MSG_VERSION: + if (datalen != 0) + fatalx("bad MSG_VERSION size"); + + fprintf(stderr, "protocol version mismatch " + "(client %d, server %u)\n", PROTOCOL_VERSION, + imsg->hdr.peerid & 0xff); + client_exitval = 1; + proc_exit(client_proc); + break; + case MSG_SHELL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_SHELL string"); + + clear_signals(0); + client_exec(data, shellcmd); + /* NOTREACHED */ + case MSG_DETACH: + case MSG_DETACHKILL: + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXITED: + proc_exit(client_proc); + break; } } /* Dispatch imsgs in attached state (after MSG_READY). */ -int -client_dispatch_attached(void) +void +client_dispatch_attached(struct imsg *imsg) { - struct imsg imsg; - struct msg_lock_data lockdata; - struct sigaction sigact; - ssize_t n, datalen; + struct sigaction sigact; + char *data; + ssize_t datalen; - 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; + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - log_debug("got %d from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_DETACHKILL: - case MSG_DETACH: - if (datalen != 0) - fatalx("bad MSG_DETACH size"); + switch (imsg->hdr.type) { + case MSG_DETACH: + case MSG_DETACHKILL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_DETACH string"); - client_exittype = imsg.hdr.type; - if (imsg.hdr.type == MSG_DETACHKILL) - client_exitreason = CLIENT_EXIT_DETACHED_HUP; - else - client_exitreason = CLIENT_EXIT_DETACHED; - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXIT: - if (datalen != 0 && - datalen != sizeof (struct msg_exit_data)) - fatalx("bad MSG_EXIT size"); + client_exitsession = xstrdup(data); + client_exittype = imsg->hdr.type; + if (imsg->hdr.type == MSG_DETACHKILL) + client_exitreason = CLIENT_EXIT_DETACHED_HUP; + else + client_exitreason = CLIENT_EXIT_DETACHED; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXIT: + if (datalen != 0 && datalen != sizeof (int)) + fatalx("bad MSG_EXIT size"); - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_EXITED; - break; - case MSG_EXITED: - if (datalen != 0) - fatalx("bad MSG_EXITED size"); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_EXITED; + break; + case MSG_EXITED: + if (datalen != 0) + fatalx("bad MSG_EXITED size"); - imsg_free(&imsg); - return (-1); - case MSG_SHUTDOWN: - if (datalen != 0) - fatalx("bad MSG_SHUTDOWN size"); + proc_exit(client_proc); + break; + case MSG_SHUTDOWN: + if (datalen != 0) + fatalx("bad MSG_SHUTDOWN size"); - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_SERVER_EXITED; - client_exitval = 1; - break; - case MSG_SUSPEND: - if (datalen != 0) - fatalx("bad MSG_SUSPEND size"); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_SERVER_EXITED; + client_exitval = 1; + break; + case MSG_SUSPEND: + if (datalen != 0) + fatalx("bad MSG_SUSPEND size"); - memset(&sigact, 0, sizeof sigact); - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_RESTART; - sigact.sa_handler = SIG_DFL; - if (sigaction(SIGTSTP, &sigact, NULL) != 0) - fatal("sigaction failed"); - kill(getpid(), SIGTSTP); - break; - case MSG_LOCK: - if (datalen != sizeof lockdata) - fatalx("bad MSG_LOCK size"); - memcpy(&lockdata, imsg.data, sizeof lockdata); + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + kill(getpid(), SIGTSTP); + break; + case MSG_LOCK: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_LOCK string"); - lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; - system(lockdata.cmd); - client_write_server(MSG_UNLOCK, NULL, 0); - break; - default: - fatalx("unexpected message"); - } - - imsg_free(&imsg); + system(data); + proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); + break; } } diff --git a/clock.c b/clock.c deleted file mode 100644 index 49a883cf..00000000 --- a/clock.c +++ /dev/null @@ -1,159 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * 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 - -#include -#include - -#include "tmux.h" - -const char clock_table[14][5][5] = { - { { 1,1,1,1,1 }, /* 0 */ - { 1,0,0,0,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 0,0,0,0,1 }, /* 1 */ - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 2 */ - { 0,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,0 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 3 */ - { 0,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,0,0,0,1 }, /* 4 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 5 */ - { 1,0,0,0,0 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 6 */ - { 1,0,0,0,0 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 7 */ - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 }, - { 0,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* 8 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 1,1,1,1,1 }, /* 9 */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 0,0,0,0,1 }, - { 1,1,1,1,1 } }, - { { 0,0,0,0,0 }, /* : */ - { 0,0,1,0,0 }, - { 0,0,0,0,0 }, - { 0,0,1,0,0 }, - { 0,0,0,0,0 } }, - { { 1,1,1,1,1 }, /* A */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 } }, - { { 1,1,1,1,1 }, /* P */ - { 1,0,0,0,1 }, - { 1,1,1,1,1 }, - { 1,0,0,0,0 }, - { 1,0,0,0,0 } }, - { { 1,0,0,0,1 }, /* M */ - { 1,1,0,1,1 }, - { 1,0,1,0,1 }, - { 1,0,0,0,1 }, - { 1,0,0,0,1 } }, -}; - -void -clock_draw(struct screen_write_ctx *ctx, int colour, int style) -{ - struct screen *s = ctx->s; - struct grid_cell gc; - char tim[64], *ptr; - time_t t; - u_int i, j, x, y, idx; - - t = time(NULL); - if (style == 0) - strftime(tim, sizeof tim, "%l:%M %p", localtime(&t)); - else - strftime(tim, sizeof tim, "%H:%M", localtime(&t)); - - screen_write_clearscreen(ctx); - - if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { - if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { - x = (screen_size_x(s) / 2) - (strlen(tim) / 2); - y = screen_size_y(s) / 2; - screen_write_cursormove(ctx, x, y); - - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_fg(&gc, colour); - screen_write_puts(ctx, &gc, "%s", tim); - } - return; - } - - x = (screen_size_x(s) / 2) - 3 * strlen(tim); - y = (screen_size_y(s) / 2) - 3; - - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_bg(&gc, colour); - for (ptr = tim; *ptr != '\0'; ptr++) { - if (*ptr >= '0' && *ptr <= '9') - idx = *ptr - '0'; - else if (*ptr == ':') - idx = 10; - else if (*ptr == 'A') - idx = 11; - else if (*ptr == 'P') - idx = 12; - else if (*ptr == 'M') - idx = 13; - else { - x += 6; - continue; - } - - for (j = 0; j < 5; j++) { - for (i = 0; i < 5; i++) { - screen_write_cursormove(ctx, x + i, y + j); - if (clock_table[idx][j][i]) - screen_write_putc(ctx, &gc, ' '); - } - } - x += 6; - } -} diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 07185737..993f4c75 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,7 +18,11 @@ #include +#include +#include #include +#include +#include #include "tmux.h" @@ -29,82 +33,122 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { - "attach-session", "attach", - "drt:", 0, 0, - "[-dr] " CMD_TARGET_SESSION_USAGE, - CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON, - NULL, - NULL, - cmd_attach_session_exec + .name = "attach-session", + .alias = "attach", + + .args = { "c:dErt:", 0, 0 }, + .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION_WITHPANE, + + .flags = CMD_STARTSERVER, + .exec = cmd_attach_session_exec }; enum cmd_retval -cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) +cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, + int Eflag) { - struct session *s; - struct client *c; - const char *update; - char *cause; - u_int i; + struct session *s = cmdq->state.tflag.s; + struct client *c = cmdq->client, *c_loop; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; + const char *update; + char *cause, *cwd; + struct format_tree *ft; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); return (CMD_RETURN_ERROR); } - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - - if (cmdq->client == NULL) + if (c == NULL) return (CMD_RETURN_NORMAL); + if (server_client_check_nested(c)) { + cmdq_error(cmdq, "sessions should be nested with care, " + "unset $TMUX to force"); + return (CMD_RETURN_ERROR); + } - if (cmdq->client->session != NULL) { + if (wl != NULL) { + if (wp != NULL) + window_set_active_pane(wp->window, wp); + session_set_current(s, wl); + } + + if (cflag != NULL) { + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, wl, wp); + cwd = format_expand(ft, cflag); + format_free(ft); + + free((void *)s->cwd); + s->cwd = cwd; + } + + if (c->session != NULL) { if (dflag) { - /* - * Can't use server_write_session in case attaching to - * the same session as currently attached to. - */ - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c_loop) continue; - if (c == cmdq->client) - continue; - server_write_client(c, MSG_DETACH, NULL, 0); + server_client_detach(c_loop, MSG_DETACH); } } - cmdq->client->session = s; - notify_attached_session_changed(cmdq->client); - session_update_activity(s); - server_redraw_client(cmdq->client); + if (!Eflag) { + update = options_get_string(s->options, + "update-environment"); + environ_update(update, c->environ, s->environ); + } + + c->session = s; + server_client_set_key_table(c, NULL); + status_timer_start(c); + notify_attached_session_changed(c); + session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); + server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { - if (server_client_open(cmdq->client, s, &cause) != 0) { + if (server_client_open(c, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (rflag) - cmdq->client->flags |= CLIENT_READONLY; + c->flags |= CLIENT_READONLY; - if (dflag) - server_write_session(s, MSG_DETACH, NULL, 0); + if (dflag) { + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c_loop) + continue; + server_client_detach(c_loop, MSG_DETACH); + } + } - update = options_get_string(&s->options, "update-environment"); - environ_update(update, &cmdq->client->environ, &s->environ); + if (!Eflag) { + update = options_get_string(s->options, + "update-environment"); + environ_update(update, c->environ, s->environ); + } - cmdq->client->session = s; - notify_attached_session_changed(cmdq->client); - session_update_activity(s); - server_redraw_client(cmdq->client); + c->session = s; + server_client_set_key_table(c, NULL); + status_timer_start(c); + notify_attached_session_changed(c); + session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); + server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; - server_write_ready(cmdq->client); + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); + hooks_run(c->session->hooks, c, NULL, "client-attached"); cmdq->client_exit = 0; } recalculate_sizes(); + alerts_check_session(s); server_update_socket(); return (CMD_RETURN_NORMAL); @@ -115,6 +159,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - return (cmd_attach_session(cmdq, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'))); + return (cmd_attach_session(cmdq, args_has(args, 'd'), + args_has(args, 'r'), args_get(args, 'c'), args_has(args, 'E'))); } diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 71e79ea0..df3285f7 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,33 +27,22 @@ * Bind a key to a command, this recurses through cmd_*. */ -enum cmd_retval cmd_bind_key_check(struct args *); enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, + key_code); const struct cmd_entry cmd_bind_key_entry = { - "bind-key", "bind", - "cnrt:", 1, -1, - "[-cnr] [-t key-table] key command [arguments]", - 0, - NULL, - cmd_bind_key_check, - cmd_bind_key_exec -}; + .name = "bind-key", + .alias = "bind", -enum cmd_retval -cmd_bind_key_check(struct args *args) -{ - if (args_has(args, 't')) { - if (args->argc != 2 && args->argc != 3) - return (CMD_RETURN_ERROR); - } else { - if (args->argc < 2) - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); -} + .args = { "cnrt:T:", 1, -1 }, + .usage = "[-cnr] [-t mode-table] [-T key-table] key command " + "[arguments]", + + .flags = 0, + .exec = cmd_bind_key_exec +}; enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) @@ -61,16 +50,36 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; char *cause; struct cmd_list *cmdlist; - int key; + key_code key; + const char *tablename; + + if (args_has(args, 't')) { + if (args->argc != 2 && args->argc != 3) { + cmdq_error(cmdq, "not enough arguments"); + return (CMD_RETURN_ERROR); + } + } else { + if (args->argc < 2) { + cmdq_error(cmdq, "not enough arguments"); + return (CMD_RETURN_ERROR); + } + } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (args_has(args, 't')) - return (cmd_bind_key_table(self, cmdq, key)); + return (cmd_bind_key_mode_table(self, cmdq, key)); + + if (args_has(args, 'T')) + tablename = args_get(args, 'T'); + else if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, &cause); @@ -80,14 +89,12 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if (!args_has(args, 'n')) - key |= KEYC_PREFIX; - key_bindings_add(key, args_has(args, 'r'), cmdlist); + key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist); return (CMD_RETURN_NORMAL); } enum cmd_retval -cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) { struct args *args = self->args; const char *tablename; @@ -108,18 +115,34 @@ cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) return (CMD_RETURN_ERROR); } - if (cmd != MODEKEYCOPY_COPYPIPE) { - if (args->argc != 2) { - cmdq_error(cmdq, "no argument allowed"); - return (CMD_RETURN_ERROR); + switch (cmd) { + case MODEKEYCOPY_APPENDSELECTION: + case MODEKEYCOPY_COPYSELECTION: + case MODEKEYCOPY_STARTNAMEDBUFFER: + if (args->argc == 2) + arg = NULL; + else { + arg = args->argv[2]; + if (strcmp(arg, "-x") != 0) { + cmdq_error(cmdq, "unknown argument"); + return (CMD_RETURN_ERROR); + } } - arg = NULL; - } else { + break; + case MODEKEYCOPY_COPYPIPE: if (args->argc != 3) { cmdq_error(cmdq, "no argument given"); return (CMD_RETURN_ERROR); } arg = args->argv[2]; + break; + default: + if (args->argc != 2) { + cmdq_error(cmdq, "no argument allowed"); + return (CMD_RETURN_ERROR); + } + arg = NULL; + break; } mtmp.key = key; diff --git a/cmd-break-pane.c b/cmd-break-pane.c index a0e85513..750ae15f 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -26,16 +26,22 @@ * Break pane off into a window. */ +#define BREAK_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" + enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { - "break-pane", "breakp", - "dPF:t:", 0, 0, - "[-dP] [-F format] " CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_break_pane_exec + .name = "break-pane", + .alias = "breakp", + + .args = { "dPF:s:t:", 0, 0 }, + .usage = "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, + + .sflag = CMD_PANE, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, + .exec = cmd_break_pane_exec }; enum cmd_retval @@ -46,68 +52,61 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); #else struct args *args = self->args; - struct winlink *wl; - struct session *s; - struct window_pane *wp; - struct window *w; + struct winlink *wl = cmdq->state.sflag.wl; + struct session *src_s = cmdq->state.sflag.s; + struct session *dst_s = cmdq->state.tflag.s; + struct window_pane *wp = cmdq->state.sflag.wp; + struct window *w = wl->window; char *name; char *cause; - int base_idx; - struct client *c; + int idx = cmdq->state.tflag.idx; struct format_tree *ft; const char *template; char *cp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - - if (window_count_panes(wl->window) == 1) { - cmdq_error(cmdq, "can't break with only one pane"); + if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { + cmdq_error(cmdq, "index %d already in use", idx); return (CMD_RETURN_ERROR); } - w = wl->window; + if (window_count_panes(w) == 1) { + cmdq_error(cmdq, "can't break with only one pane"); + return (CMD_RETURN_ERROR); + } server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); - if (wp == w->active) { - w->active = w->last; - 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; + window_lost_pane(w, wp); layout_close_pane(wp); - w = wp->window = window_create1(s->sx, s->sy); + w = wp->window = window_create1(dst_s->sx, dst_s->sy); TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; name = default_window_name(w); window_set_name(w, name); free(name); layout_init(w, wp); + wp->flags |= PANE_CHANGED; - base_idx = options_get_number(&s->options, "base-index"); - wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */ + if (idx == -1) + idx = -1 - options_get_number(dst_s->options, "base-index"); + wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) - session_select(s, wl->idx); + session_select(dst_s, wl->idx); - server_redraw_session(s); - server_status_session_group(s); + server_redraw_session(src_s); + if (src_s != dst_s) + server_redraw_session(dst_s); + server_status_session_group(src_s); + if (src_s != dst_s) + server_status_session_group(dst_s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - ft = format_create(); - if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) - format_client(ft, c); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wp); + ft = format_create(cmdq, 0); + format_defaults(ft, cmdq->state.c, dst_s, wl, wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index f59dc2d6..33f6cf08 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Jonathan Alvarado @@ -36,20 +36,23 @@ char *cmd_capture_pane_history(struct args *, struct cmd_q *, struct window_pane *, size_t *); const struct cmd_entry cmd_capture_pane_entry = { - "capture-pane", "capturep", - "ab:CeE:JpPqS:t:", 0, 0, - "[-aCeJpPq] [-b buffer-index] [-E end-line] [-S start-line]" - CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_capture_pane_exec + .name = "capture-pane", + .alias = "capturep", + + .args = { "ab:CeE:JpPqS:t:", 0, 0 }, + .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " + "[-S start-line]" CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_capture_pane_exec }; char * cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen) { - buf = xrealloc(buf, 1, *len + linelen + 1); + buf = xrealloc(buf, *len + linelen + 1); memcpy(buf + *len, line, linelen); *len += linelen; return (buf); @@ -59,15 +62,17 @@ char * cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t *len) { - char *buf, *line, tmp[5]; - size_t linelen; - u_int i; + struct evbuffer *pending; + char *buf, *line, tmp[5]; + size_t linelen; + u_int i; - if (wp->ictx.since_ground == NULL) + pending = input_pending(wp); + if (pending == NULL) return (xstrdup("")); - line = EVBUFFER_DATA(wp->ictx.since_ground); - linelen = EVBUFFER_LENGTH(wp->ictx.since_ground); + line = EVBUFFER_DATA(pending); + linelen = EVBUFFER_LENGTH(pending); buf = xstrdup(""); if (args_has(args, 'C')) { @@ -76,7 +81,7 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp, tmp[0] = line[i]; tmp[1] = '\0'; } else - xsnprintf(tmp, sizeof tmp, "\\%03o", line[i]); + xsnprintf(tmp, sizeof tmp, "\\%03hho", line[i]); buf = cmd_capture_pane_append(buf, len, tmp, strlen(tmp)); } @@ -95,6 +100,7 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, int n, with_codes, escape_c0, join_lines; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; + const char *Sflag, *Eflag; size_t linelen; sx = screen_size_x(&wp->base); @@ -110,27 +116,37 @@ cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, } else gd = wp->base.grid; - n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); - if (cause != NULL) { - top = gd->hsize; - free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) + Sflag = args_get(args, 'S'); + if (Sflag != NULL && strcmp(Sflag, "-") == 0) top = 0; - else - top = gd->hsize + n; - if (top > gd->hsize + gd->sy - 1) - top = gd->hsize + gd->sy - 1; + else { + n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + top = gd->hsize; + free(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + top = 0; + else + top = gd->hsize + n; + if (top > gd->hsize + gd->sy - 1) + top = gd->hsize + gd->sy - 1; + } - n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); - if (cause != NULL) { - bottom = gd->hsize + gd->sy - 1; - free(cause); - } else if (n < 0 && (u_int) -n > gd->hsize) - bottom = 0; - else - bottom = gd->hsize + n; - if (bottom > gd->hsize + gd->sy - 1) + Eflag = args_get(args, 'E'); + if (Eflag != NULL && strcmp(Eflag, "-") == 0) bottom = gd->hsize + gd->sy - 1; + else { + n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + bottom = gd->hsize + gd->sy - 1; + free(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + bottom = 0; + else + bottom = gd->hsize + n; + if (bottom > gd->hsize + gd->sy - 1) + bottom = gd->hsize + gd->sy - 1; + } if (bottom < top) { tmp = bottom; @@ -164,15 +180,11 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; char *buf, *cause; - int buffer; - u_int limit; + const char *bufname; size_t len; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - len = 0; if (args_has(args, 'P')) buf = cmd_capture_pane_pending(args, wp, &len); @@ -186,29 +198,22 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (c == NULL || (c->session != NULL && !(c->flags & CLIENT_CONTROL))) { cmdq_error(cmdq, "can't write to stdout"); + free(buf); return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); + free(buf); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } else { - limit = options_get_number(&global_options, "buffer-limit"); - if (!args_has(args, 'b')) { - paste_add(&global_buffers, buf, len, limit); - return (CMD_RETURN_NORMAL); - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(buf); + if (paste_set(buf, len, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(cause); - return (CMD_RETURN_ERROR); - } - - if (paste_replace(&global_buffers, buffer, buf, len) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); free(buf); return (CMD_RETURN_ERROR); } diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index e6b79d91..1f8fbfb2 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott @@ -27,31 +27,37 @@ * Enter choice mode to choose a buffer. */ +#define CHOOSE_BUFFER_TEMPLATE \ + "#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}" + enum cmd_retval cmd_choose_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_buffer_entry = { - "choose-buffer", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, - NULL, - NULL, - cmd_choose_buffer_exec + .name = "choose-buffer", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_choose_buffer_exec }; enum cmd_retval cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; + struct winlink *wl = cmdq->state.tflag.wl; struct window_choose_data *cdata; - struct winlink *wl; struct paste_buffer *pb; char *action, *action_data; const char *template; u_int idx; - if ((c = cmd_current_client(cmdq)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } @@ -59,10 +65,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = CHOOSE_BUFFER_TEMPLATE; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - - if (paste_get_top(&global_buffers) == NULL) + if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) @@ -74,19 +77,20 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) action = xstrdup("paste-buffer -b '%%'"); idx = 0; - while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = idx - 1; + cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_add(cdata->ft, "line", "%u", idx - 1); - format_paste_buffer(cdata->ft, pb); + format_defaults_paste_buffer(cdata->ft, pb); - xasprintf(&action_data, "%u", idx - 1); + xasprintf(&action_data, "%s", paste_buffer_name(pb)); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); window_choose_add(wl->window->active, cdata); + idx++; } free(action); diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 40752a70..7d5fc606 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -27,18 +27,27 @@ * Enter choice mode to choose a client. */ +#define CHOOSE_CLIENT_TEMPLATE \ + "#{client_tty}: #{session_name} " \ + "[#{client_width}x#{client_height} #{client_termname}]" \ + "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \ + "(last used #{t:client_activity})" + enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_q *); void cmd_choose_client_callback(struct window_choose_data *); const struct cmd_entry cmd_choose_client_entry = { - "choose-client", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, - NULL, - NULL, - cmd_choose_client_exec + .name = "choose-client", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_choose_client_exec }; struct cmd_choose_client_data { @@ -49,22 +58,19 @@ enum cmd_retval cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct client *c1; struct window_choose_data *cdata; - struct winlink *wl; + struct winlink *wl = cmdq->state.tflag.wl; const char *template; char *action; - u_int i, idx, cur; + u_int idx, cur; - if ((c = cmd_current_client(cmdq)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); @@ -77,25 +83,24 @@ cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) action = xstrdup("detach-client -t '%%'"); cur = idx = 0; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c1 = ARRAY_ITEM(&clients, i); - if (c1 == NULL || c1->session == NULL || c1->tty.path == NULL) + TAILQ_FOREACH(c1, &clients, entry) { + if (c1->session == NULL || c1->tty.path == NULL) continue; if (c1 == cmdq->client) cur = idx; - idx++; cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = i; + cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_add(cdata->ft, "line", "%u", i); - format_session(cdata->ft, c1->session); - format_client(cdata->ft, c1); + format_add(cdata->ft, "line", "%u", idx); + format_defaults(cdata->ft, c1, NULL, NULL, NULL); cdata->command = cmd_template_replace(action, c1->tty.path, 1); window_choose_add(wl->window->active, cdata); + + idx++; } free(action); @@ -109,15 +114,19 @@ void cmd_choose_client_callback(struct window_choose_data *cdata) { struct client *c; + u_int idx; if (cdata == NULL) return; if (cdata->start_client->flags & CLIENT_DEAD) return; - if (cdata->idx > ARRAY_LENGTH(&clients) - 1) - return; - c = ARRAY_ITEM(&clients, cdata->idx); + idx = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (idx == cdata->idx) + break; + idx++; + } if (c == NULL || c->session == NULL) return; diff --git a/cmd-choose-list.c b/cmd-choose-list.c deleted file mode 100644 index 15f87294..00000000 --- a/cmd-choose-list.c +++ /dev/null @@ -1,98 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2012 Thomas Adam - * - * 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 - -#include -#include - -#include - -#include "tmux.h" - -#define CMD_CHOOSE_LIST_DEFAULT_TEMPLATE "run-shell '%%'" - -/* - * Enter choose mode to choose a custom list. - */ - -enum cmd_retval cmd_choose_list_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_choose_list_entry = { - "choose-list", NULL, - "l:t:", 0, 1, - "[-l items] " CMD_TARGET_WINDOW_USAGE "[template]", - 0, - NULL, - NULL, - cmd_choose_list_exec -}; - -enum cmd_retval -cmd_choose_list_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - struct client *c; - struct winlink *wl; - const char *list1; - char *template, *item, *copy, *list; - u_int idx; - - if ((c = cmd_current_client(cmdq)) == NULL) { - cmdq_error(cmdq, "no client available"); - return (CMD_RETURN_ERROR); - } - - if ((list1 = args_get(args, 'l')) == NULL) - return (CMD_RETURN_ERROR); - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) - return (CMD_RETURN_NORMAL); - - if (args->argc != 0) - template = xstrdup(args->argv[0]); - else - template = xstrdup(CMD_CHOOSE_LIST_DEFAULT_TEMPLATE); - - copy = list = xstrdup(list1); - idx = 0; - while ((item = strsep(&list, ",")) != NULL) - { - if (*item == '\0') /* no empty entries */ - continue; - window_choose_add_item(wl->window->active, c, wl, item, - template, idx); - idx++; - } - free(copy); - - if (idx == 0) { - free(template); - window_pane_reset_mode(wl->window->active); - return (CMD_RETURN_ERROR); - } - - window_choose_ready(wl->window->active, 0, NULL); - - free(template); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index e2d382b3..db9222ba 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2012 Thomas Adam @@ -32,46 +32,64 @@ * Enter choice mode to choose a session and/or window. */ +#define CHOOSE_TREE_SESSION_TEMPLATE \ + "#{session_name}: #{session_windows} windows" \ + "#{?session_grouped, (group ,}" \ + "#{session_group}#{?session_grouped,),}" \ + "#{?session_attached, (attached),}" +#define CHOOSE_TREE_WINDOW_TEMPLATE \ + "#{window_index}: #{window_name}#{window_flags} " \ + "\"#{pane_title}\"" + enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_tree_entry = { - "choose-tree", NULL, - "S:W:swub:c:t:", 0, 1, - "[-suw] [-b session-template] [-c window template] [-S format] " \ - "[-W format] " CMD_TARGET_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_choose_tree_exec + .name = "choose-tree", + .alias = NULL, + + .args = { "S:W:swub:c:t:", 0, 1 }, + .usage = "[-suw] [-b session-template] [-c window template] " + "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_session_entry = { - "choose-session", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, - NULL, - NULL, - cmd_choose_tree_exec + .name = "choose-session", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_window_entry = { - "choose-window", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE "[-F format] [template]", - 0, - NULL, - NULL, - cmd_choose_tree_exec + .name = "choose-window", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_choose_tree_exec }; enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl, *wm; - struct session *s, *s2; - struct client *c; + struct client *c = cmdq->state.c; + struct winlink *wl = cmdq->state.tflag.wl, *wm; + struct session *s = cmdq->state.tflag.s, *s2; struct window_choose_data *wcd = NULL; const char *ses_template, *win_template; char *final_win_action, *cur_win_template; @@ -84,17 +102,11 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) ses_template = win_template = NULL; ses_action = win_action = NULL; - if ((c = cmd_current_client(cmdq)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((s = c->session) == NULL) - return (CMD_RETURN_ERROR); - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); @@ -232,8 +244,10 @@ windows_only: window_choose_ready(wl->window->active, cur_win, NULL); - if (args_has(args, 'u')) + if (args_has(args, 'u')) { window_choose_expand_all(wl->window->active); + window_choose_set_current(wl->window->active, cur_win); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-clear-history.c b/cmd-clear-history.c index aebaa27d..1236e7f1 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -27,28 +27,29 @@ enum cmd_retval cmd_clear_history_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clear_history_entry = { - "clear-history", "clearhist", - "t:", 0, 0, - CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_clear_history_exec + .name = "clear-history", + .alias = "clearhist", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_clear_history_exec }; enum cmd_retval -cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_clear_history_exec(__unused struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; struct grid *gd; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - gd = wp->base.grid; + gd = cmdq->state.tflag.wp->base.grid; - grid_move_lines(gd, 0, gd->hsize, gd->sy); - gd->hsize = 0; + if (wp->mode == &window_copy_mode) + window_pane_reset_mode(wp); + grid_clear_history(gd); return (CMD_RETURN_NORMAL); } diff --git a/cmd-clock-mode.c b/cmd-clock-mode.c deleted file mode 100644 index 872f3d53..00000000 --- a/cmd-clock-mode.c +++ /dev/null @@ -1,51 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * 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 - -#include "tmux.h" - -/* - * Enter clock mode. - */ - -enum cmd_retval cmd_clock_mode_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_clock_mode_entry = { - "clock-mode", NULL, - "t:", 0, 0, - CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_clock_mode_exec -}; - -enum cmd_retval -cmd_clock_mode_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - struct window_pane *wp; - - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - - window_pane_set_mode(wp, &window_clock_mode); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 3bb79ed9..9200ada1 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -29,21 +29,23 @@ * Prompt for command in client. */ -void cmd_command_prompt_key_binding(struct cmd *, int); -int cmd_command_prompt_check(struct args *); enum cmd_retval cmd_command_prompt_exec(struct cmd *, struct cmd_q *); int cmd_command_prompt_callback(void *, const char *); void cmd_command_prompt_free(void *); const struct cmd_entry cmd_command_prompt_entry = { - "command-prompt", NULL, - "I:p:t:", 0, 1, - "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", - 0, - cmd_command_prompt_key_binding, - NULL, - cmd_command_prompt_exec + .name = "command-prompt", + .alias = NULL, + + .args = { "I:p:t:", 0, 1 }, + .usage = "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " + "[template]", + + .tflag = CMD_CLIENT, + + .flags = 0, + .exec = cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { @@ -56,47 +58,16 @@ struct cmd_command_prompt_cdata { int idx; }; -void -cmd_command_prompt_key_binding(struct cmd *self, int key) -{ - switch (key) { - case '$': - self->args = args_create(1, "rename-session '%%'"); - args_set(self->args, 'I', "#S"); - break; - case ',': - self->args = args_create(1, "rename-window '%%'"); - args_set(self->args, 'I', "#W"); - break; - case '.': - self->args = args_create(1, "move-window -t '%%'"); - break; - case 'f': - self->args = args_create(1, "find-window '%%'"); - break; - case '\'': - self->args = args_create(1, "select-window -t ':%%'"); - args_set(self->args, 'p', "index"); - break; - default: - self->args = args_create(0); - break; - } -} - enum cmd_retval cmd_command_prompt_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; - struct client *c; + struct client *c = cmdq->state.c; char *prompt, *ptr, *input = NULL; size_t n; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (c->prompt_string != NULL) return (CMD_RETURN_NORMAL); @@ -183,7 +154,7 @@ cmd_command_prompt_callback(void *data, const char *s) return (0); } - cmdq_run(c->cmdq, cmdlist); + cmdq_run(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index e670f69c..b5a12821 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha @@ -16,6 +16,8 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + #include #include #include @@ -26,20 +28,22 @@ * Asks for confirmation before executing a command. */ -void cmd_confirm_before_key_binding(struct cmd *, int); enum cmd_retval cmd_confirm_before_exec(struct cmd *, struct cmd_q *); int cmd_confirm_before_callback(void *, const char *); void cmd_confirm_before_free(void *); const struct cmd_entry cmd_confirm_before_entry = { - "confirm-before", "confirm", - "p:t:", 1, 1, - "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - 0, - cmd_confirm_before_key_binding, - NULL, - cmd_confirm_before_exec + .name = "confirm-before", + .alias = "confirm", + + .args = { "p:t:", 1, 1 }, + .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", + + .tflag = CMD_CLIENT, + + .flags = 0, + .exec = cmd_confirm_before_exec }; struct cmd_confirm_before_data { @@ -47,36 +51,15 @@ struct cmd_confirm_before_data { struct client *client; }; -void -cmd_confirm_before_key_binding(struct cmd *self, int key) -{ - switch (key) { - case '&': - self->args = args_create(1, "kill-window"); - args_set(self->args, 'p', "kill-window #W? (y/n)"); - break; - case 'x': - self->args = args_create(1, "kill-pane"); - args_set(self->args, 'p', "kill-pane #P? (y/n)"); - break; - default: - self->args = args_create(0); - break; - } -} - enum cmd_retval cmd_confirm_before_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_confirm_before_data *cdata; - struct client *c; + struct client *c = cmdq->state.c; char *cmd, *copy, *new_prompt, *ptr; const char *prompt; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { @@ -124,7 +107,7 @@ cmd_confirm_before_callback(void *data, const char *s) return (0); } - cmdq_run(c->cmdq, cmdlist); + cmdq_run(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); return (0); @@ -136,7 +119,7 @@ cmd_confirm_before_free(void *data) struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; - c->references--; + server_client_unref(c); free(cdata->cmd); free(cdata); diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index f014be83..1a006ebb 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -21,42 +21,65 @@ #include "tmux.h" /* - * Enter copy mode. + * Enter copy or clock mode. */ -void cmd_copy_mode_key_binding(struct cmd *, int); enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { - "copy-mode", NULL, - "t:u", 0, 0, - "[-u] " CMD_TARGET_PANE_USAGE, - 0, - cmd_copy_mode_key_binding, - NULL, - cmd_copy_mode_exec + .name = "copy-mode", + .alias = NULL, + + .args = { "Met:u", 0, 0 }, + .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_copy_mode_exec }; -void -cmd_copy_mode_key_binding(struct cmd *self, int key) -{ - self->args = args_create(0); - if (key == KEYC_PPAGE) - args_set(self->args, 'u', NULL); -} +const struct cmd_entry cmd_clock_mode_entry = { + .name = "clock-mode", + .alias = NULL, + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_PANE_USAGE, + + .flags = 0, + .exec = cmd_copy_mode_exec +}; enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct window_pane *wp; + struct client *c = cmdq->client; + struct session *s; + struct window_pane *wp = cmdq->state.tflag.wp; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); + if (args_has(args, 'M')) { + if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL) + return (CMD_RETURN_NORMAL); + if (c == NULL || c->session != s) + return (CMD_RETURN_NORMAL); + } - if (window_pane_set_mode(wp, &window_copy_mode) != 0) + if (self->entry == &cmd_clock_mode_entry) { + window_pane_set_mode(wp, &window_clock_mode); return (CMD_RETURN_NORMAL); - window_copy_init_from_pane(wp); + } + + if (wp->mode != &window_copy_mode) { + if (window_pane_set_mode(wp, &window_copy_mode) != 0) + return (CMD_RETURN_NORMAL); + window_copy_init_from_pane(wp, args_has(self->args, 'e')); + } + if (args_has(args, 'M')) { + if (wp->mode != NULL && wp->mode != &window_copy_mode) + return (CMD_RETURN_NORMAL); + window_copy_start_drag(c, &cmdq->item->mouse); + } if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp); diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c deleted file mode 100644 index bc3982ca..00000000 --- a/cmd-delete-buffer.c +++ /dev/null @@ -1,66 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * 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 - -#include - -#include "tmux.h" - -/* - * Delete a paste buffer. - */ - -enum cmd_retval cmd_delete_buffer_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_delete_buffer_entry = { - "delete-buffer", "deleteb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - NULL, - NULL, - cmd_delete_buffer_exec -}; - -enum cmd_retval -cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - char *cause; - int buffer; - - if (!args_has(args, 'b')) { - paste_free_top(&global_buffers); - return (CMD_RETURN_NORMAL); - } - - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - if (paste_free_index(&global_buffers, buffer) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); - return (CMD_RETURN_ERROR); - } - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 6e00e079..daf9a5c6 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,6 +18,8 @@ #include +#include + #include "tmux.h" /* @@ -27,23 +29,46 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { - "detach-client", "detach", - "as:t:P", 0, 0, - "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, - CMD_READONLY, - NULL, - NULL, - cmd_detach_client_exec + .name = "detach-client", + .alias = "detach", + + .args = { "as:t:P", 0, 0 }, + .usage = "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + + .sflag = CMD_SESSION, + .tflag = CMD_CLIENT, + + .flags = CMD_READONLY, + .exec = cmd_detach_client_exec +}; + +const struct cmd_entry cmd_suspend_client_entry = { + .name = "suspend-client", + .alias = "suspendc", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .tflag = CMD_CLIENT, + + .flags = 0, + .exec = cmd_detach_client_exec }; enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c, *c2; - struct session *s; - enum msgtype msgtype; - u_int i; + struct client *c = cmdq->state.c, *cloop; + struct session *s; + enum msgtype msgtype; + + if (self->entry == &cmd_suspend_client_entry) { + tty_stop_tty(&c->tty); + c->flags |= CLIENT_SUSPENDED; + proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); + return (CMD_RETURN_NORMAL); + } if (args_has(args, 'P')) msgtype = MSG_DETACHKILL; @@ -51,30 +76,22 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) msgtype = MSG_DETACH; if (args_has(args, 's')) { - s = cmd_find_session(cmdq, args_get(args, 's'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session == s) - server_write_client(c, msgtype, NULL, 0); + s = cmdq->state.sflag.s; + TAILQ_FOREACH(cloop, &clients, entry) { + if (cloop->session == s) + server_client_detach(cloop, msgtype); } - } else { - c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - - if (args_has(args, 'a')) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c2 = ARRAY_ITEM(&clients, i); - if (c2 == NULL || c == c2) - continue; - server_write_client(c2, msgtype, NULL, 0); - } - } else - server_write_client(c, msgtype, NULL, 0); + return (CMD_RETURN_STOP); } + if (args_has(args, 'a')) { + TAILQ_FOREACH(cloop, &clients, entry) { + if (cloop->session != NULL && cloop != c) + server_client_detach(cloop, msgtype); + } + return (CMD_RETURN_NORMAL); + } + + server_client_detach(c, msgtype); return (CMD_RETURN_STOP); } diff --git a/cmd-display-message.c b/cmd-display-message.c index 485ccf08..a041b5a1 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha @@ -27,79 +27,55 @@ * Displays a message in the status line. */ +#define DISPLAY_MESSAGE_TEMPLATE \ + "[#{session_name}] #{window_index}:" \ + "#{window_name}, current pane #{pane_index} " \ + "- (%H:%M %d-%b-%y)" + enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_message_entry = { - "display-message", "display", - "c:pt:F:", 0, 1, - "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE - " [message]", - 0, - NULL, - NULL, - cmd_display_message_exec + .name = "display-message", + .alias = "display", + + .args = { "c:pt:F:", 0, 1 }, + .usage = "[-p] [-c target-client] [-F format] " + CMD_TARGET_PANE_USAGE " [message]", + + .cflag = CMD_CLIENT_CANFAIL, + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_display_message_exec }; enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct client *c = cmdq->state.c; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; const char *template; char *msg; struct format_tree *ft; - char out[BUFSIZ]; - time_t t; - size_t len; - - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - } else { - wl = cmd_find_pane(cmdq, NULL, &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - } if (args_has(args, 'F') && args->argc != 0) { cmdq_error(cmdq, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } - if (args_has(args, 'c')) { - c = cmd_find_client(cmdq, args_get(args, 'c'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - } else { - c = cmd_current_client(cmdq); - if (c == NULL && !args_has(self->args, 'p')) { - cmdq_error(cmdq, "no client available"); - return (CMD_RETURN_ERROR); - } - } - template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; - ft = format_create(); - if (c != NULL) - format_client(ft, c); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wp); + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, wl, wp); - t = time(NULL); - len = strftime(out, sizeof out, template, localtime(&t)); - out[len] = '\0'; - - msg = format_expand(ft, out); + msg = format_expand_time(ft, template, time(NULL)); if (args_has(self->args, 'p')) cmdq_print(cmdq, "%s", msg); else diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 4a8731a4..d8db4066 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -27,25 +27,22 @@ enum cmd_retval cmd_display_panes_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_panes_entry = { - "display-panes", "displayp", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - 0, - NULL, - NULL, - cmd_display_panes_exec + .name = "display-panes", + .alias = "displayp", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .tflag = CMD_CLIENT, + + .flags = 0, + .exec = cmd_display_panes_exec }; enum cmd_retval -cmd_display_panes_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_display_panes_exec(__unused struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - server_set_identify(c); + server_set_identify(cmdq->state.c); return (CMD_RETURN_NORMAL); } diff --git a/cmd-find-window.c b/cmd-find-window.c index 02f19307..eb940d8c 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -28,6 +28,11 @@ * Find window containing text. */ +#define FIND_WINDOW_TEMPLATE \ + "#{window_index}: #{window_name} " \ + "[#{window_width}x#{window_height}] " \ + "(#{window_panes} panes) #{window_find_matches}" + enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmd_q *); void cmd_find_window_callback(struct window_choose_data *); @@ -43,24 +48,28 @@ void cmd_find_window_callback(struct window_choose_data *); CMD_FIND_WINDOW_BY_NAME) const struct cmd_entry cmd_find_window_entry = { - "find-window", "findw", - "F:CNt:T", 1, 4, - "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", - 0, - NULL, - NULL, - cmd_find_window_exec + .name = "find-window", + .alias = "findw", + + .args = { "F:CNt:T", 1, 4 }, + .usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_find_window_exec }; struct cmd_find_window_data { struct winlink *wl; char *list_ctx; u_int pane_id; + TAILQ_ENTRY(cmd_find_window_data) entry; }; -ARRAY_DECL(cmd_find_window_data_list, struct cmd_find_window_data); +TAILQ_HEAD(cmd_find_window_list, cmd_find_window_data); u_int cmd_find_window_match_flags(struct args *); -void cmd_find_window_match(struct cmd_find_window_data_list *, int, +void cmd_find_window_match(struct cmd_find_window_list *, int, struct winlink *, const char *, const char *); u_int @@ -84,15 +93,16 @@ cmd_find_window_match_flags(struct args *args) } void -cmd_find_window_match(struct cmd_find_window_data_list *find_list, - int match_flags, struct winlink *wl, const char *str, const char *searchstr) +cmd_find_window_match(struct cmd_find_window_list *find_list, + int match_flags, struct winlink *wl, const char *str, + const char *searchstr) { - struct cmd_find_window_data find_data; + struct cmd_find_window_data *find_data; struct window_pane *wp; u_int i, line; char *sres; - memset(&find_data, 0, sizeof find_data); + find_data = xcalloc(1, sizeof *find_data); i = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { @@ -100,53 +110,53 @@ cmd_find_window_match(struct cmd_find_window_data_list *find_list, if ((match_flags & CMD_FIND_WINDOW_BY_NAME) && fnmatch(searchstr, wl->window->name, 0) == 0) { - find_data.list_ctx = xstrdup(""); + find_data->list_ctx = xstrdup(""); break; } if ((match_flags & CMD_FIND_WINDOW_BY_TITLE) && fnmatch(searchstr, wp->base.title, 0) == 0) { - xasprintf(&find_data.list_ctx, + xasprintf(&find_data->list_ctx, "pane %u title: \"%s\"", i - 1, wp->base.title); break; } if (match_flags & CMD_FIND_WINDOW_BY_CONTENT && (sres = window_pane_search(wp, str, &line)) != NULL) { - xasprintf(&find_data.list_ctx, + xasprintf(&find_data->list_ctx, "pane %u line %u: \"%s\"", i - 1, line + 1, sres); free(sres); break; } } - if (find_data.list_ctx != NULL) { - find_data.wl = wl; - find_data.pane_id = i - 1; - ARRAY_ADD(find_list, find_data); - } + + if (find_data->list_ctx != NULL) { + find_data->wl = wl; + find_data->pane_id = i - 1; + TAILQ_INSERT_TAIL(find_list, find_data, entry); + } else + free(find_data); } enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct window_choose_data *cdata; - struct session *s; - struct winlink *wl, *wm; - struct cmd_find_window_data_list find_list; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl, *wm; + struct cmd_find_window_list find_list; + struct cmd_find_window_data *find_data; + struct cmd_find_window_data *find_data1; char *str, *searchstr; const char *template; u_int i, match_flags; - if ((c = cmd_current_client(cmdq)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - s = c->session; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); if ((template = args_get(args, 'F')) == NULL) template = FIND_WINDOW_TEMPLATE; @@ -154,21 +164,20 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) match_flags = cmd_find_window_match_flags(args); str = args->argv[0]; - ARRAY_INIT(&find_list); + TAILQ_INIT(&find_list); xasprintf(&searchstr, "*%s*", str); RB_FOREACH(wm, winlinks, &s->windows) - cmd_find_window_match (&find_list, match_flags, wm, str, searchstr); + cmd_find_window_match(&find_list, match_flags, wm, str, searchstr); free(searchstr); - if (ARRAY_LENGTH(&find_list) == 0) { + if (TAILQ_EMPTY(&find_list)) { cmdq_error(cmdq, "no windows matching: %s", str); - ARRAY_FREE(&find_list); return (CMD_RETURN_ERROR); } - if (ARRAY_LENGTH(&find_list) == 1) { - if (session_select(s, ARRAY_FIRST(&find_list).wl->idx) == 0) + if (TAILQ_NEXT(TAILQ_FIRST(&find_list), entry) == NULL) { + if (session_select(s, TAILQ_FIRST(&find_list)->wl->idx) == 0) server_redraw_session(s); recalculate_sizes(); goto out; @@ -177,30 +186,33 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) goto out; - for (i = 0; i < ARRAY_LENGTH(&find_list); i++) { - wm = ARRAY_ITEM(&find_list, i).wl; - + i = 0; + TAILQ_FOREACH(find_data, &find_list, entry) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = wm->idx; - cdata->wl = wm; + cdata->idx = find_data->wl->idx; + cdata->wl = find_data->wl; cdata->ft_template = xstrdup(template); - cdata->pane_id = ARRAY_ITEM(&find_list, i).pane_id; + cdata->pane_id = find_data->pane_id; format_add(cdata->ft, "line", "%u", i); format_add(cdata->ft, "window_find_matches", "%s", - ARRAY_ITEM(&find_list, i).list_ctx); - format_session(cdata->ft, s); - format_winlink(cdata->ft, s, wm); - format_window_pane(cdata->ft, wm->window->active); + find_data->list_ctx); + format_defaults(cdata->ft, NULL, s, find_data->wl, NULL); window_choose_add(wl->window->active, cdata); + + i++; } window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: - ARRAY_FREE(&find_list); + TAILQ_FOREACH_SAFE(find_data, &find_list, entry, find_data1) { + free(find_data->list_ctx); + TAILQ_REMOVE(&find_list, find_data, entry); + free(find_data); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-find.c b/cmd-find.c new file mode 100644 index 00000000..0e85152a --- /dev/null +++ b/cmd-find.c @@ -0,0 +1,1228 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Nicholas Marriott + * + * 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 + +#include +#include +#include +#include +#include + +#include "tmux.h" + +struct session *cmd_find_try_TMUX(struct client *, struct window *); +int cmd_find_client_better(struct client *, struct client *); +struct client *cmd_find_best_client(struct client **, u_int); +int cmd_find_session_better(struct session *, struct session *, + int); +struct session *cmd_find_best_session(struct session **, u_int, int); +int cmd_find_best_session_with_window(struct cmd_find_state *); +int cmd_find_best_winlink_with_window(struct cmd_find_state *); + +int cmd_find_current_session_with_client(struct cmd_find_state *); +int cmd_find_current_session(struct cmd_find_state *); +struct client *cmd_find_current_client(struct cmd_q *); + +const char *cmd_find_map_table(const char *[][2], const char *); + +int cmd_find_get_session(struct cmd_find_state *, const char *); +int cmd_find_get_window(struct cmd_find_state *, const char *); +int cmd_find_get_window_with_session(struct cmd_find_state *, const char *); +int cmd_find_get_window_with_pane(struct cmd_find_state *); +int cmd_find_get_pane(struct cmd_find_state *, const char *); +int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); +int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); + +const char *cmd_find_session_table[][2] = { + { NULL, NULL } +}; +const char *cmd_find_window_table[][2] = { + { "{start}", "^" }, + { "{last}", "!" }, + { "{end}", "$" }, + { "{next}", "+" }, + { "{previous}", "-" }, + { NULL, NULL } +}; +const char *cmd_find_pane_table[][2] = { + { "{last}", "!" }, + { "{next}", "+" }, + { "{previous}", "-" }, + { "{top}", "top" }, + { "{bottom}", "bottom" }, + { "{left}", "left" }, + { "{right}", "right" }, + { "{top-left}", "top-left" }, + { "{top-right}", "top-right" }, + { "{bottom-left}", "bottom-left" }, + { "{bottom-right}", "bottom-right" }, + { "{up-of}", "{up-of}" }, + { "{down-of}", "{down-of}" }, + { "{left-of}", "{left-of}" }, + { "{right-of}", "{right-of}" }, + { NULL, NULL } +}; + +/* Get session from TMUX if present. */ +struct session * +cmd_find_try_TMUX(struct client *c, struct window *w) +{ + struct environ_entry *envent; + char tmp[256]; + long long pid; + u_int session; + struct session *s; + + envent = environ_find(c->environ, "TMUX"); + if (envent == NULL) + return (NULL); + + if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3) + return (NULL); + if (pid != getpid()) + return (NULL); + log_debug("client %p TMUX is %s (session @%u)", c, envent->value, + session); + + s = session_find_by_id(session); + if (s == NULL || (w != NULL && !session_has(s, w))) + return (NULL); + return (s); +} + +/* Is this client better? */ +int +cmd_find_client_better(struct client *c, struct client *than) +{ + if (than == NULL) + return (1); + return (timercmp(&c->activity_time, &than->activity_time, >)); +} + +/* Find best client from a list, or all if list is NULL. */ +struct client * +cmd_find_best_client(struct client **clist, u_int csize) +{ + struct client *c_loop, *c; + u_int i; + + c = NULL; + if (clist != NULL) { + for (i = 0; i < csize; i++) { + if (clist[i]->session == NULL) + continue; + if (cmd_find_client_better(clist[i], c)) + c = clist[i]; + } + } else { + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session == NULL) + continue; + if (cmd_find_client_better(c_loop, c)) + c = c_loop; + } + } + return (c); +} + +/* Is this session better? */ +int +cmd_find_session_better(struct session *s, struct session *than, int flags) +{ + int attached; + + if (than == NULL) + return (1); + if (flags & CMD_FIND_PREFER_UNATTACHED) { + attached = (~than->flags & SESSION_UNATTACHED); + if (attached && (s->flags & SESSION_UNATTACHED)) + return (1); + else if (!attached && (~s->flags & SESSION_UNATTACHED)) + return (0); + } + return (timercmp(&s->activity_time, &than->activity_time, >)); +} + +/* Find best session from a list, or all if list is NULL. */ +struct session * +cmd_find_best_session(struct session **slist, u_int ssize, int flags) +{ + struct session *s_loop, *s; + u_int i; + + s = NULL; + if (slist != NULL) { + for (i = 0; i < ssize; i++) { + if (cmd_find_session_better(slist[i], s, flags)) + s = slist[i]; + } + } else { + RB_FOREACH(s_loop, sessions, &sessions) { + if (cmd_find_session_better(s_loop, s, flags)) + s = s_loop; + } + } + return (s); +} + +/* Find best session and winlink for window. */ +int +cmd_find_best_session_with_window(struct cmd_find_state *fs) +{ + struct session **slist = NULL; + u_int ssize; + struct session *s; + + if (fs->cmdq != NULL && fs->cmdq->client != NULL) { + fs->s = cmd_find_try_TMUX(fs->cmdq->client, fs->w); + if (fs->s != NULL) + return (cmd_find_best_winlink_with_window(fs)); + } + + ssize = 0; + RB_FOREACH(s, sessions, &sessions) { + if (!session_has(s, fs->w)) + continue; + slist = xreallocarray(slist, ssize + 1, sizeof *slist); + slist[ssize++] = s; + } + if (ssize == 0) + goto fail; + fs->s = cmd_find_best_session(slist, ssize, fs->flags); + if (fs->s == NULL) + goto fail; + free(slist); + return (cmd_find_best_winlink_with_window(fs)); + +fail: + free(slist); + return (-1); +} + +/* + * Find the best winlink for a window (the current if it contains the pane, + * otherwise the first). + */ +int +cmd_find_best_winlink_with_window(struct cmd_find_state *fs) +{ + struct winlink *wl, *wl_loop; + + wl = NULL; + if (fs->s->curw->window == fs->w) + wl = fs->s->curw; + else { + RB_FOREACH(wl_loop, winlinks, &fs->s->windows) { + if (wl_loop->window == fs->w) { + wl = wl_loop; + break; + } + } + } + if (wl == NULL) + return (-1); + fs->wl = wl; + fs->idx = fs->wl->idx; + return (0); +} + +/* Find current session when we have an unattached client. */ +int +cmd_find_current_session_with_client(struct cmd_find_state *fs) +{ + struct window_pane *wp; + + /* + * If this is running in a pane, we can use that to limit the list of + * sessions to those containing that pane (we still use the current + * window in the best session). + */ + if (fs->cmdq != NULL && fs->cmdq->client->tty.path != NULL) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) + break; + } + } else + wp = NULL; + + /* Not running in a pane. We know nothing. Find the best session. */ + if (wp == NULL) + goto unknown_pane; + + /* Find the best session and winlink containing this pane. */ + fs->w = wp->window; + if (cmd_find_best_session_with_window(fs) != 0) { + if (wp != NULL) { + /* + * The window may have been destroyed but the pane + * still on all_window_panes due to something else + * holding a reference. + */ + goto unknown_pane; + } + return (-1); + } + + /* Use the current window and pane from this session. */ + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + + return (0); + +unknown_pane: + fs->s = NULL; + if (fs->cmdq != NULL) + fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); + if (fs->s == NULL) + fs->s = cmd_find_best_session(NULL, 0, fs->flags); + if (fs->s == NULL) + return (-1); + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + + return (0); +} + +/* + * Work out the best current state. If this function succeeds, the state is + * guaranteed to be completely filled in. + */ +int +cmd_find_current_session(struct cmd_find_state *fs) +{ + /* If we know the current client, use it. */ + if (fs->cmdq != NULL && fs->cmdq->client != NULL) { + log_debug("%s: have client %p%s", __func__, fs->cmdq->client, + fs->cmdq->client->session == NULL ? "" : " (with session)"); + if (fs->cmdq->client->session == NULL) + return (cmd_find_current_session_with_client(fs)); + fs->s = fs->cmdq->client->session; + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + return (0); + } + + /* We know nothing, find the best session and client. */ + fs->s = cmd_find_best_session(NULL, 0, fs->flags); + if (fs->s == NULL) + return (-1); + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + + return (0); +} + +/* Work out the best current client. */ +struct client * +cmd_find_current_client(struct cmd_q *cmdq) +{ + struct cmd_find_state current; + struct session *s; + struct client *c, **clist = NULL; + u_int csize; + + /* If the queue client has a session, use it. */ + if (cmdq->client != NULL && cmdq->client->session != NULL) { + log_debug("%s: using cmdq %p client %p", __func__, cmdq, + cmdq->client); + return (cmdq->client); + } + + /* Otherwise find the current session. */ + cmd_find_clear_state(¤t, cmdq, 0); + if (cmd_find_current_session(¤t) != 0) + return (NULL); + + /* If it is attached, find the best of it's clients. */ + s = current.s; + log_debug("%s: current session $%u %s", __func__, s->id, s->name); + if (~s->flags & SESSION_UNATTACHED) { + csize = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) + continue; + clist = xreallocarray(clist, csize + 1, sizeof *clist); + clist[csize++] = c; + } + if (csize != 0) { + c = cmd_find_best_client(clist, csize); + if (c != NULL) { + free(clist); + return (c); + } + } + free(clist); + } + + /* Otherwise pick best of all clients. */ + return (cmd_find_best_client(NULL, 0)); +} + +/* Maps string in table. */ +const char * +cmd_find_map_table(const char *table[][2], const char *s) +{ + u_int i; + + for (i = 0; table[i][0] != NULL; i++) { + if (strcmp(s, table[i][0]) == 0) + return (table[i][1]); + } + return (s); +} + +/* Find session from string. Fills in s. */ +int +cmd_find_get_session(struct cmd_find_state *fs, const char *session) +{ + struct session *s, *s_loop; + + log_debug("%s: %s", __func__, session); + + /* Check for session ids starting with $. */ + if (*session == '$') { + fs->s = session_find_by_id_str(session); + if (fs->s == NULL) + return (-1); + return (0); + } + + /* Look for exactly this session. */ + fs->s = session_find(session); + if (fs->s != NULL) + return (0); + + /* Stop now if exact only. */ + if (fs->flags & CMD_FIND_EXACT_SESSION) + return (-1); + + /* Otherwise look for prefix. */ + s = NULL; + RB_FOREACH(s_loop, sessions, &sessions) { + if (strncmp(session, s_loop->name, strlen(session)) == 0) { + if (s != NULL) + return (-1); + s = s_loop; + } + } + if (s != NULL) { + fs->s = s; + return (0); + } + + /* Then as a pattern. */ + s = NULL; + RB_FOREACH(s_loop, sessions, &sessions) { + if (fnmatch(session, s_loop->name, 0) == 0) { + if (s != NULL) + return (-1); + s = s_loop; + } + } + if (s != NULL) { + fs->s = s; + return (0); + } + + return (-1); +} + +/* Find window from string. Fills in s, wl, w. */ +int +cmd_find_get_window(struct cmd_find_state *fs, const char *window) +{ + log_debug("%s: %s", __func__, window); + + /* Check for window ids starting with @. */ + if (*window == '@') { + fs->w = window_find_by_id_str(window); + if (fs->w == NULL) + return (-1); + return (cmd_find_best_session_with_window(fs)); + } + + /* Not a window id, so use the current session. */ + fs->s = fs->current->s; + + /* We now only need to find the winlink in this session. */ + if (cmd_find_get_window_with_session(fs, window) == 0) + return (0); + + /* Otherwise try as a session itself. */ + if (cmd_find_get_session(fs, window) == 0) { + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + if (~fs->flags & CMD_FIND_WINDOW_INDEX) + fs->idx = fs->wl->idx; + return (0); + } + + return (-1); +} + +/* + * Find window from string, assuming it is in given session. Needs s, fills in + * wl and w. + */ +int +cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) +{ + struct winlink *wl; + const char *errstr; + int idx, n, exact; + struct session *s; + + log_debug("%s: %s", __func__, window); + exact = (fs->flags & CMD_FIND_EXACT_WINDOW); + + /* + * Start with the current window as the default. So if only an index is + * found, the window will be the current. + */ + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + + /* Check for window ids starting with @. */ + if (*window == '@') { + fs->w = window_find_by_id_str(window); + if (fs->w == NULL || !session_has(fs->s, fs->w)) + return (-1); + return (cmd_find_best_winlink_with_window(fs)); + } + + /* Try as an offset. */ + if (!exact && (window[0] == '+' || window[0] == '-')) { + if (window[1] != '\0') + n = strtonum(window + 1, 1, INT_MAX, NULL); + else + n = 1; + s = fs->s; + if (fs->flags & CMD_FIND_WINDOW_INDEX) { + if (window[0] == '+') { + if (INT_MAX - s->curw->idx < n) + return (-1); + fs->idx = s->curw->idx + n; + } else { + if (n < s->curw->idx) + return (-1); + fs->idx = s->curw->idx - n; + } + return (0); + } + if (window[0] == '+') + fs->wl = winlink_next_by_number(s->curw, s, n); + else + fs->wl = winlink_previous_by_number(s->curw, s, n); + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + } + + /* Try special characters. */ + if (!exact) { + if (strcmp(window, "!") == 0) { + fs->wl = TAILQ_FIRST(&fs->s->lastw); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "^") == 0) { + fs->wl = RB_MIN(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "$") == 0) { + fs->wl = RB_MAX(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + } + + /* First see if this is a valid window index in this session. */ + if (window[0] != '+' && window[0] != '-') { + idx = strtonum(window, 0, INT_MAX, &errstr); + if (errstr == NULL) { + if (fs->flags & CMD_FIND_WINDOW_INDEX) { + fs->idx = idx; + return (0); + } + fs->wl = winlink_find_by_index(&fs->s->windows, idx); + if (fs->wl != NULL) { + fs->w = fs->wl->window; + return (0); + } + } + } + + /* Look for exact matches, error if more than one. */ + fs->wl = NULL; + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (strcmp(window, wl->window->name) == 0) { + if (fs->wl != NULL) + return (-1); + fs->wl = wl; + } + } + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + /* Stop now if exact only. */ + if (exact) + return (-1); + + /* Try as the start of a window name, error if multiple. */ + fs->wl = NULL; + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (strncmp(window, wl->window->name, strlen(window)) == 0) { + if (fs->wl != NULL) + return (-1); + fs->wl = wl; + } + } + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + /* Now look for pattern matches, again error if multiple. */ + fs->wl = NULL; + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (fnmatch(window, wl->window->name, 0) == 0) { + if (fs->wl != NULL) + return (-1); + fs->wl = wl; + } + } + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + return (-1); +} + +/* Find window from given pane. Needs wp, fills in s and wl and w. */ +int +cmd_find_get_window_with_pane(struct cmd_find_state *fs) +{ + log_debug("%s", __func__); + + fs->w = fs->wp->window; + return (cmd_find_best_session_with_window(fs)); +} + +/* Find pane from string. Fills in s, wl, w, wp. */ +int +cmd_find_get_pane(struct cmd_find_state *fs, const char *pane) +{ + log_debug("%s: %s", __func__, pane); + + /* Check for pane ids starting with %. */ + if (*pane == '%') { + fs->wp = window_pane_find_by_id_str(pane); + if (fs->wp == NULL) + return (-1); + fs->w = fs->wp->window; + return (cmd_find_best_session_with_window(fs)); + } + + /* Not a pane id, so try the current session and window. */ + fs->s = fs->current->s; + fs->wl = fs->current->wl; + fs->idx = fs->current->idx; + fs->w = fs->current->w; + + /* We now only need to find the pane in this window. */ + if (cmd_find_get_pane_with_window(fs, pane) == 0) + return (0); + + /* Otherwise try as a window itself (this will also try as session). */ + if (cmd_find_get_window(fs, pane) == 0) { + fs->wp = fs->w->active; + return (0); + } + + return (-1); +} + +/* + * Find pane from string, assuming it is in given session. Needs s, fills in wl + * and w and wp. + */ +int +cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane) +{ + log_debug("%s: %s", __func__, pane); + + /* Check for pane ids starting with %. */ + if (*pane == '%') { + fs->wp = window_pane_find_by_id_str(pane); + if (fs->wp == NULL) + return (-1); + fs->w = fs->wp->window; + return (cmd_find_best_winlink_with_window(fs)); + } + + /* Otherwise use the current window. */ + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + + /* Now we just need to look up the pane. */ + return (cmd_find_get_pane_with_window(fs, pane)); +} + +/* + * Find pane from string, assuming it is in the given window. Needs w, fills in + * wp. + */ +int +cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) +{ + const char *errstr; + int idx; + struct window_pane *wp; + u_int n; + + log_debug("%s: %s", __func__, pane); + + /* Check for pane ids starting with %. */ + if (*pane == '%') { + fs->wp = window_pane_find_by_id_str(pane); + if (fs->wp == NULL || fs->wp->window != fs->w) + return (-1); + return (0); + } + + /* Try special characters. */ + if (strcmp(pane, "!") == 0) { + if (fs->w->last == NULL) + return (-1); + fs->wp = fs->w->last; + return (0); + } else if (strcmp(pane, "{up-of}") == 0) { + fs->wp = window_pane_find_up(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } else if (strcmp(pane, "{down-of}") == 0) { + fs->wp = window_pane_find_down(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } else if (strcmp(pane, "{left-of}") == 0) { + fs->wp = window_pane_find_left(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } else if (strcmp(pane, "{right-of}") == 0) { + fs->wp = window_pane_find_right(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } + + /* Try as an offset. */ + if (pane[0] == '+' || pane[0] == '-') { + if (pane[1] != '\0') + n = strtonum(pane + 1, 1, INT_MAX, NULL); + else + n = 1; + wp = fs->w->active; + if (pane[0] == '+') + fs->wp = window_pane_next_by_number(fs->w, wp, n); + else + fs->wp = window_pane_previous_by_number(fs->w, wp, n); + if (fs->wp != NULL) + return (0); + } + + /* Get pane by index. */ + idx = strtonum(pane, 0, INT_MAX, &errstr); + if (errstr == NULL) { + fs->wp = window_pane_at_index(fs->w, idx); + if (fs->wp != NULL) + return (0); + } + + /* Try as a description. */ + fs->wp = window_find_string(fs->w, pane); + if (fs->wp != NULL) + return (0); + + return (-1); +} + +/* Clear state. */ +void +cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) +{ + memset(fs, 0, sizeof *fs); + + fs->cmdq = cmdq; + fs->flags = flags; + + fs->idx = -1; +} + +/* Check if a state if valid. */ +int +cmd_find_valid_state(struct cmd_find_state *fs) +{ + struct winlink *wl; + + if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL) + return (0); + + if (!session_alive(fs->s)) + return (0); + + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (wl->window == fs->w && wl == fs->wl) + break; + } + if (wl == NULL) + return (0); + + if (fs->w != fs->wl->window) + return (0); + + if (!window_has_pane(fs->w, fs->wp)) + return (0); + return (window_pane_visible(fs->wp)); +} + +/* Copy a state. */ +void +cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src) +{ + dst->s = src->s; + dst->wl = src->wl; + dst->idx = src->idx; + dst->w = src->w; + dst->wp = src->wp; +} + +/* Log the result. */ +void +cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) +{ + if (fs->s != NULL) + log_debug("%s: s=$%u", prefix, fs->s->id); + else + log_debug("%s: s=none", prefix); + if (fs->wl != NULL) { + log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, + fs->wl->window == fs->w, fs->w->id, fs->w->name); + } else + log_debug("%s: wl=none", prefix); + if (fs->wp != NULL) + log_debug("%s: wp=%%%u", prefix, fs->wp->id); + else + log_debug("%s: wp=none", prefix); + if (fs->idx != -1) + log_debug("%s: idx=%d", prefix, fs->idx); + else + log_debug("%s: idx=none", prefix); +} + +/* Find state from a session. */ +int +cmd_find_from_session(struct cmd_find_state *fs, struct session *s) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->s = s; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + + cmd_find_log_state(__func__, fs); + return (0); +} + +/* Find state from a window. */ +int +cmd_find_from_window(struct cmd_find_state *fs, struct window *w) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->w = w; + if (cmd_find_best_session_with_window(fs) != 0) + return (-1); + if (cmd_find_best_winlink_with_window(fs) != 0) + return (-1); + + cmd_find_log_state(__func__, fs); + return (0); +} + +/* Find state from a pane. */ +int +cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp) +{ + if (cmd_find_from_window(fs, wp->window) != 0) + return (-1); + fs->wp = wp; + + cmd_find_log_state(__func__, fs); + return (0); +} + +/* + * Split target into pieces and resolve for the given type. Fills in the given + * state. Returns 0 on success or -1 on error. + */ +int +cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, + const char *target, enum cmd_find_type type, int flags) +{ + struct cmd_find_state current; + struct mouse_event *m; + char *colon, *period, *copy = NULL; + const char *session, *window, *pane; + + /* Log the arguments. */ + if (target == NULL) + log_debug("%s: target none, type %d", __func__, type); + else + log_debug("%s: target %s, type %d", __func__, target, type); + log_debug("%s: cmdq %p, flags %#x", __func__, cmdq, flags); + + /* Clear new state. */ + cmd_find_clear_state(fs, cmdq, flags); + + /* Find current state. */ + if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) + fs->current = &marked_pane; + else if (cmd_find_valid_state(&cmdq->current)) + fs->current = &cmdq->current; + else { + cmd_find_clear_state(¤t, cmdq, flags); + if (cmd_find_current_session(¤t) != 0) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no current session"); + goto error; + } + fs->current = ¤t; + } + + /* An empty or NULL target is the current. */ + if (target == NULL || *target == '\0') + goto current; + + /* Mouse target is a plain = or {mouse}. */ + if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { + m = &cmdq->item->mouse; + switch (type) { + case CMD_FIND_PANE: + fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); + if (fs->wp != NULL) + fs->w = fs->wl->window; + break; + case CMD_FIND_WINDOW: + case CMD_FIND_SESSION: + fs->wl = cmd_mouse_window(m, &fs->s); + if (fs->wl != NULL) { + fs->w = fs->wl->window; + fs->wp = fs->w->active; + } + break; + } + if (fs->wp == NULL) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no mouse target"); + goto error; + } + goto found; + } + + /* Marked target is a plain ~ or {marked}. */ + if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) { + if (!server_check_marked()) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no marked target"); + goto error; + } + cmd_find_copy_state(fs, &marked_pane); + goto found; + } + + /* Find separators if they exist. */ + copy = xstrdup(target); + colon = strchr(copy, ':'); + if (colon != NULL) + *colon++ = '\0'; + if (colon == NULL) + period = strchr(copy, '.'); + else + period = strchr(colon, '.'); + if (period != NULL) + *period++ = '\0'; + + /* Set session, window and pane parts. */ + session = window = pane = NULL; + if (colon != NULL && period != NULL) { + session = copy; + window = colon; + pane = period; + } else if (colon != NULL && period == NULL) { + session = copy; + window = colon; + } else if (colon == NULL && period != NULL) { + window = copy; + pane = period; + } else { + if (*copy == '$') + session = copy; + else if (*copy == '@') + window = copy; + else if (*copy == '%') + pane = copy; + else { + switch (type) { + case CMD_FIND_SESSION: + session = copy; + break; + case CMD_FIND_WINDOW: + window = copy; + break; + case CMD_FIND_PANE: + pane = copy; + break; + } + } + } + + /* Set exact match flags. */ + if (session != NULL && *session == '=') { + session++; + fs->flags |= CMD_FIND_EXACT_SESSION; + } + if (window != NULL && *window == '=') { + window++; + fs->flags |= CMD_FIND_EXACT_WINDOW; + } + + /* Empty is the same as NULL. */ + if (session != NULL && *session == '\0') + session = NULL; + if (window != NULL && *window == '\0') + window = NULL; + if (pane != NULL && *pane == '\0') + pane = NULL; + + /* Map though conversion table. */ + if (session != NULL) + session = cmd_find_map_table(cmd_find_session_table, session); + if (window != NULL) + window = cmd_find_map_table(cmd_find_window_table, window); + if (pane != NULL) + pane = cmd_find_map_table(cmd_find_pane_table, pane); + + log_debug("target %s (flags %#x): session=%s, window=%s, pane=%s", + target, flags, session == NULL ? "none" : session, + window == NULL ? "none" : window, pane == NULL ? "none" : pane); + + /* No pane is allowed if want an index. */ + if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't specify pane here"); + goto error; + } + + /* If the session isn't NULL, look it up. */ + if (session != NULL) { + /* This will fill in session. */ + if (cmd_find_get_session(fs, session) != 0) + goto no_session; + + /* If window and pane are NULL, use that session's current. */ + if (window == NULL && pane == NULL) { + fs->wl = fs->s->curw; + fs->idx = -1; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + goto found; + } + + /* If window is present but pane not, find window in session. */ + if (window != NULL && pane == NULL) { + /* This will fill in winlink and window. */ + if (cmd_find_get_window_with_session(fs, window) != 0) + goto no_window; + fs->wp = fs->wl->window->active; + goto found; + } + + /* If pane is present but window not, find pane. */ + if (window == NULL && pane != NULL) { + /* This will fill in winlink and window and pane. */ + if (cmd_find_get_pane_with_session(fs, pane) != 0) + goto no_pane; + goto found; + } + + /* + * If window and pane are present, find both in session. This + * will fill in winlink and window. + */ + if (cmd_find_get_window_with_session(fs, window) != 0) + goto no_window; + /* This will fill in pane. */ + if (cmd_find_get_pane_with_window(fs, pane) != 0) + goto no_pane; + goto found; + } + + /* No session. If window and pane, try them. */ + if (window != NULL && pane != NULL) { + /* This will fill in session, winlink and window. */ + if (cmd_find_get_window(fs, window) != 0) + goto no_window; + /* This will fill in pane. */ + if (cmd_find_get_pane_with_window(fs, pane) != 0) + goto no_pane; + goto found; + } + + /* If just window is present, try it. */ + if (window != NULL && pane == NULL) { + /* This will fill in session, winlink and window. */ + if (cmd_find_get_window(fs, window) != 0) + goto no_window; + fs->wp = fs->wl->window->active; + goto found; + } + + /* If just pane is present, try it. */ + if (window == NULL && pane != NULL) { + /* This will fill in session, winlink, window and pane. */ + if (cmd_find_get_pane(fs, pane) != 0) + goto no_pane; + goto found; + } + +current: + /* Use the current session. */ + cmd_find_copy_state(fs, fs->current); + if (flags & CMD_FIND_WINDOW_INDEX) + fs->idx = -1; + goto found; + +error: + fs->current = NULL; + log_debug(" error"); + + free(copy); + return (-1); + +found: + fs->current = NULL; + cmd_find_log_state(__func__, fs); + + free(copy); + return (0); + +no_session: + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't find session %s", session); + goto error; + +no_window: + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't find window %s", window); + goto error; + +no_pane: + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't find pane %s", pane); + goto error; +} + +/* Find the target client or report an error and return NULL. */ +struct client * +cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) +{ + struct client *c; + char *copy; + size_t size; + const char *path; + + /* A NULL argument means the current client. */ + if (target == NULL) { + c = cmd_find_current_client(cmdq); + if (c == NULL && !quiet) + cmdq_error(cmdq, "no current client"); + log_debug("%s: no target, return %p", __func__, c); + return (c); + } + copy = xstrdup(target); + + /* Trim a single trailing colon if any. */ + size = strlen(copy); + if (size != 0 && copy[size - 1] == ':') + copy[size - 1] = '\0'; + + /* Check path of each client. */ + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || c->tty.path == NULL) + continue; + path = c->tty.path; + + /* Try for exact match. */ + if (strcmp(copy, path) == 0) + break; + + /* Try without leading /dev. */ + if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) + continue; + if (strcmp(copy, path + (sizeof _PATH_DEV) - 1) == 0) + break; + } + + /* If no client found, report an error. */ + if (c == NULL && !quiet) + cmdq_error(cmdq, "can't find client %s", copy); + + free(copy); + log_debug("%s: target %s, return %p", __func__, target, c); + return (c); +} diff --git a/cmd-has-session.c b/cmd-has-session.c deleted file mode 100644 index c4286b86..00000000 --- a/cmd-has-session.c +++ /dev/null @@ -1,48 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * 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 - -#include "tmux.h" - -/* - * Cause client to report an error and exit with 1 if session doesn't exist. - */ - -enum cmd_retval cmd_has_session_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_has_session_entry = { - "has-session", "has", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - 0, - NULL, - NULL, - cmd_has_session_exec -}; - -enum cmd_retval -cmd_has_session_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - - if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL) - return (CMD_RETURN_ERROR); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-if-shell.c b/cmd-if-shell.c index d1cbd7f3..229289cd 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha @@ -36,21 +36,28 @@ void cmd_if_shell_done(struct cmd_q *); void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { - "if-shell", "if", - "bt:", 2, 3, - "[-b] " CMD_TARGET_PANE_USAGE " shell-command command [command]", - 0, - NULL, - NULL, - cmd_if_shell_exec + .name = "if-shell", + .alias = "if", + + .args = { "bFt:", 2, 3 }, + .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " + "[command]", + + .tflag = CMD_PANE_CANFAIL, + + .flags = 0, + .exec = cmd_if_shell_exec }; struct cmd_if_shell_data { - char *cmd_if; - char *cmd_else; - struct cmd_q *cmdq; - int bflag; - int started; + char *cmd_if; + char *cmd_else; + + struct cmd_q *cmdq; + struct mouse_event mouse; + + int bflag; + int references; }; enum cmd_retval @@ -58,47 +65,65 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_if_shell_data *cdata; - char *shellcmd; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window_pane *wp = NULL; + char *shellcmd, *cmd, *cause; + struct cmd_list *cmdlist; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; + const char *cwd; - if (args_has(args, 't')) - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - } + cwd = wp->cwd; - ft = format_create(); - if (s != NULL) - format_session(ft, s); - if (s != NULL && wl != NULL) - format_winlink(ft, s, wl); - if (wp != NULL) - format_window_pane(ft, wp); + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = NULL; + ft = format_create(cmdq, 0); + format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); + if (args_has(args, 'F')) { + cmd = NULL; + if (*shellcmd != '0' && *shellcmd != '\0') + cmd = args->argv[1]; + else if (args->argc == 3) + cmd = args->argv[2]; + free(shellcmd); + if (cmd == NULL) + return (CMD_RETURN_NORMAL); + if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { + if (cause != NULL) { + cmdq_error(cmdq, "%s", cause); + free(cause); + } + return (CMD_RETURN_ERROR); + } + cmdq_run(cmdq, cmdlist, &cmdq->item->mouse); + cmd_list_free(cmdlist); + return (CMD_RETURN_NORMAL); + } + cdata = xmalloc(sizeof *cdata); + cdata->cmd_if = xstrdup(args->argv[1]); if (args->argc == 3) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; + cdata->bflag = args_has(args, 'b'); - cdata->started = 0; cdata->cmdq = cmdq; + memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse); cmdq->references++; - job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata); + cdata->references = 1; + job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free, + cdata); free(shellcmd); if (cdata->bflag) @@ -114,7 +139,7 @@ cmd_if_shell_callback(struct job *job) struct cmd_list *cmdlist; char *cause, *cmd; - if (cmdq->dead) + if (cmdq->flags & CMD_Q_DEAD) return; if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) @@ -132,13 +157,12 @@ cmd_if_shell_callback(struct job *job) return; } - cdata->started = 1; - cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; - cmdq_run(cmdq1, cmdlist); + cdata->references++; + cmdq_run(cmdq1, cmdlist, &cdata->mouse); cmd_list_free(cmdlist); } @@ -148,11 +172,16 @@ cmd_if_shell_done(struct cmd_q *cmdq1) struct cmd_if_shell_data *cdata = cmdq1->data; struct cmd_q *cmdq = cdata->cmdq; + if (cmdq1->client_exit >= 0) + cmdq->client_exit = cmdq1->client_exit; + cmdq_free(cmdq1); + + if (--cdata->references != 0) + return; + if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); - cmdq_free(cmdq1); - free(cdata->cmd_else); free(cdata->cmd_if); free(cdata); @@ -164,7 +193,7 @@ cmd_if_shell_free(void *data) struct cmd_if_shell_data *cdata = data; struct cmd_q *cmdq = cdata->cmdq; - if (cdata->started) + if (--cdata->references != 0) return; if (!cmdq_free(cmdq) && !cdata->bflag) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 971bc0b9..50f5f202 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2011 George Nachman @@ -28,44 +28,37 @@ * Join or move a pane into another (like split/swap/kill). */ -void cmd_join_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmd_q *); enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { - "join-pane", "joinp", - "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", - 0, - cmd_join_pane_key_binding, - NULL, - cmd_join_pane_exec + .name = "join-pane", + .alias = "joinp", + + .args = { "bdhvp:l:s:t:", 0, 0 }, + .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + + .sflag = CMD_PANE_MARKED, + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_join_pane_exec }; const struct cmd_entry cmd_move_pane_entry = { - "move-pane", "movep", - "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", - 0, - NULL, - NULL, - cmd_join_pane_exec -}; + .name = "move-pane", + .alias = "movep", -void -cmd_join_pane_key_binding(struct cmd *self, int key) -{ - switch (key) { - case '%': - self->args = args_create(0); - args_set(self->args, 'h', NULL); - break; - default: - self->args = args_create(0); - break; - } -} + .args = { "bdhvp:l:s:t:", 0, 0 }, + .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + + .sflag = CMD_PANE, + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_join_pane_exec +}; enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmd_q *cmdq) @@ -90,16 +83,15 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) enum layout_type type; struct layout_cell *lc; - dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp); - if (dst_wl == NULL) - return (CMD_RETURN_ERROR); + dst_s = cmdq->state.tflag.s; + dst_wl = cmdq->state.tflag.wl; + dst_wp = cmdq->state.tflag.wp; dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); + src_wl = cmdq->state.sflag.wl; + src_wp = cmdq->state.sflag.wp; src_w = src_wl->window; server_unzoom_window(src_w); @@ -144,11 +136,7 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) layout_close_pane(src_wp); - if (src_w->active == src_wp) { - src_w->active = TAILQ_PREV(src_wp, window_panes, entry); - if (src_w->active == NULL) - src_w->active = TAILQ_NEXT(src_wp, entry); - } + window_lost_pane(src_w, src_wp); TAILQ_REMOVE(&src_w->panes, src_wp, entry); if (window_count_panes(src_w) == 0) diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 40761350..f843bc58 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -29,24 +29,24 @@ enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_pane_entry = { - "kill-pane", "killp", - "at:", 0, 0, - "[-a] " CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_kill_pane_exec + .name = "kill-pane", + .alias = "killp", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_kill_pane_exec }; enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct window_pane *loopwp, *tmpwp, *wp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *loopwp, *tmpwp, *wp = cmdq->state.tflag.wp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); server_unzoom_window(wl->window); if (window_count_panes(wl->window) == 1) { diff --git a/cmd-kill-server.c b/cmd-kill-server.c index a6065460..6f84e959 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -30,19 +30,32 @@ enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_server_entry = { - "kill-server", NULL, - "", 0, 0, - "", - 0, - NULL, - NULL, - cmd_kill_server_exec + .name = "kill-server", + .alias = NULL, + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_kill_server_exec +}; + +const struct cmd_entry cmd_start_server_entry = { + .name = "start-server", + .alias = "start", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = CMD_STARTSERVER, + .exec = cmd_kill_server_exec }; enum cmd_retval -cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) +cmd_kill_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { - kill(getpid(), SIGTERM); + if (self->entry == &cmd_kill_server_entry) + kill(getpid(), SIGTERM); return (CMD_RETURN_NORMAL); } diff --git a/cmd-kill-session.c b/cmd-kill-session.c index a12cc8a4..4ca4e2c8 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -30,29 +30,38 @@ enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { - "kill-session", NULL, - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, - 0, - NULL, - NULL, - cmd_kill_session_exec + .name = "kill-session", + .alias = NULL, + + .args = { "aCt:", 0, 0 }, + .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_kill_session_exec }; enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s, *s2, *s3; + struct session *s, *sloop, *stmp; + struct winlink *wl; - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); + s = cmdq->state.tflag.s; - if (args_has(args, 'a')) { - RB_FOREACH_SAFE(s2, sessions, &sessions, s3) { - if (s != s2) { - server_destroy_session(s2); - session_destroy(s2); + if (args_has(args, 'C')) { + RB_FOREACH(wl, winlinks, &s->windows) { + wl->window->flags &= ~WINDOW_ALERTFLAGS; + wl->flags &= ~WINLINK_ALERTFLAGS; + } + server_redraw_session(s); + } else if (args_has(args, 'a')) { + RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { + if (sloop != s) { + server_destroy_session(sloop); + session_destroy(sloop); } } } else { diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 34b97499..7a8e9fa6 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,32 +27,54 @@ enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_window_entry = { - "kill-window", "killw", - "at:", 0, 0, - "[-a] " CMD_TARGET_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_kill_window_exec + .name = "kill-window", + .alias = "killw", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_kill_window_exec +}; + +const struct cmd_entry cmd_unlink_window_entry = { + .name = "unlink-window", + .alias = "unlinkw", + + .args = { "kt:", 0, 0 }, + .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_kill_window_exec }; enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl, *wl2, *wl3; - struct session *s; + struct args *args = self->args; + struct winlink *wl = cmdq->state.tflag.wl, *wl2, *wl3; + struct window *w = wl->window; + struct session *s = cmdq->state.tflag.s; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - - if (args_has(args, 'a')) { - RB_FOREACH_SAFE(wl2, winlinks, &s->windows, wl3) { - if (wl != wl2) - server_kill_window(wl2->window); + if (self->entry == &cmd_unlink_window_entry) { + if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { + cmdq_error(cmdq, "window only linked to one session"); + return (CMD_RETURN_ERROR); } - } else - server_kill_window(wl->window); + server_unlink_window(s, wl); + } else { + if (args_has(args, 'a')) { + RB_FOREACH_SAFE(wl2, winlinks, &s->windows, wl3) { + if (wl != wl2) + server_kill_window(wl2->window); + } + } else + server_kill_window(wl->window); + } recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-link-window.c b/cmd-link-window.c deleted file mode 100644 index 761dad18..00000000 --- a/cmd-link-window.c +++ /dev/null @@ -1,70 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * 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 - -#include - -#include "tmux.h" - -/* - * Link a window into another session. - */ - -enum cmd_retval cmd_link_window_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_link_window_entry = { - "link-window", "linkw", - "dks:t:", 0, 0, - "[-dk] " CMD_SRCDST_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_link_window_exec -}; - -enum cmd_retval -cmd_link_window_exec(struct cmd *self, struct cmd_q *cmdq) -{ -#ifdef TMATE - cmdq_error(cmdq, "link window is not supported with tmate"); - return (CMD_RETURN_ERROR); -#else - struct args *args = self->args; - struct session *src, *dst; - struct winlink *wl; - char *cause; - int idx, kflag, dflag; - - if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) - return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) - return (CMD_RETURN_ERROR); - - kflag = args_has(self->args, 'k'); - dflag = args_has(self->args, 'd'); - if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { - cmdq_error(cmdq, "can't link window: %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - recalculate_sizes(); - - return (CMD_RETURN_NORMAL); -#endif -} diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 58af0020..a6007c33 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,36 +27,38 @@ * List paste buffers. */ +#define LIST_BUFFERS_TEMPLATE \ + "#{buffer_name}: #{buffer_size} bytes: \"#{buffer_sample}\"" + enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_buffers_entry = { - "list-buffers", "lsb", - "F:", 0, 0, - "[-F format]", - 0, - NULL, - NULL, - cmd_list_buffers_exec + .name = "list-buffers", + .alias = "lsb", + + .args = { "F:", 0, 0 }, + .usage = "[-F format]", + + .flags = 0, + .exec = cmd_list_buffers_exec }; enum cmd_retval -cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) +cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; - u_int idx; char *line; const char *template; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; - idx = 0; - while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { - ft = format_create(); - format_add(ft, "line", "%u", idx - 1); - format_paste_buffer(ft, pb); + pb = NULL; + while ((pb = paste_walk(pb)) != NULL) { + ft = format_create(cmdq, 0); + format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 59f63099..75c6f570 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -28,16 +28,24 @@ * List all clients. */ +#define LIST_CLIENTS_TEMPLATE \ + "#{client_tty}: #{session_name} " \ + "[#{client_width}x#{client_height} #{client_termname}]" \ + "#{?client_utf8, (utf8),} #{?client_readonly, (ro),}" + enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_clients_entry = { - "list-clients", "lsc", - "F:t:", 0, 0, - "[-F format] " CMD_TARGET_SESSION_USAGE, - CMD_READONLY, - NULL, - NULL, - cmd_list_clients_exec + .name = "list-clients", + .alias = "lsc", + + .args = { "F:t:", 0, 0 }, + .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = CMD_READONLY, + .exec = cmd_list_clients_exec }; enum cmd_retval @@ -48,37 +56,33 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) struct session *s; struct format_tree *ft; const char *template; - u_int i; + u_int idx; char *line; - if (args_has(args, 't')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - } else + if (args_has(args, 't')) + s = cmdq->state.tflag.s; + else s = NULL; if ((template = args_get(args, 'F')) == NULL) template = LIST_CLIENTS_TEMPLATE; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) + idx = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || (s != NULL && s != c->session)) continue; - if (s != NULL && s != c->session) - continue; - - ft = format_create(); - format_add(ft, "line", "%u", i); - format_session(ft, c->session); - format_client(ft, c); + ft = format_create(cmdq, 0); + format_add(ft, "line", "%u", idx); + format_defaults(ft, c, NULL, NULL, NULL); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); + + idx++; } return (CMD_RETURN_NORMAL); diff --git a/cmd-list-commands.c b/cmd-list-commands.c deleted file mode 100644 index 1bf6e703..00000000 --- a/cmd-list-commands.c +++ /dev/null @@ -1,55 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * 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 - -#include "tmux.h" - -/* - * List all commands with usages. - */ - -enum cmd_retval cmd_list_commands_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_list_commands_entry = { - "list-commands", "lscm", - "", 0, 0, - "", - 0, - NULL, - NULL, - cmd_list_commands_exec -}; - -enum cmd_retval -cmd_list_commands_exec(unused struct cmd *self, struct cmd_q *cmdq) -{ - const struct cmd_entry **entryp; - - for (entryp = cmd_table; *entryp != NULL; entryp++) { - if ((*entryp)->alias != NULL) { - cmdq_print(cmdq, "%s (%s) %s", (*entryp)->name, - (*entryp)->alias, (*entryp)->usage); - } else { - cmdq_print(cmdq, "%s %s", (*entryp)->name, - (*entryp)->usage); - } - } - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-list-keys.c b/cmd-list-keys.c index f0bcece3..c5e58826 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,6 +18,7 @@ #include +#include #include #include "tmux.h" @@ -27,27 +28,44 @@ */ enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmd_q *); + enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_list_keys_commands(struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { - "list-keys", "lsk", - "t:", 0, 0, - "[-t key-table]", - 0, - NULL, - NULL, - cmd_list_keys_exec + .name = "list-keys", + .alias = "lsk", + + .args = { "t:T:", 0, 0 }, + .usage = "[-t mode-table] [-T key-table]", + + .flags = CMD_STARTSERVER, + .exec = cmd_list_keys_exec +}; + +const struct cmd_entry cmd_list_commands_entry = { + .name = "list-commands", + .alias = "lscm", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = CMD_STARTSERVER, + .exec = cmd_list_keys_exec }; enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct key_table *table; struct key_binding *bd; - const char *key; - char tmp[BUFSIZ], flags[8]; - size_t used; - int width, keywidth; + const char *key, *tablename, *r; + char *cp, tmp[BUFSIZ]; + int repeat, width, tablewidth, keywidth; + + if (self->entry == &cmd_list_commands_entry) + return (cmd_list_keys_commands(cmdq)); #ifdef TMATE /* XXX TODO Really nasty hack, we really need our own client instance... */ @@ -62,46 +80,62 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); - width = 0; - - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) - continue; - - keywidth = strlen(key); - if (!(bd->key & KEYC_PREFIX)) { - if (bd->can_repeat) - keywidth += 4; - else - keywidth += 3; - } else if (bd->can_repeat) - keywidth += 3; - if (keywidth > width) - width = keywidth; + tablename = args_get(args, 'T'); + if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); } - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) + repeat = 0; + tablewidth = keywidth = 0; + RB_FOREACH(table, key_tables, &key_tables) { + if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + key = key_string_lookup_key(bd->key); - *flags = '\0'; - if (!(bd->key & KEYC_PREFIX)) { if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-rn "); - else - xsnprintf(flags, sizeof flags, "-n "); - } else if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-r "); + repeat = 1; - used = xsnprintf(tmp, sizeof tmp, "%s%*s ", - flags, (int) (width - strlen(flags)), key); - if (used >= sizeof tmp) + width = utf8_cstrwidth(table->name); + if (width > tablewidth) + tablewidth = width; + width = utf8_cstrwidth(key); + if (width > keywidth) + keywidth = width; + } + } + + RB_FOREACH(table, key_tables, &key_tables) { + if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + key = key_string_lookup_key(bd->key); - cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); - cmdq_print(cmdq, "bind-key %s", tmp); + if (!repeat) + r = ""; + else if (bd->can_repeat) + r = "-r "; + else + r = " "; + xsnprintf(tmp, sizeof tmp, "%s-T ", r); + + cp = utf8_padcstr(table->name, tablewidth); + strlcat(tmp, cp, sizeof tmp); + strlcat(tmp, " ", sizeof tmp); + free(cp); + + cp = utf8_padcstr(key, keywidth); + strlcat(tmp, cp, sizeof tmp); + strlcat(tmp, " ", sizeof tmp); + free(cp); + + cp = cmd_list_print(bd->cmdlist); + strlcat(tmp, cp, sizeof tmp); + free(cp); + + cmdq_print(cmdq, "bind-key %s", tmp); + } } return (CMD_RETURN_NORMAL); @@ -127,8 +161,6 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) any_mode = 0; RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); - if (key == NULL) - continue; if (mbind->mode != 0) any_mode = 1; @@ -140,8 +172,6 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); - if (key == NULL) - continue; mode = ""; if (mbind->mode != 0) @@ -159,3 +189,22 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } + +enum cmd_retval +cmd_list_keys_commands(struct cmd_q *cmdq) +{ + const struct cmd_entry **entryp; + const struct cmd_entry *entry; + + for (entryp = cmd_table; *entryp != NULL; entryp++) { + entry = *entryp; + if (entry->alias == NULL) { + cmdq_print(cmdq, "%s %s", entry->name, entry->usage); + continue; + } + cmdq_print(cmdq, "%s (%s) %s", entry->name, entry->alias, + entry->usage); + } + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 0d498e28..da0e0962 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -19,7 +19,6 @@ #include #include -#include #include "tmux.h" @@ -30,41 +29,37 @@ enum cmd_retval cmd_list_panes_exec(struct cmd *, struct cmd_q *); void cmd_list_panes_server(struct cmd *, struct cmd_q *); -void cmd_list_panes_session( - struct cmd *, struct session *, struct cmd_q *, int); -void cmd_list_panes_window(struct cmd *, - struct session *, struct winlink *, struct cmd_q *, int); +void cmd_list_panes_session(struct cmd *, struct session *, struct cmd_q *, + int); +void cmd_list_panes_window(struct cmd *, struct session *, struct winlink *, + struct cmd_q *, int); const struct cmd_entry cmd_list_panes_entry = { - "list-panes", "lsp", - "asF:t:", 0, 0, - "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_list_panes_exec + .name = "list-panes", + .alias = "lsp", + + .args = { "asF:t:", 0, 0 }, + .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_list_panes_exec }; enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; if (args_has(args, 'a')) cmd_list_panes_server(self, cmdq); - else if (args_has(args, 's')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + else if (args_has(args, 's')) cmd_list_panes_session(self, s, cmdq, 1); - } else { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); + else cmd_list_panes_window(self, s, wl, cmdq, 0); - } return (CMD_RETURN_NORMAL); } @@ -79,8 +74,8 @@ cmd_list_panes_server(struct cmd *self, struct cmd_q *cmdq) } void -cmd_list_panes_session( - struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) +cmd_list_panes_session(struct cmd *self, struct session *s, struct cmd_q *cmdq, + int type) { struct winlink *wl; @@ -89,8 +84,8 @@ cmd_list_panes_session( } void -cmd_list_panes_window(struct cmd *self, - struct session *s, struct winlink *wl, struct cmd_q *cmdq, int type) +cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, + struct cmd_q *cmdq, int type) { struct args *args = self->args; struct window_pane *wp; @@ -117,9 +112,9 @@ cmd_list_panes_window(struct cmd *self, "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; case 2: - template = "#{session_name}:#{window_index}.#{pane_index}: " - "[#{pane_width}x#{pane_height}] [history " - "#{history_size}/#{history_limit}, " + template = "#{session_name}:#{window_index}." + "#{pane_index}: [#{pane_width}x#{pane_height}] " + "[history #{history_size}/#{history_limit}, " "#{history_bytes} bytes] #{pane_id}" "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; @@ -128,11 +123,9 @@ cmd_list_panes_window(struct cmd *self, n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wp); + format_defaults(ft, NULL, s, wl, wp); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 61c12f4e..1fde7f86 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -28,16 +28,25 @@ * List all sessions. */ +#define LIST_SESSIONS_TEMPLATE \ + "#{session_name}: #{session_windows} windows " \ + "(created #{t:session_created}) " \ + "[#{session_width}x#{session_height}]" \ + "#{?session_grouped, (group ,}" \ + "#{session_group}#{?session_grouped,),}" \ + "#{?session_attached, (attached),}" + enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_sessions_entry = { - "list-sessions", "ls", - "F:", 0, 0, - "[-F format]", - 0, - NULL, - NULL, - cmd_list_sessions_exec + .name = "list-sessions", + .alias = "ls", + + .args = { "F:", 0, 0 }, + .usage = "[-F format]", + + .flags = 0, + .exec = cmd_list_sessions_exec }; enum cmd_retval @@ -55,9 +64,9 @@ cmd_list_sessions_exec(struct cmd *self, struct cmd_q *cmdq) n = 0; RB_FOREACH(s, sessions, &sessions) { - ft = format_create(); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); - format_session(ft, s); + format_defaults(ft, NULL, s, NULL, NULL); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 5c2a2b95..dd05ea85 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,36 +27,46 @@ * List windows on given session. */ +#define LIST_WINDOWS_TEMPLATE \ + "#{window_index}: #{window_name}#{window_flags} " \ + "(#{window_panes} panes) " \ + "[#{window_width}x#{window_height}] " \ + "[layout #{window_layout}] #{window_id}" \ + "#{?window_active, (active),}"; +#define LIST_WINDOWS_WITH_SESSION_TEMPLATE \ + "#{session_name}:" \ + "#{window_index}: #{window_name}#{window_flags} " \ + "(#{window_panes} panes) " \ + "[#{window_width}x#{window_height}] " + enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmd_q *); void cmd_list_windows_server(struct cmd *, struct cmd_q *); -void cmd_list_windows_session( - struct cmd *, struct session *, struct cmd_q *, int); +void cmd_list_windows_session(struct cmd *, struct session *, + struct cmd_q *, int); const struct cmd_entry cmd_list_windows_entry = { - "list-windows", "lsw", - "F:at:", 0, 0, - "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, - 0, - NULL, - NULL, - cmd_list_windows_exec + .name = "list-windows", + .alias = "lsw", + + .args = { "F:at:", 0, 0 }, + .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_list_windows_exec }; enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; if (args_has(args, 'a')) cmd_list_windows_server(self, cmdq); - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - cmd_list_windows_session(self, s, cmdq, 0); - } + else + cmd_list_windows_session(self, cmdq->state.tflag.s, cmdq, 0); return (CMD_RETURN_NORMAL); } @@ -71,8 +81,8 @@ cmd_list_windows_server(struct cmd *self, struct cmd_q *cmdq) } void -cmd_list_windows_session( - struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) +cmd_list_windows_session(struct cmd *self, struct session *s, + struct cmd_q *cmdq, int type) { struct args *args = self->args; struct winlink *wl; @@ -95,11 +105,9 @@ cmd_list_windows_session( n = 0; RB_FOREACH(wl, winlinks, &s->windows) { - ft = format_create(); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wl->window->active); + format_defaults(ft, NULL, s, wl, NULL); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); diff --git a/cmd-list.c b/cmd-list.c index 08e2067c..59fc7796 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -24,7 +24,7 @@ #include "tmux.h" struct cmd_list * -cmd_list_parse(int argc, char **argv, const char* file, u_int line, +cmd_list_parse(int argc, char **argv, const char *file, u_int line, char **cause) { struct cmd_list *cmdlist; @@ -99,21 +99,28 @@ cmd_list_free(struct cmd_list *cmdlist) free(cmdlist); } -size_t -cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) +char * +cmd_list_print(struct cmd_list *cmdlist) { struct cmd *cmd; - size_t off; + char *buf, *this; + size_t len; + + len = 1; + buf = xcalloc(1, len); - off = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if (off >= len) - break; - off += cmd_print(cmd, buf + off, len - off); - if (off >= len) - break; + this = cmd_print(cmd); + + len += strlen(this) + 3; + buf = xrealloc(buf, len); + + strlcat(buf, this, len); if (TAILQ_NEXT(cmd, qentry) != NULL) - off += xsnprintf(buf + off, len - off, " ; "); + strlcat(buf, " ; ", len); + + free(this); } - return (off); + + return (buf); } diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 3be14d6a..6fd2a767 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -34,13 +35,14 @@ enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmd_q *); void cmd_load_buffer_callback(struct client *, int, void *); const struct cmd_entry cmd_load_buffer_entry = { - "load-buffer", "loadb", - "b:", 1, 1, - CMD_BUFFER_USAGE " path", - 0, - NULL, - NULL, - cmd_load_buffer_exec + .name = "load-buffer", + .alias = "loadb", + + .args = { "b:", 1, 1 }, + .usage = CMD_BUFFER_USAGE " path", + + .flags = 0, + .exec = cmd_load_buffer_exec }; enum cmd_retval @@ -50,30 +52,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path, *newpath, *wd; - char *pdata, *new_pdata, *cause; + const char *path, *bufname, *cwd; + char *pdata, *new_pdata, *cause, *file, resolved[PATH_MAX]; size_t psize; - u_int limit; - int ch, error, buffer, *buffer_ptr; + int ch, error; - if (!args_has(args, 'b')) - buffer = -1; - else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); path = args->argv[0]; if (strcmp(path, "-") == 0) { - buffer_ptr = xmalloc(sizeof *buffer_ptr); - *buffer_ptr = buffer; - - error = server_set_stdin_callback (c, cmd_load_buffer_callback, - buffer_ptr, &cause); + error = server_set_stdin_callback(c, cmd_load_buffer_callback, + (void *)bufname, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); @@ -82,21 +73,26 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_WAIT); } - if (c != NULL) - wd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) { - wd = options_get_string(&s->options, "default-path"); - if (*wd == '\0') - wd = s->cwd; - } else - wd = NULL; - if (wd != NULL && *wd != '\0') { - newpath = get_full_path(wd, path); - if (newpath != NULL) - path = newpath; + if (c != NULL && c->session == NULL) + cwd = c->cwd; + else if ((s = c->session) != NULL) + cwd = s->cwd; + else + cwd = "."; + + if (*path == '/') + file = xstrdup(path); + else + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL && + strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { + cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); + return (CMD_RETURN_ERROR); } - if ((f = fopen(path, "rb")) == NULL) { - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + f = fopen(resolved, "rb"); + free(file); + if (f == NULL) { + cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); return (CMD_RETURN_ERROR); } @@ -112,7 +108,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) pdata[psize++] = ch; } if (ferror(f)) { - cmdq_error(cmdq, "%s: read error", path); + cmdq_error(cmdq, "%s: read error", resolved); goto error; } if (pdata != NULL) @@ -120,14 +116,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) fclose(f); - limit = options_get_number(&global_options, "buffer-limit"); - if (buffer == -1) { - paste_add(&global_buffers, pdata, psize, limit); - return (CMD_RETURN_NORMAL); - } - if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); + if (paste_set(pdata, psize, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); free(pdata); + free(cause); return (CMD_RETURN_ERROR); } @@ -143,39 +135,39 @@ error: void cmd_load_buffer_callback(struct client *c, int closed, void *data) { - int *buffer = data; - char *pdata; - size_t psize; - u_int limit; + const char *bufname = data; + char *pdata, *cause, *saved; + size_t psize; if (!closed) return; c->stdin_callback = NULL; - c->references--; + server_client_unref(c); if (c->flags & CLIENT_DEAD) return; psize = EVBUFFER_LENGTH(c->stdin_data); - if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { - free(data); + if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) goto out; - } + memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); pdata[psize] = '\0'; evbuffer_drain(c->stdin_data, psize); - limit = options_get_number(&global_options, "buffer-limit"); - if (*buffer == -1) - paste_add(&global_buffers, pdata, psize, limit); - else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) { + if (paste_set(pdata, psize, bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ - evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); - server_push_stderr(c); + if (~c->flags & CLIENT_UTF8) { + saved = cause; + cause = utf8_sanitize(saved); + free(saved); + } + evbuffer_add_printf(c->stderr_data, "%s", cause); + server_client_push_stderr(c); + free(pdata); + free(cause); } - free(data); - out: cmdq_continue(c->cmdq); } diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 0b6aafe8..9cdd816f 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -18,10 +18,6 @@ #include -#include -#include -#include - #include "tmux.h" /* @@ -31,55 +27,52 @@ enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_lock_server_entry = { - "lock-server", "lock", - "", 0, 0, - "", - 0, - NULL, - NULL, - cmd_lock_server_exec + .name = "lock-server", + .alias = "lock", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_session_entry = { - "lock-session", "locks", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - 0, - NULL, - NULL, - cmd_lock_server_exec + .name = "lock-session", + .alias = "locks", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_client_entry = { - "lock-client", "lockc", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - 0, - NULL, - NULL, - cmd_lock_server_exec + .name = "lock-client", + .alias = "lockc", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .tflag = CMD_CLIENT, + + .flags = 0, + .exec = cmd_lock_server_exec }; enum cmd_retval -cmd_lock_server_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_lock_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - struct session *s; - if (self->entry == &cmd_lock_server_entry) server_lock(); - else if (self->entry == &cmd_lock_session_entry) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - server_lock_session(s); - } else { - c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - server_lock_client(c); - } + else if (self->entry == &cmd_lock_session_entry) + server_lock_session(cmdq->state.tflag.s); + else + server_lock_client(cmdq->state.c); + recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-move-window.c b/cmd-move-window.c index 3c675b03..000d8362 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -29,13 +29,31 @@ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { - "move-window", "movew", - "dkrs:t:", 0, 0, - "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_move_window_exec + .name = "move-window", + .alias = "movew", + + .args = { "adkrs:t:", 0, 0 }, + .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, + + .sflag = CMD_WINDOW, + .tflag = CMD_MOVEW_R, + + .flags = 0, + .exec = cmd_move_window_exec +}; + +const struct cmd_entry cmd_link_window_entry = { + .name = "link-window", + .alias = "linkw", + + .args = { "adks:t:", 0, 0 }, + .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, + + .sflag = CMD_WINDOW, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, + .exec = cmd_move_window_exec }; enum cmd_retval @@ -46,34 +64,48 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); #else struct args *args = self->args; - struct session *src, *dst, *s; - struct winlink *wl; + struct session *src = cmdq->state.sflag.s; + struct session *dst = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.sflag.wl; char *cause; - int idx, kflag, dflag; + int idx = cmdq->state.tflag.idx, kflag, dflag, sflag; + + kflag = args_has(self->args, 'k'); + dflag = args_has(self->args, 'd'); if (args_has(args, 'r')) { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - session_renumber_windows(s); + session_renumber_windows(dst); recalculate_sizes(); return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) - return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) - return (CMD_RETURN_ERROR); - kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); - if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { - cmdq_error(cmdq, "can't move window: %s", cause); + sflag = args_has(self->args, 's'); + + if (args_has(self->args, 'a')) { + if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) + return (CMD_RETURN_ERROR); + } + + if (server_link_window(src, wl, dst, idx, kflag, !dflag, + &cause) != 0) { + cmdq_error(cmdq, "can't link window: %s", cause); free(cause); return (CMD_RETURN_ERROR); } - server_unlink_window(src, wl); + if (self->entry == &cmd_move_window_entry) + server_unlink_window(src, wl); + + /* + * Renumber the winlinks in the src session only, the destination + * session already has the correct winlink id to us, either + * automatically or specified by -s. + */ + if (!sflag && options_get_number(src->options, "renumber-windows")) + session_renumber_windows(src); + recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 4eebe632..f96003c7 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,7 +18,8 @@ #include -#include +#include +#include #include #include #include @@ -30,45 +31,68 @@ * Create a new session and attach to the current terminal unless -d is given. */ -enum cmd_retval cmd_new_session_check(struct args *); +#define NEW_SESSION_TEMPLATE "#{session_name}:" + enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { - "new-session", "new", - "AdDF:n:Ps:t:x:y:", 0, 1, - "[-AdDP] [-F format] [-n window-name] [-s session-name] " - CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", - CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, - NULL, - cmd_new_session_check, - cmd_new_session_exec + .name = "new-session", + .alias = "new", + + .args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 }, + .usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " + "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " + "[-y height] [command]", + + .tflag = CMD_SESSION_CANFAIL, + + .flags = CMD_STARTSERVER, + .exec = cmd_new_session_exec }; -enum cmd_retval -cmd_new_session_check(struct args *args) -{ - if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) - return (CMD_RETURN_ERROR); - return (CMD_RETURN_NORMAL); -} +const struct cmd_entry cmd_has_session_entry = { + .name = "has-session", + .alias = "has", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_new_session_exec +}; enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->client; - struct session *s, *groupwith; + struct session *s, *attach_sess; + struct session *groupwith = cmdq->state.tflag.s; struct window *w; - struct environ env; + struct environ *env; struct termios tio, *tiop; - struct passwd *pw; - const char *newname, *target, *update, *cwd, *errstr; - const char *template; - char *cmd, *cause, *cp; - int detached, idx; + const char *newname, *target, *update, *errstr, *template; + const char *path, *cwd, *to_free; + char **argv, *cmd, *cause, *cp; + int detached, already_attached, idx, argc; u_int sx, sy; - int already_attached; struct format_tree *ft; + struct environ_entry *envent; + + if (self->entry == &cmd_has_session_entry) { + /* + * cmd_prepare() will fail if the session cannot be found, + * hence always return success here. + */ + return (CMD_RETURN_NORMAL); + } + + if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { + cmdq_error(cmdq, "command or window name given with target"); + return (CMD_RETURN_ERROR); + } newname = args_get(args, 's'); if (newname != NULL) { @@ -76,22 +100,25 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } - if (session_find(newname) != NULL) { + if ((attach_sess = session_find(newname)) != NULL) { if (args_has(args, 'A')) { - return (cmd_attach_session(cmdq, newname, - args_has(args, 'D'), 0)); + /* + * This cmdq is now destined for + * attach-session. Because attach-session + * will have already been prepared, copy this + * session into its tflag so it can be used. + */ + cmdq->state.tflag.s = attach_sess; + return (cmd_attach_session(cmdq, + args_has(args, 'D'), 0, NULL, + args_has(args, 'E'))); } cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); } } - target = args_get(args, 't'); - if (target != NULL) { - groupwith = cmd_find_session(cmdq, target, 0); - if (groupwith == NULL) - return (CMD_RETURN_ERROR); - } else + if ((target = args_get(args, 't')) == NULL) groupwith = NULL; /* Set -d if no client. */ @@ -104,16 +131,33 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session != NULL) already_attached = 1; + /* Get the new session working directory. */ + to_free = NULL; + if (args_has(args, 'c')) { + ft = format_create(cmdq, 0); + format_defaults(ft, c, NULL, NULL, NULL); + to_free = cwd = format_expand(ft, args_get(args, 'c')); + format_free(ft); + } else if (c != NULL && c->session == NULL) + cwd = c->cwd; + else + cwd = "."; + /* - * Save the termios settings, part of which is used for new windows in - * this session. + * If this is a new client, check for nesting and save the termios + * settings (part of which is used for new windows in this session). * - * This is read again with tcgetattr() rather than using tty.tio as if - * detached, tty_open won't be called. Because of this, it must be done - * before opening the terminal as that calls tcsetattr() to prepare for - * tmux taking over. + * tcgetattr() is used rather than using tty.tio since if the client is + * detached, tty_open won't be called. It must be done before opening + * the terminal as that calls tcsetattr() to prepare for tmux taking + * over. */ if (!detached && !already_attached && c->tty.fd != -1) { + if (server_client_check_nested(cmdq->client)) { + cmdq_error(cmdq, "sessions should be nested with care, " + "unset $TMUX to force"); + return (CMD_RETURN_ERROR); + } if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); tiop = &tio; @@ -122,24 +166,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Open the terminal if necessary. */ if (!detached && !already_attached) { - if (server_client_open(c, NULL, &cause) != 0) { + if (server_client_open(c, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } } - /* Get the new session working directory. */ - if (c != NULL && c->cwd != NULL) - cwd = c->cwd; - else { - pw = getpwuid(getuid()); - if (pw->pw_dir != NULL && *pw->pw_dir != '\0') - cwd = pw->pw_dir; - else - cwd = "/"; - } - /* Find new session size. */ if (c != NULL) { sx = c->tty.sx; @@ -152,17 +185,17 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "width %s", errstr); - return (CMD_RETURN_ERROR); + goto error; } } if (detached && args_has(args, 'y')) { sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "height %s", errstr); - return (CMD_RETURN_ERROR); + goto error; } } - if (sy > 0 && options_get_number(&global_s_options, "status")) + if (sy > 0 && options_get_number(global_s_options, "status")) sy--; if (sx == 0) sx = 1; @@ -170,44 +203,64 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) sy = 1; /* Figure out the command for the new window. */ - if (target != NULL) - cmd = NULL; - else if (args->argc != 0) - cmd = args->argv[0]; + argc = -1; + argv = NULL; + if (!args_has(args, 't') && args->argc != 0) { + argc = args->argc; + argv = args->argv; + } else if (target == NULL) { + cmd = options_get_string(global_s_options, "default-command"); + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = &cmd; + } else { + argc = 0; + argv = NULL; + } + } + + path = NULL; + if (c != NULL && c->session == NULL) + envent = environ_find(c->environ, "PATH"); else - cmd = options_get_string(&global_s_options, "default-command"); + envent = environ_find(global_environ, "PATH"); + if (envent != NULL) + path = envent->value; /* Construct the environment. */ - environ_init(&env); - update = options_get_string(&global_s_options, "update-environment"); - if (c != NULL) - environ_update(update, &c->environ, &env); + env = environ_create(); + if (c != NULL && !args_has(args, 'E')) { + update = options_get_string(global_s_options, + "update-environment"); + environ_update(update, c->environ, env); + } /* Create the new session. */ - idx = -1 - options_get_number(&global_s_options, "base-index"); - s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause); + idx = -1 - options_get_number(global_s_options, "base-index"); + s = session_create(newname, argc, argv, path, cwd, env, tiop, idx, sx, + sy, &cause); + environ_free(env); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } - environ_free(&env); /* Set the initial window name if one given. */ - if (cmd != NULL && args_has(args, 'n')) { + if (argc >= 0 && args_has(args, 'n')) { w = s->curw->window; window_set_name(w, args_get(args, 'n')); - options_set_number(&w->options, "automatic-rename", 0); + options_set_number(w->options, "automatic-rename", 0); } /* * If a target session is given, this is to be part of a session group, * so add it to the group and synchronize. */ - if (groupwith != NULL) { + if (args_has(args, 't')) { session_group_add(groupwith, s); session_group_synchronize_to(s); - session_select(s, RB_ROOT(&s->windows)->idx); + session_select(s, RB_MIN(winlinks, &s->windows)->idx); } /* @@ -215,13 +268,17 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { - if (!already_attached) - server_write_ready(c); - else if (c->session != NULL) + if (!already_attached) { + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); + } else if (c->session != NULL) c->last_session = c->session; c->session = s; + server_client_set_key_table(c, NULL); + status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s); + session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); } recalculate_sizes(); @@ -239,10 +296,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - ft = format_create(); - if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) - format_client(ft, c); - format_session(ft, s); + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, NULL, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); @@ -253,5 +308,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (!detached) cmdq->client_exit = 0; + + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); + +error: + if (to_free != NULL) + free((void *)to_free); + return (CMD_RETURN_ERROR); } diff --git a/cmd-new-window.c b/cmd-new-window.c index cfc0b8bd..33f68935 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,7 +18,11 @@ #include +#include +#include #include +#include +#include #include "tmux.h" @@ -26,59 +30,79 @@ * Create a new window. */ +#define NEW_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" + enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { - "new-window", "neww", - "ac:dF:kn:Pt:", 0, 1, - "[-adkP] [-c start-directory] [-F format] [-n window-name] " - CMD_TARGET_WINDOW_USAGE " [command]", - 0, - NULL, - NULL, - cmd_new_window_exec + .name = "new-window", + .alias = "neww", + + .args = { "ac:dF:kn:Pt:", 0, -1 }, + .usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] " + CMD_TARGET_WINDOW_USAGE " [command]", + + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, + .exec = cmd_new_window_exec }; enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - struct client *c; - const char *cmd, *cwd, *template; - char *cause, *cp; - int idx, last, detached; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct client *c = cmdq->state.c; + int idx = cmdq->state.tflag.idx; + const char *cmd, *path, *template, *cwd, *to_free; + char **argv, *cause, *cp; + int argc, detached; struct format_tree *ft; + struct environ_entry *envent; if (args_has(args, 'a')) { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); - idx = wl->idx + 1; - - /* Find the next free index. */ - for (last = idx; last < INT_MAX; last++) { - if (winlink_find_by_index(&s->windows, last) == NULL) - break; - } - if (last == INT_MAX) { + if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } - - /* Move everything from last - 1 to idx up a bit. */ - for (; last > idx; last--) { - wl = winlink_find_by_index(&s->windows, last - 1); - server_link_window(s, wl, s, last, 0, 0, NULL); - server_unlink_window(s, wl); - } - } else { - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) - return (CMD_RETURN_ERROR); } detached = args_has(args, 'd'); + if (args->argc == 0) { + cmd = options_get_string(s->options, "default-command"); + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char **)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = args->argc; + argv = args->argv; + } + + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(cmdq->client->environ, "PATH"); + else + envent = environ_find(s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + + to_free = NULL; + if (args_has(args, 'c')) { + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, NULL, NULL); + cwd = to_free = format_expand(ft, args_get(args, 'c')); + format_free(ft); + } else if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else + cwd = s->cwd; + wl = NULL; if (idx != -1) wl = winlink_find_by_index(&s->windows, idx); @@ -99,19 +123,14 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } } - if (args->argc == 0) - cmd = options_get_string(&s->options, "default-command"); - else - cmd = args->argv[0]; - cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); - if (idx == -1) - idx = -1 - options_get_number(&s->options, "base-index"); - wl = session_new(s, args_get(args, 'n'), cmd, cwd, idx, &cause); + idx = -1 - options_get_number(s->options, "base-index"); + wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, + &cause); if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); - return (CMD_RETURN_ERROR); + goto error; } if (!detached) { session_select(s, wl->idx); @@ -123,12 +142,8 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - ft = format_create(); - if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) - format_client(ft, c); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wl->window->active); + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, wl, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); @@ -137,5 +152,12 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); + +error: + if (to_free != NULL) + free((void *)to_free); + return (CMD_RETURN_ERROR); } diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index b07c9faf..6bfd753e 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -33,52 +33,44 @@ void cmd_paste_buffer_filter(struct window_pane *, const char *, size_t, const char *, int); const struct cmd_entry cmd_paste_buffer_entry = { - "paste-buffer", "pasteb", - "db:prs:t:", 0, 0, - "[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_paste_buffer_exec + .name = "paste-buffer", + .alias = "pasteb", + + .args = { "db:prs:t:", 0, 0 }, + .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " + CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_paste_buffer_exec }; enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct window_pane *wp; - struct session *s; + struct window_pane *wp = cmdq->state.tflag.wp; struct paste_buffer *pb; - const char *sepstr; - char *cause; - int buffer; - int pflag; + const char *sepstr, *bufname, *bufdata, *bufend, *line; + size_t seplen, bufsize; + int bracket = args_has(args, 'p'); - if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) - return (CMD_RETURN_ERROR); + bufname = NULL; + if (args_has(args, 'b')) + bufname = args_get(args, 'b'); - if (!args_has(args, 'b')) - buffer = -1; + if (bufname == NULL) + pb = paste_get_top(NULL); else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - } - - if (buffer == -1) - pb = paste_get_top(&global_buffers); - else { - pb = paste_get_index(&global_buffers, buffer); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } - if (pb != NULL) { + if (pb != NULL && ~wp->flags & PANE_INPUTOFF) { sepstr = args_get(args, 's'); if (sepstr == NULL) { if (args_has(args, 'r')) @@ -86,17 +78,33 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else sepstr = "\r"; } - pflag = (wp->screen->mode & MODE_BRACKETPASTE); - paste_send_pane(pb, wp, sepstr, args_has(args, 'p') && pflag); + seplen = strlen(sepstr); + + if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) + bufferevent_write(wp->event, "\033[200~", 6); + + bufdata = paste_buffer_data(pb, &bufsize); + bufend = bufdata + bufsize; + + for (;;) { + line = memchr(bufdata, '\n', bufend - bufdata); + if (line == NULL) + break; + + bufferevent_write(wp->event, bufdata, line - bufdata); + bufferevent_write(wp->event, sepstr, seplen); + + bufdata = line + 1; + } + if (bufdata != bufend) + bufferevent_write(wp->event, bufdata, bufend - bufdata); + + if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) + bufferevent_write(wp->event, "\033[201~", 6); } - /* Delete the buffer if -d. */ - if (args_has(args, 'd')) { - if (buffer == -1) - paste_free_top(&global_buffers); - else - paste_free_index(&global_buffers, buffer); - } + if (pb != NULL && args_has(args, 'd')) + paste_free(pb); return (CMD_RETURN_NORMAL); } diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index aa72c699..99397ae3 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -36,27 +37,29 @@ enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmd_q *); void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); const struct cmd_entry cmd_pipe_pane_entry = { - "pipe-pane", "pipep", - "ot:", 0, 1, - "[-o] " CMD_TARGET_PANE_USAGE " [command]", - 0, - NULL, - NULL, - cmd_pipe_pane_exec + .name = "pipe-pane", + .alias = "pipep", + + .args = { "ot:", 0, 1 }, + .usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]", + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_pipe_pane_exec }; enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct window_pane *wp; - char *command; + struct client *c = cmdq->state.c; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + char *cmd; int old_fd, pipe_fd[2], null_fd; - - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - c = cmd_find_client(cmdq, NULL, 1); + struct format_tree *ft; /* Destroy the old pipe. */ old_fd = wp->pipe_fd; @@ -85,10 +88,18 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } + /* Expand the command. */ + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, wl, wp); + cmd = format_expand_time(ft, args->argv[0], time(NULL)); + format_free(ft); + /* Fork the child. */ switch (fork()) { case -1: cmdq_error(cmdq, "fork error: %s", strerror(errno)); + + free(cmd); return (CMD_RETURN_ERROR); case 0: /* Child process. */ @@ -110,9 +121,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) closefrom(STDERR_FILENO + 1); - command = status_replace( - c, NULL, NULL, NULL, args->argv[0], time(NULL), 0); - execl(_PATH_BSHELL, "sh", "-c", command, (char *) NULL); + execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); _exit(1); default: /* Parent process. */ @@ -126,13 +135,15 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) bufferevent_enable(wp->pipe_event, EV_WRITE); setblocking(wp->pipe_fd, 0); + + free(cmd); return (CMD_RETURN_NORMAL); } } void -cmd_pipe_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +cmd_pipe_pane_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { struct window_pane *wp = data; diff --git a/cmd-queue.c b/cmd-queue.c index 44602114..69cc310c 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2013 Nicholas Marriott @@ -20,11 +20,14 @@ #include #include +#include #include #include "tmux.h" #include "tmate.h" +static enum cmd_retval cmdq_continue_one(struct cmd_q *); + /* Create new command queue. */ struct cmd_q * cmdq_new(struct client *c) @@ -33,15 +36,18 @@ cmdq_new(struct client *c) cmdq = xcalloc(1, sizeof *cmdq); cmdq->references = 1; - cmdq->dead = 0; + cmdq->flags = 0; cmdq->client = c; - cmdq->client_exit = 0; + cmdq->client_exit = -1; TAILQ_INIT(&cmdq->queue); cmdq->item = NULL; cmdq->cmd = NULL; + cmd_find_clear_state(&cmdq->current, NULL, 0); + cmdq->parent = NULL; + return (cmdq); } @@ -49,8 +55,11 @@ cmdq_new(struct client *c) int cmdq_free(struct cmd_q *cmdq) { - if (--cmdq->references != 0) - return (cmdq->dead); + if (--cmdq->references != 0) { + if (cmdq->flags & CMD_Q_DEAD) + return (1); + return (0); + } cmdq_flush(cmdq); free(cmdq); @@ -58,31 +67,38 @@ cmdq_free(struct cmd_q *cmdq) } /* Show message from command. */ -void printflike2 +void cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) { struct client *c = cmdq->client; struct window *w; va_list ap; + char *tmp, *msg; va_start(ap, fmt); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { - va_start(ap, fmt); - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - va_end(ap); - + if (~c->flags & CLIENT_UTF8) { + vasprintf(&tmp, fmt, ap); + msg = utf8_sanitize(tmp); + free(tmp); + evbuffer_add(c->stdout_data, msg, strlen(msg)); + free(msg); + } else + evbuffer_add_vprintf(c->stdout_data, fmt, ap); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } else { w = c->session->curw->window; if (w->active->mode != &window_copy_mode) { window_pane_reset_mode(w->active); window_pane_set_mode(w->active, &window_copy_mode); window_copy_init_for_output(w->active); +#ifdef TMATE tmate_sync_copy_mode(w->active); +#endif } window_copy_vadd(w->active, fmt, ap); } @@ -90,69 +106,41 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) va_end(ap); } -/* Show info from command. */ -void printflike2 -cmdq_info(struct cmd_q *cmdq, const char *fmt, ...) -{ - struct client *c = cmdq->client; - va_list ap; - char *msg; - - if (options_get_number(&global_options, "quiet")) - return; - - va_start(ap, fmt); - - if (c == NULL) - /* nothing */; - else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { - va_start(ap, fmt); - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - va_end(ap); - - evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); - } else { - xvasprintf(&msg, fmt, ap); - *msg = toupper((u_char) *msg); - status_message_set(c, "%s", msg); - free(msg); - } - - va_end(ap); - -} - /* Show error from command. */ -void printflike2 +void cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) { struct client *c = cmdq->client; struct cmd *cmd = cmdq->cmd; va_list ap; - char *msg, *cause; + char *msg; size_t msglen; + char *tmp; va_start(ap, fmt); msglen = xvasprintf(&msg, fmt, ap); va_end(ap); - if (c == NULL) { + if (c == NULL) #ifdef TMATE if (cmd->file && cmd->line) - xasprintf(&cause, "%s:%u: %s", cmd->file, cmd->line, msg); + cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); else - xasprintf(&cause, "%s", msg); + cfg_add_cause("%s", msg); #else - xasprintf(&cause, "%s:%u: %s", cmd->file, cmd->line, msg); + cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); #endif - ARRAY_ADD(&cfg_causes, cause); - } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + if (~c->flags & CLIENT_UTF8) { + tmp = msg; + msg = utf8_sanitize(tmp); + free(tmp); + msglen = strlen(msg); + } evbuffer_add(c->stderr_data, msg, msglen); evbuffer_add(c->stderr_data, "\n", 1); - - server_push_stderr(c); - c->retcode = 1; + server_client_push_stderr(c); + c->retval = 1; } else { *msg = toupper((u_char) *msg); status_message_set(c, "%s", msg); @@ -162,27 +150,24 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) } /* Print a guard line. */ -int -cmdq_guard(struct cmd_q *cmdq, const char *guard) +void +cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) { struct client *c = cmdq->client; - if (c == NULL || c->session == NULL) - return 0; - if (!(c->flags & CLIENT_CONTROL)) - return 0; + if (c == NULL || !(c->flags & CLIENT_CONTROL)) + return; - evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, - (long) cmdq->time, cmdq->number); - server_push_stdout(c); - return 1; + evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, + (long) cmdq->time, cmdq->number, flags); + server_client_push_stdout(c); } /* Add command list to queue and begin processing if needed. */ void -cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) +cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) { - cmdq_append(cmdq, cmdlist); + cmdq_append(cmdq, cmdlist, m); if (cmdq->item == NULL) { cmdq->cmd = NULL; @@ -192,7 +177,7 @@ cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) /* Add command list to queue. */ void -cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist) +cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) { struct cmd_q_item *item; @@ -200,19 +185,64 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist) item->cmdlist = cmdlist; TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); cmdlist->references++; + + if (m != NULL) + memcpy(&item->mouse, m, sizeof item->mouse); + else + item->mouse.valid = 0; +} + +/* Process one command. */ +static enum cmd_retval +cmdq_continue_one(struct cmd_q *cmdq) +{ + struct cmd *cmd = cmdq->cmd; + enum cmd_retval retval; + char *tmp; + int flags = !!(cmd->flags & CMD_CONTROL); + + tmp = cmd_print(cmd); + log_debug("cmdq %p: %s", cmdq, tmp); +#ifdef TMATE + if (tmate_should_replicate_cmd(cmd->entry)) + tmate_exec_cmd(tmp); +#endif + free(tmp); + + cmdq->time = time(NULL); + cmdq->number++; + + cmdq_guard(cmdq, "begin", flags); + + if (cmd_prepare_state(cmd, cmdq) != 0) + goto error; + retval = cmd->entry->exec(cmd, cmdq); + if (retval == CMD_RETURN_ERROR) + goto error; + + cmdq_guard(cmdq, "end", flags); + return (retval); + +error: + cmdq_guard(cmdq, "error", flags); + return (CMD_RETURN_ERROR); } /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { + struct client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; - int empty, guard; - char s[1024]; + int empty; + cmdq->references++; notify_disable(); + log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags, + c); + empty = TAILQ_EMPTY(&cmdq->queue); if (empty) goto empty; @@ -224,30 +254,8 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); do { - next = TAILQ_NEXT(cmdq->item, qentry); - while (cmdq->cmd != NULL) { - cmd_print(cmdq->cmd, s, sizeof s); - log_debug("cmdq %p: %s (client %d)", cmdq, s, - cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); - - cmdq->time = time(NULL); - cmdq->number++; - -#ifdef TMATE - if (tmate_should_replicate_cmd(cmdq->cmd->entry)) - tmate_exec_cmd(s); -#endif - - guard = cmdq_guard(cmdq, "begin"); - retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); - if (guard) { - if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error"); - else - cmdq_guard(cmdq, "end"); - } - + retval = cmdq_continue_one(cmdq); if (retval == CMD_RETURN_ERROR) break; if (retval == CMD_RETURN_WAIT) @@ -256,9 +264,9 @@ cmdq_continue(struct cmd_q *cmdq) cmdq_flush(cmdq); goto empty; } - cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); } + next = TAILQ_NEXT(cmdq->item, qentry); TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry); cmd_list_free(cmdq->item->cmdlist); @@ -270,14 +278,16 @@ cmdq_continue(struct cmd_q *cmdq) } while (cmdq->item != NULL); empty: - if (cmdq->client_exit) + if (cmdq->client_exit > 0) cmdq->client->flags |= CLIENT_EXIT; if (cmdq->emptyfn != NULL) - cmdq->emptyfn(cmdq); /* may free cmdq */ + cmdq->emptyfn(cmdq); empty = 1; out: notify_enable(); + cmdq_free(cmdq); + return (empty); } @@ -294,3 +304,4 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } + diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 7d9d539f..444e83fc 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,26 +27,26 @@ enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { - "refresh-client", "refresh", - "C:St:", 0, 0, - "[-S] [-C size]" CMD_TARGET_CLIENT_USAGE, - 0, - NULL, - NULL, - cmd_refresh_client_exec + .name = "refresh-client", + .alias = "refresh", + + .args = { "C:St:", 0, 0 }, + .usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, + + .tflag = CMD_CLIENT, + + .flags = 0, + .exec = cmd_refresh_client_exec }; enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; const char *size; u_int w, h; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (args_has(args, 'C')) { if ((size = args_get(args, 'C')) == NULL) { cmdq_error(cmdq, "missing size"); @@ -68,10 +68,12 @@ cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) if (tty_set_size(&c->tty, w, h)) recalculate_sizes(); } else if (args_has(args, 'S')) { - status_update_jobs(c); + c->flags |= CLIENT_STATUSFORCE; server_status_client(c); - } else + } else { + c->flags |= CLIENT_STATUSFORCE; server_redraw_client(c); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 3f8a9d8f..7fc6193d 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -29,20 +29,23 @@ enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_session_entry = { - "rename-session", "rename", - "t:", 1, 1, - CMD_TARGET_SESSION_USAGE " new-name", - 0, - NULL, - NULL, - cmd_rename_session_exec + .name = "rename-session", + .alias = "rename", + + .args = { "t:", 1, 1 }, + .usage = CMD_TARGET_SESSION_USAGE " new-name", + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_rename_session_exec }; enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; + struct session *s = cmdq->state.tflag.s; const char *newname; newname = args->argv[0]; @@ -55,9 +58,6 @@ cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - RB_REMOVE(sessions, &sessions, s); free(s->name); s->name = xstrdup(newname); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index c756ba1f..36c1bf31 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -29,27 +29,26 @@ enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_window_entry = { - "rename-window", "renamew", - "t:", 1, 1, - CMD_TARGET_WINDOW_USAGE " new-name", - 0, - NULL, - NULL, - cmd_rename_window_exec + .name = "rename-window", + .alias = "renamew", + + .args = { "t:", 1, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " new-name", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_rename_window_exec }; enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); + struct winlink *wl = cmdq->state.tflag.wl; window_set_name(wl->window, args->argv[0]); - options_set_number(&wl->window->options, "automatic-rename", 0); + options_set_number(wl->window->options, "automatic-rename", 0); server_status_window(wl->window); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index ca2a6cd3..bb29cef9 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -26,81 +26,49 @@ * Increase or decrease pane size. */ -void cmd_resize_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); -const struct cmd_entry cmd_resize_pane_entry = { - "resize-pane", "resizep", - "DLRt:Ux:y:Z", 0, 1, - "[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", - 0, - cmd_resize_pane_key_binding, - NULL, - cmd_resize_pane_exec -}; +void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); -void -cmd_resize_pane_key_binding(struct cmd *self, int key) -{ - switch (key) { - case KEYC_UP | KEYC_CTRL: - self->args = args_create(0); - args_set(self->args, 'U', NULL); - break; - case KEYC_DOWN | KEYC_CTRL: - self->args = args_create(0); - args_set(self->args, 'D', NULL); - break; - case KEYC_LEFT | KEYC_CTRL: - self->args = args_create(0); - args_set(self->args, 'L', NULL); - break; - case KEYC_RIGHT | KEYC_CTRL: - self->args = args_create(0); - args_set(self->args, 'R', NULL); - break; - case KEYC_UP | KEYC_ESCAPE: - self->args = args_create(1, "5"); - args_set(self->args, 'U', NULL); - break; - case KEYC_DOWN | KEYC_ESCAPE: - self->args = args_create(1, "5"); - args_set(self->args, 'D', NULL); - break; - case KEYC_LEFT | KEYC_ESCAPE: - self->args = args_create(1, "5"); - args_set(self->args, 'L', NULL); - break; - case KEYC_RIGHT | KEYC_ESCAPE: - self->args = args_create(1, "5"); - args_set(self->args, 'R', NULL); - break; - case 'z': - self->args = args_create(0); - args_set(self->args, 'Z', NULL); - break; - default: - self->args = args_create(0); - break; - } -} +const struct cmd_entry cmd_resize_pane_entry = { + .name = "resize-pane", + .alias = "resizep", + + .args = { "DLMRt:Ux:y:Z", 0, 1 }, + .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " + "[adjustment]", + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_resize_pane_exec +}; enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; + struct window_pane *wp = cmdq->state.tflag.wp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct client *c = cmdq->client; + struct session *s = cmdq->state.tflag.s; const char *errstr; char *cause; - struct window_pane *wp; u_int adjust; int x, y; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; + if (args_has(args, 'M')) { + if (cmd_mouse_window(&cmdq->item->mouse, &s) == NULL) + return (CMD_RETURN_NORMAL); + if (c == NULL || c->session != s) + return (CMD_RETURN_NORMAL); + c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; + cmd_resize_pane_mouse_update(c, &cmdq->item->mouse); + return (CMD_RETURN_NORMAL); + } + w = wl->window; if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); @@ -155,3 +123,50 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } + +void +cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) +{ + struct winlink *wl; + struct window_pane *wp; + int found; + u_int y, ly; + + wl = cmd_mouse_window(m, NULL); + if (wl == NULL) { + c->tty.mouse_drag_update = NULL; + return; + } + + y = m->y; + if (m->statusat == 0 && y > 0) + y--; + else if (m->statusat > 0 && y >= (u_int)m->statusat) + y = m->statusat - 1; + ly = m->ly; + if (m->statusat == 0 && ly > 0) + ly--; + else if (m->statusat > 0 && ly >= (u_int)m->statusat) + ly = m->statusat - 1; + + found = 0; + TAILQ_FOREACH(wp, &wl->window->panes, entry) { + if (!window_pane_visible(wp)) + continue; + + if (wp->xoff + wp->sx == m->lx && + wp->yoff <= 1 + ly && wp->yoff + wp->sy >= ly) { + layout_resize_pane(wp, LAYOUT_LEFTRIGHT, m->x - m->lx); + found = 1; + } + if (wp->yoff + wp->sy == ly && + wp->xoff <= 1 + m->lx && wp->xoff + wp->sx >= m->lx) { + layout_resize_pane(wp, LAYOUT_TOPBOTTOM, y - ly); + found = 1; + } + } + if (found) + server_redraw_window(wl->window); + else + c->tty.mouse_drag_update = NULL; +} diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 4486c91f..bff6c11b 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -31,62 +31,67 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { - "respawn-pane", "respawnp", - "kt:", 0, 1, - "[-k] " CMD_TARGET_PANE_USAGE " [command]", - 0, - NULL, - NULL, - cmd_respawn_pane_exec + .name = "respawn-pane", + .alias = "respawnp", + + .args = { "kt:", 0, -1 }, + .usage = "[-k] " CMD_TARGET_PANE_USAGE " [command]", + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_respawn_pane_exec }; enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - struct session *s; - struct environ env; - const char *cmd; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; + struct environ *env; + const char *path; char *cause; u_int idx; - - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; + struct environ_entry *envent; if (!args_has(self->args, 'k') && wp->fd != -1) { if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); - cmdq_error(cmdq, "pane still active: %s:%u.%u", + cmdq_error(cmdq, "pane still active: %s:%d.%u", s->name, wl->idx, idx); return (CMD_RETURN_ERROR); } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); window_pane_reset_mode(wp); screen_reinit(&wp->base); input_init(wp); - if (args->argc != 0) - cmd = args->argv[0]; + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(cmdq->client->environ, "PATH"); else - cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + envent = environ_find(s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, + s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); - environ_free(&env); + environ_free(env); return (CMD_RETURN_ERROR); } wp->flags |= PANE_REDRAW; server_status_window(w); - environ_free(&env); + environ_free(env); return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 35bd3471..da9a365a 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -30,45 +30,45 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { - "respawn-window", "respawnw", - "kt:", 0, 1, - "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", - 0, - NULL, - NULL, - cmd_respawn_window_exec + .name = "respawn-window", + .alias = "respawnw", + + .args = { "kt:", 0, -1 }, + .usage = "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_respawn_window_exec }; enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct window_pane *wp; - struct session *s; - struct environ env; - const char *cmd; + struct environ *env; + const char *path; char *cause; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; + struct environ_entry *envent; if (!args_has(self->args, 'k')) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd == -1) continue; - cmdq_error(cmdq, - "window still active: %s:%d", s->name, wl->idx); + cmdq_error(cmdq, "window still active: %s:%d", s->name, + wl->idx); return (CMD_RETURN_ERROR); } } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); @@ -76,15 +76,21 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) window_destroy_panes(w); TAILQ_INSERT_HEAD(&w->panes, wp, entry); window_pane_resize(wp, w->sx, w->sy); - if (args->argc != 0) - cmd = args->argv[0]; + + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(cmdq->client->environ, "PATH"); else - cmd = NULL; - if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + envent = environ_find(s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, + s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); - environ_free(&env); - server_destroy_pane(wp); + environ_free(env); + server_destroy_pane(wp, 0); return (CMD_RETURN_ERROR); } layout_init(w, wp); @@ -96,6 +102,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); server_redraw_window(w); - environ_free(&env); + environ_free(env); return (CMD_RETURN_NORMAL); } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 75ca7292..014c1f2f 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -24,41 +24,30 @@ * Rotate the panes in a window. */ -void cmd_rotate_window_key_binding(struct cmd *, int); enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rotate_window_entry = { - "rotate-window", "rotatew", - "Dt:U", 0, 0, - "[-DU] " CMD_TARGET_WINDOW_USAGE, - 0, - cmd_rotate_window_key_binding, - NULL, - cmd_rotate_window_exec -}; + .name = "rotate-window", + .alias = "rotatew", -void -cmd_rotate_window_key_binding(struct cmd *self, int key) -{ - self->args = args_create(0); - if (key == ('o' | KEYC_ESCAPE)) - args_set(self->args, 'D', NULL); -} + .args = { "Dt:U", 0, 0 }, + .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_rotate_window_exec +}; enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct window *w; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct window_pane *wp, *wp2; struct layout_cell *lc; u_int sx, sy, xoff, yoff; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (args_has(self->args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); TAILQ_REMOVE(&w->panes, wp, entry); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 7c7d333c..e857e9c9 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha @@ -36,13 +36,16 @@ void cmd_run_shell_free(void *); void cmd_run_shell_print(struct job *, const char *); const struct cmd_entry cmd_run_shell_entry = { - "run-shell", "run", - "bt:", 1, 1, - "[-b] " CMD_TARGET_PANE_USAGE " shell-command", - 0, - NULL, - NULL, - cmd_run_shell_exec + .name = "run-shell", + .alias = "run", + + .args = { "bt:", 1, 1 }, + .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", + + .tflag = CMD_PANE_CANFAIL, + + .flags = 0, + .exec = cmd_run_shell_exec }; struct cmd_run_shell_data { @@ -77,30 +80,20 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct cmd_run_shell_data *cdata; char *shellcmd; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window_pane *wp = NULL; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; + const char *cwd; - if (args_has(args, 't')) - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - } - - ft = format_create(); - if (s != NULL) - format_session(ft, s); - if (s != NULL && wl != NULL) - format_winlink(ft, s, wl); - if (wp != NULL) - format_window_pane(ft, wp); + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = NULL; + ft = format_create(cmdq, 0); + format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); @@ -112,7 +105,8 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cdata->cmdq = cmdq; cmdq->references++; - job_run(shellcmd, s, cmd_run_shell_callback, cmd_run_shell_free, cdata); + job_run(shellcmd, s, cwd, cmd_run_shell_callback, cmd_run_shell_free, + cdata); if (cdata->bflag) return (CMD_RETURN_NORMAL); @@ -129,7 +123,7 @@ cmd_run_shell_callback(struct job *job) int retcode; u_int lines; - if (cmdq->dead) + if (cmdq->flags & CMD_Q_DEAD) return; cmd = cdata->cmd; @@ -162,13 +156,9 @@ cmd_run_shell_callback(struct job *job) retcode = WTERMSIG(job->status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); } - if (msg != NULL) { - if (lines == 0) - cmdq_info(cmdq, "%s", msg); - else - cmd_run_shell_print(job, msg); - free(msg); - } + if (msg != NULL) + cmd_run_shell_print(job, msg); + free(msg); } void diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 52914a94..f8756587 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha @@ -20,8 +20,10 @@ #include #include +#include #include #include +#include #include "tmux.h" @@ -32,67 +34,60 @@ enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_save_buffer_entry = { - "save-buffer", "saveb", - "ab:", 1, 1, - "[-a] " CMD_BUFFER_USAGE " path", - 0, - NULL, - NULL, - cmd_save_buffer_exec + .name = "save-buffer", + .alias = "saveb", + + .args = { "ab:", 1, 1 }, + .usage = "[-a] " CMD_BUFFER_USAGE " path", + + .flags = 0, + .exec = cmd_save_buffer_exec }; const struct cmd_entry cmd_show_buffer_entry = { - "show-buffer", "showb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - NULL, - NULL, - cmd_save_buffer_exec + .name = "show-buffer", + .alias = "showb", + + .args = { "b:", 0, 0 }, + .usage = CMD_BUFFER_USAGE, + + .flags = 0, + .exec = cmd_save_buffer_exec }; enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *newpath, *wd; - char *cause, *start, *end; - size_t size, used; - int buffer; - mode_t mask; + const char *path, *bufname, *bufdata, *start, *end, *cwd; + const char *flags; + char *msg, *file, resolved[PATH_MAX]; + size_t size, used, msglen, bufsize; FILE *f; - char *msg; - size_t msglen; if (!args_has(args, 'b')) { - if ((pb = paste_get_top(&global_buffers)) == NULL) { + if ((pb = paste_get_top(NULL)) == NULL) { cmdq_error(cmdq, "no buffers"); return (CMD_RETURN_ERROR); } } else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - pb = paste_get_index(&global_buffers, buffer); + bufname = args_get(args, 'b'); + pb = paste_get_name(bufname); if (pb == NULL) { - cmdq_error(cmdq, "no buffer %d", buffer); + cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } + bufdata = paste_buffer_data(pb, &bufsize); if (self->entry == &cmd_show_buffer_entry) path = "-"; else path = args->argv[0]; if (strcmp(path, "-") == 0) { - c = cmdq->client; if (c == NULL) { cmdq_error(cmdq, "can't write to stdout"); return (CMD_RETURN_ERROR); @@ -102,33 +97,35 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) goto do_print; } - c = cmdq->client; - if (c != NULL) - wd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) { - wd = options_get_string(&s->options, "default-path"); - if (*wd == '\0') - wd = s->cwd; - } else - wd = NULL; - if (wd != NULL && *wd != '\0') { - newpath = get_full_path(wd, path); - if (newpath != NULL) - path = newpath; - } - - mask = umask(S_IRWXG | S_IRWXO); - if (args_has(self->args, 'a')) - f = fopen(path, "ab"); + if (c != NULL && c->session == NULL) + cwd = c->cwd; + else if ((s = c->session) != NULL) + cwd = s->cwd; else - f = fopen(path, "wb"); - umask(mask); - if (f == NULL) { - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + cwd = "."; + + flags = "wb"; + if (args_has(self->args, 'a')) + flags = "ab"; + + if (*path == '/') + file = xstrdup(path); + else + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL && + strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { + cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); return (CMD_RETURN_ERROR); } - if (fwrite(pb->data, 1, pb->size, f) != pb->size) { - cmdq_error(cmdq, "%s: fwrite error", path); + f = fopen(resolved, flags); + free(file); + if (f == NULL) { + cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); + return (CMD_RETURN_ERROR); + } + + if (fwrite(bufdata, 1, bufsize, f) != bufsize) { + cmdq_error(cmdq, "%s: write error", resolved); fclose(f); return (CMD_RETURN_ERROR); } @@ -137,29 +134,28 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); do_stdout: - evbuffer_add(c->stdout_data, pb->data, pb->size); - server_push_stdout(c); + evbuffer_add(c->stdout_data, bufdata, bufsize); + server_client_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: - if (pb->size > (INT_MAX / 4) - 1) { + if (bufsize > (INT_MAX / 4) - 1) { cmdq_error(cmdq, "buffer too big"); return (CMD_RETURN_ERROR); } msg = NULL; - msglen = 0; used = 0; - while (used != pb->size) { - start = pb->data + used; - end = memchr(start, '\n', pb->size - used); + while (used != bufsize) { + start = bufdata + used; + end = memchr(start, '\n', bufsize - used); if (end != NULL) size = end - start; else - size = pb->size - used; + size = bufsize - used; msglen = size * 4 + 1; - msg = xrealloc(msg, 1, msglen); + msg = xrealloc(msg, msglen); strvisx(msg, start, size, VIS_OCTAL|VIS_TAB); cmdq_print(cmdq, "%s", msg); diff --git a/cmd-select-layout.c b/cmd-select-layout.c index ae1be4c4..e6ede1af 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -18,118 +18,122 @@ #include +#include + #include "tmux.h" /* * Switch window to selected layout. */ -void cmd_select_layout_key_binding(struct cmd *, int); enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { - "select-layout", "selectl", - "npt:", 0, 1, - "[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - 0, - cmd_select_layout_key_binding, - NULL, - cmd_select_layout_exec + .name = "select-layout", + .alias = "selectl", + + .args = { "nopt:", 0, 1 }, + .usage = "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_next_layout_entry = { - "next-layout", "nextl", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_select_layout_exec + .name = "next-layout", + .alias = "nextl", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_previous_layout_entry = { - "previous-layout", "prevl", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_select_layout_exec -}; + .name = "previous-layout", + .alias = "prevl", -void -cmd_select_layout_key_binding(struct cmd *self, int key) -{ - switch (key) { - case '1' | KEYC_ESCAPE: - self->args = args_create(1, "even-horizontal"); - break; - case '2' | KEYC_ESCAPE: - self->args = args_create(1, "even-vertical"); - break; - case '3' | KEYC_ESCAPE: - self->args = args_create(1, "main-horizontal"); - break; - case '4' | KEYC_ESCAPE: - self->args = args_create(1, "main-vertical"); - break; - case '5' | KEYC_ESCAPE: - self->args = args_create(1, "tiled"); - break; - default: - self->args = args_create(0); - break; - } -} + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_select_layout_exec +}; enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w; const char *layoutname; + char *oldlayout; int next, previous, layout; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - server_unzoom_window(wl->window); + w = wl->window; + server_unzoom_window(w); next = self->entry == &cmd_next_layout_entry; - if (args_has(self->args, 'n')) + if (args_has(args, 'n')) next = 1; previous = self->entry == &cmd_previous_layout_entry; - if (args_has(self->args, 'p')) + if (args_has(args, 'p')) previous = 1; + oldlayout = w->old_layout; + w->old_layout = layout_dump(w->layout_root); + if (next || previous) { if (next) - layout = layout_set_next(wl->window); + layout_set_next(w); else - layout = layout_set_previous(wl->window); - server_redraw_window(wl->window); - cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); - return (CMD_RETURN_NORMAL); + layout_set_previous(w); + goto changed; } - if (args->argc == 0) - layout = wl->window->lastlayout; - else - layout = layout_set_lookup(args->argv[0]); - if (layout != -1) { - layout = layout_set_select(wl->window, layout); - server_redraw_window(wl->window); - cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); - return (CMD_RETURN_NORMAL); - } - - if (args->argc != 0) { - layoutname = args->argv[0]; - if (layout_parse(wl->window, layoutname) == -1) { - cmdq_error(cmdq, "can't set layout: %s", layoutname); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'o')) { + if (args->argc == 0) + layout = w->lastlayout; + else + layout = layout_set_lookup(args->argv[0]); + if (layout != -1) { + layout_set_select(w, layout); + goto changed; } - server_redraw_window(wl->window); - cmdq_info(cmdq, "arranging in: %s", layoutname); } + + if (args->argc != 0) + layoutname = args->argv[0]; + else if (args_has(args, 'o')) + layoutname = oldlayout; + else + layoutname = NULL; + + if (layoutname != NULL) { + if (layout_parse(w, layoutname) == -1) { + cmdq_error(cmdq, "can't set layout: %s", layoutname); + goto error; + } + goto changed; + } + + free(oldlayout); return (CMD_RETURN_NORMAL); + +changed: + free(oldlayout); + server_redraw_window(w); + return (CMD_RETURN_NORMAL); + +error: + free(w->old_layout); + w->old_layout = oldlayout; + return (CMD_RETURN_ERROR); } diff --git a/cmd-select-pane.c b/cmd-select-pane.c index b8a12671..6ebe753c 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -24,95 +24,147 @@ * Select pane. */ -void cmd_select_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { - "select-pane", "selectp", - "lDLRt:U", 0, 0, - "[-lDLRU] " CMD_TARGET_PANE_USAGE, - 0, - cmd_select_pane_key_binding, - NULL, - cmd_select_pane_exec + .name = "select-pane", + .alias = "selectp", + + .args = { "DdegLlMmP:Rt:U", 0, 0 }, + .usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_select_pane_exec }; const struct cmd_entry cmd_last_pane_entry = { - "last-pane", "lastp", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_select_pane_exec -}; + .name = "last-pane", + .alias = "lastp", -void -cmd_select_pane_key_binding(struct cmd *self, int key) -{ - self->args = args_create(0); - if (key == KEYC_UP) - args_set(self->args, 'U', NULL); - if (key == KEYC_DOWN) - args_set(self->args, 'D', NULL); - if (key == KEYC_LEFT) - args_set(self->args, 'L', NULL); - if (key == KEYC_RIGHT) - args_set(self->args, 'R', NULL); - if (key == 'o') - args_set(self->args, 't', ":.+"); -} + .args = { "det:", 0, 0 }, + .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_select_pane_exec +}; enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window_pane *wp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct session *s = cmdq->state.tflag.s; + struct window_pane *wp = cmdq->state.tflag.wp, *lastwp, *markedwp; + const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); if (wl->window->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } - server_unzoom_window(wl->window); - window_set_active_pane(wl->window, wl->window->last); - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + if (args_has(self->args, 'e')) + w->last->flags &= ~PANE_INPUTOFF; + else if (args_has(self->args, 'd')) + w->last->flags |= PANE_INPUTOFF; + else { + server_unzoom_window(w); + window_redraw_active_switch(w, w->last); + if (window_set_active_pane(w, w->last)) { + server_status_window(w); + server_redraw_window_borders(w); + } + } return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); - server_unzoom_window(wp->window); if (!window_pane_visible(wp)) { cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); } - if (args_has(self->args, 'L')) - wp = window_pane_find_left(wp); - else if (args_has(self->args, 'R')) - wp = window_pane_find_right(wp); - else if (args_has(self->args, 'U')) - wp = window_pane_find_up(wp); - else if (args_has(self->args, 'D')) - wp = window_pane_find_down(wp); - if (wp == NULL) { - cmdq_error(cmdq, "pane not found"); - return (CMD_RETURN_ERROR); + if (args_has(args, 'm') || args_has(args, 'M')) { + if (args_has(args, 'm') && !window_pane_visible(wp)) + return (CMD_RETURN_NORMAL); + lastwp = marked_pane.wp; + + if (args_has(args, 'M') || server_is_marked(s, wl, wp)) + server_clear_marked(); + else + server_set_marked(s, wl, wp); + markedwp = marked_pane.wp; + + if (lastwp != NULL) { + server_redraw_window_borders(lastwp->window); + server_status_window(lastwp->window); + } + if (markedwp != NULL) { + server_redraw_window_borders(markedwp->window); + server_status_window(markedwp->window); + } + return (CMD_RETURN_NORMAL); } - window_set_active_pane(wl->window, wp); - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + if (args_has(self->args, 'P') || args_has(self->args, 'g')) { + if (args_has(args, 'P')) { + style = args_get(args, 'P'); + if (style_parse(&grid_default_cell, &wp->colgc, + style) == -1) { + cmdq_error(cmdq, "bad style: %s", style); + return (CMD_RETURN_ERROR); + } + wp->flags |= PANE_REDRAW; + } + if (args_has(self->args, 'g')) + cmdq_print(cmdq, "%s", style_tostring(&wp->colgc)); + return (CMD_RETURN_NORMAL); + } + + if (args_has(self->args, 'L')) { + server_unzoom_window(wp->window); + wp = window_pane_find_left(wp); + } else if (args_has(self->args, 'R')) { + server_unzoom_window(wp->window); + wp = window_pane_find_right(wp); + } else if (args_has(self->args, 'U')) { + server_unzoom_window(wp->window); + wp = window_pane_find_up(wp); + } else if (args_has(self->args, 'D')) { + server_unzoom_window(wp->window); + wp = window_pane_find_down(wp); + } + if (wp == NULL) + return (CMD_RETURN_NORMAL); + + if (args_has(self->args, 'e')) { + wp->flags &= ~PANE_INPUTOFF; + return (CMD_RETURN_NORMAL); + } + if (args_has(self->args, 'd')) { + wp->flags |= PANE_INPUTOFF; + return (CMD_RETURN_NORMAL); + } + + if (wp == w->active) + return (CMD_RETURN_NORMAL); + server_unzoom_window(wp->window); + if (!window_pane_visible(wp)) { + cmdq_error(cmdq, "pane not visible"); + return (CMD_RETURN_ERROR); + } + window_redraw_active_switch(w, wp); + if (window_set_active_pane(w, wp)) { + server_status_window(w); + server_redraw_window_borders(w); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-select-window.c b/cmd-select-window.c index c15d5858..82acc859 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -26,69 +26,65 @@ * Select window by index. */ -void cmd_select_window_key_binding(struct cmd *, int); enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_window_entry = { - "select-window", "selectw", - "lnpTt:", 0, 0, - "[-lnpT] " CMD_TARGET_WINDOW_USAGE, - 0, - cmd_select_window_key_binding, - NULL, - cmd_select_window_exec + .name = "select-window", + .alias = "selectw", + + .args = { "lnpTt:", 0, 0 }, + .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, + + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_next_window_entry = { - "next-window", "next", - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, - 0, - cmd_select_window_key_binding, - NULL, - cmd_select_window_exec + .name = "next-window", + .alias = "next", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_previous_window_entry = { - "previous-window", "prev", - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, - 0, - cmd_select_window_key_binding, - NULL, - cmd_select_window_exec + .name = "previous-window", + .alias = "prev", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_last_window_entry = { - "last-window", "last", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - 0, - NULL, - NULL, - cmd_select_window_exec + .name = "last-window", + .alias = "last", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_select_window_exec }; -void -cmd_select_window_key_binding(struct cmd *self, int key) -{ - char tmp[16]; - - self->args = args_create(0); - if (key >= '0' && key <= '9') { - xsnprintf(tmp, sizeof tmp, ":%d", key - '0'); - args_set(self->args, 't', tmp); - } - if (key == ('n' | KEYC_ESCAPE) || key == ('p' | KEYC_ESCAPE)) - args_set(self->args, 'a', NULL); -} - enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct session *s; + struct winlink *wl = cmdq->state.tflag.wl; + struct session *s = cmdq->state.tflag.s; int next, previous, last, activity; next = self->entry == &cmd_next_window_entry; @@ -102,10 +98,6 @@ cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) last = 1; if (next || previous || last) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - activity = args_has(self->args, 'a'); if (next) { if (session_next(s, activity) != 0) { @@ -126,10 +118,6 @@ cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) server_redraw_session(s); } else { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); - /* * If -T and select-window is invoked on same window as * current, switch to previous window. diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 37d4fd2b..7b0b952c 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -30,72 +30,76 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { - "send-keys", "send", - "lRt:", 0, -1, - "[-lR] " CMD_TARGET_PANE_USAGE " key ...", - 0, - NULL, - NULL, - cmd_send_keys_exec + .name = "send-keys", + .alias = "send", + + .args = { "lRMt:", 0, -1 }, + .usage = "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_send_keys_exec }; const struct cmd_entry cmd_send_prefix_entry = { - "send-prefix", NULL, - "2t:", 0, 0, - "[-2] " CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_send_keys_exec + .name = "send-prefix", + .alias = NULL, + + .args = { "2t:", 0, 0 }, + .usage = "[-2] " CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_send_keys_exec }; enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct window_pane *wp; - struct session *s; - struct input_ctx *ictx; - const char *str; - int i, key; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; + struct mouse_event *m = &cmdq->item->mouse; + const u_char *keystr; + int i, literal; + key_code key; - if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) - return (CMD_RETURN_ERROR); - - if (self->entry == &cmd_send_prefix_entry) { - if (args_has(args, '2')) - key = options_get_number(&s->options, "prefix2"); - else - key = options_get_number(&s->options, "prefix"); - window_pane_key(wp, s, key); + if (args_has(args, 'M')) { + wp = cmd_mouse_pane(m, &s, NULL); + if (wp == NULL) { + cmdq_error(cmdq, "no mouse target"); + return (CMD_RETURN_ERROR); + } + window_pane_key(wp, NULL, s, m->key, m); return (CMD_RETURN_NORMAL); } - if (args_has(args, 'R')) { - ictx = &wp->ictx; - - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; - - if (wp->mode == NULL) - screen_write_start(&ictx->ctx, wp, &wp->base); + if (self->entry == &cmd_send_prefix_entry) { + if (args_has(args, '2')) + key = options_get_number(s->options, "prefix2"); else - screen_write_start(&ictx->ctx, NULL, &wp->base); - screen_write_reset(&ictx->ctx); - screen_write_stop(&ictx->ctx); + key = options_get_number(s->options, "prefix"); + window_pane_key(wp, NULL, s, key, NULL); + return (CMD_RETURN_NORMAL); } - for (i = 0; i < args->argc; i++) { - str = args->argv[i]; + if (args_has(args, 'R')) + input_reset(wp, 1); - if (!args_has(args, 'l') && - (key = key_string_lookup_string(str)) != KEYC_NONE) { - window_pane_key(wp, s, key); - } else { - for (; *str != '\0'; str++) - window_pane_key(wp, s, *str); + for (i = 0; i < args->argc; i++) { + literal = args_has(args, 'l'); + if (!literal) { + key = key_string_lookup_string(args->argv[i]); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) + window_pane_key(wp, NULL, s, key, NULL); + else + literal = 1; + } + if (literal) { + for (keystr = args->argv[i]; *keystr != '\0'; keystr++) + window_pane_key(wp, NULL, s, *keystr, NULL); } } diff --git a/cmd-server-info.c b/cmd-server-info.c deleted file mode 100644 index 8eba172a..00000000 --- a/cmd-server-info.c +++ /dev/null @@ -1,174 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2008 Nicholas Marriott - * - * 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 -#include - -#include -#include -#include -#include - -#include "tmux.h" - -/* - * Show various information about server. - */ - -enum cmd_retval cmd_server_info_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_server_info_entry = { - "server-info", "info", - "", 0, 0, - "", - 0, - NULL, - NULL, - cmd_server_info_exec -}; - -enum cmd_retval -cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq) -{ - struct tty_term *term; - struct client *c; - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - struct tty_code *code; - const struct tty_term_code_entry *ent; - struct utsname un; - struct job *job; - struct grid *gd; - struct grid_line *gl; - u_int i, j, k, lines; - size_t size; - char out[80]; - char *tim; - time_t t; - - tim = ctime(&start_time); - *strchr(tim, '\n') = '\0'; - cmdq_print(cmdq, - "tmux " VERSION ", pid %ld, started %s", (long) getpid(), tim); - cmdq_print(cmdq, "socket path %s, debug level %d", socket_path, - debug_level); - if (uname(&un) >= 0) { - cmdq_print(cmdq, "system is %s %s %s %s", - un.sysname, un.release, un.version, un.machine); - } - if (cfg_file != NULL) - cmdq_print(cmdq, "configuration file is %s", cfg_file); - else - cmdq_print(cmdq, "configuration file not specified"); - cmdq_print(cmdq, "protocol version is %d", PROTOCOL_VERSION); - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Clients:"); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - - cmdq_print(cmdq,"%2d: %s (%d, %d): %s [%ux%u %s bs=%hho " - "class=%u] [flags=0x%x/0x%x, references=%u]", i, - c->tty.path, c->ibuf.fd, c->tty.fd, c->session->name, - c->tty.sx, c->tty.sy, c->tty.termname, - c->tty.tio.c_cc[VERASE], c->tty.class, - c->flags, c->tty.flags, c->references); - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Sessions: [%zu]", sizeof (struct grid_cell)); - RB_FOREACH(s, sessions, &sessions) { - t = s->creation_time.tv_sec; - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - - cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] " - "[flags=0x%x]", s->id, s->name, - winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); - RB_FOREACH(wl, winlinks, &s->windows) { - w = wl->window; - cmdq_print(cmdq, "%4u: %s [%ux%u] [flags=0x%x, " - "references=%u, last layout=%d]", wl->idx, w->name, - w->sx, w->sy, w->flags, w->references, - w->lastlayout); - j = 0; - TAILQ_FOREACH(wp, &w->panes, entry) { - lines = size = 0; - gd = wp->base.grid; - for (k = 0; k < gd->hsize + gd->sy; k++) { - gl = &gd->linedata[k]; - if (gl->celldata == NULL) - continue; - lines++; - size += gl->cellsize * - sizeof *gl->celldata; - } - cmdq_print(cmdq, - "%6u: %s %lu %d %u/%u, %zu bytes", j, - wp->tty, (u_long) wp->pid, wp->fd, lines, - gd->hsize + gd->sy, size); - j++; - } - } - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Terminals:"); - LIST_FOREACH(term, &tty_terms, entry) { - cmdq_print(cmdq, "%s [references=%u, flags=0x%x]:", - term->name, term->references, term->flags); - for (i = 0; i < NTTYCODE; i++) { - ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; - switch (code->type) { - case TTYCODE_NONE: - cmdq_print(cmdq, "%2u: %s: [missing]", - ent->code, ent->name); - break; - case TTYCODE_STRING: - strnvis(out, code->value.string, sizeof out, - VIS_OCTAL|VIS_TAB|VIS_NL); - cmdq_print(cmdq, "%2u: %s: (string) %s", - ent->code, ent->name, out); - break; - case TTYCODE_NUMBER: - cmdq_print(cmdq, "%2u: %s: (number) %d", - ent->code, ent->name, code->value.number); - break; - case TTYCODE_FLAG: - cmdq_print(cmdq, "%2u: %s: (flag) %s", - ent->code, ent->name, - code->value.flag ? "true" : "false"); - break; - } - } - } - cmdq_print(cmdq, "%s", ""); - - cmdq_print(cmdq, "Jobs:"); - LIST_FOREACH(job, &all_jobs, lentry) { - cmdq_print(cmdq, "%s [fd=%d, pid=%d, status=%d]", - job->cmd, job->fd, job->pid, job->status); - } - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 46d28ff2..1494cf26 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -24,51 +24,98 @@ #include "tmux.h" /* - * Add or set a paste buffer. + * Add, set, append to or delete a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { - "set-buffer", "setb", - "b:", 1, 1, - CMD_BUFFER_USAGE " data", - 0, - NULL, - NULL, - cmd_set_buffer_exec + .name = "set-buffer", + .alias = "setb", + + .args = { "ab:n:", 0, 1 }, + .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", + + .flags = 0, + .exec = cmd_set_buffer_exec +}; + +const struct cmd_entry cmd_delete_buffer_entry = { + .name = "delete-buffer", + .alias = "deleteb", + + .args = { "b:", 0, 0 }, + .usage = CMD_BUFFER_USAGE, + + .flags = 0, + .exec = cmd_set_buffer_exec }; enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - u_int limit; - char *pdata, *cause; - size_t psize; - int buffer; + struct args *args = self->args; + struct paste_buffer *pb; + char *bufdata, *cause; + const char *bufname, *olddata; + size_t bufsize, newsize; - limit = options_get_number(&global_options, "buffer-limit"); + bufname = args_get(args, 'b'); + if (bufname == NULL) + pb = NULL; + else + pb = paste_get_name(bufname); - pdata = xstrdup(args->argv[0]); - psize = strlen(pdata); - - if (!args_has(args, 'b')) { - paste_add(&global_buffers, pdata, psize, limit); + if (self->entry == &cmd_delete_buffer_entry) { + if (pb == NULL) + pb = paste_get_top(&bufname); + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); + return (CMD_RETURN_ERROR); + } + paste_free(pb); return (CMD_RETURN_NORMAL); } - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - free(pdata); - return (CMD_RETURN_ERROR); + if (args_has(args, 'n')) { + if (pb == NULL) + pb = paste_get_top(&bufname); + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); + return (CMD_RETURN_ERROR); + } + if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { + cmdq_error(cmdq, "%s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } - if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); - free(pdata); + if (args->argc != 1) { + cmdq_error(cmdq, "no data specified"); + return (CMD_RETURN_ERROR); + } + if ((newsize = strlen(args->argv[0])) == 0) + return (CMD_RETURN_NORMAL); + + bufsize = 0; + bufdata = NULL; + + if (args_has(args, 'a') && pb != NULL) { + olddata = paste_buffer_data(pb, &bufsize); + bufdata = xmalloc(bufsize); + memcpy(bufdata, olddata, bufsize); + } + + bufdata = xrealloc(bufdata, bufsize + newsize); + memcpy(bufdata + bufsize, args->argv[0], newsize); + bufsize += newsize; + + if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { + cmdq_error(cmdq, "%s", cause); + free(bufdata); + free(cause); return (CMD_RETURN_ERROR); } diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 0f0365aa..f701d7d9 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -30,20 +30,22 @@ enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_environment_entry = { - "set-environment", "setenv", - "grt:u", 1, 2, - "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - 0, - NULL, - NULL, - cmd_set_environment_exec + .name = "set-environment", + .alias = "setenv", + + .args = { "grt:u", 1, 2 }, + .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", + + .tflag = CMD_SESSION_CANFAIL, + + .flags = 0, + .exec = cmd_set_environment_exec }; enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct environ *env; const char *name, *value; @@ -62,13 +64,10 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) else value = args->argv[1]; - if (args_has(self->args, 'g')) - env = &global_environ; - else { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - env = &s->environ; - } + if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) + env = global_environ; + else + env = cmdq->state.tflag.s->environ; if (args_has(self->args, 'u')) { if (value != NULL) { @@ -81,13 +80,13 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "can't specify a value with -r"); return (CMD_RETURN_ERROR); } - environ_set(env, name, NULL); + environ_clear(env, name); } else { if (value == NULL) { cmdq_error(cmdq, "no value specified"); return (CMD_RETURN_ERROR); } - environ_set(env, name, value); + environ_set(env, name, "%s", value); } return (CMD_RETURN_NORMAL); diff --git a/cmd-set-hook.c b/cmd-set-hook.c new file mode 100644 index 00000000..8ef02f8c --- /dev/null +++ b/cmd-set-hook.c @@ -0,0 +1,120 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Thomas Adam + * + * 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 + +#include +#include + +#include "tmux.h" + +/* + * Set or show global or session hooks. + */ + +enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmd_q *); + +const struct cmd_entry cmd_set_hook_entry = { + .name = "set-hook", + .alias = NULL, + + .args = { "gt:u", 1, 2 }, + .usage = "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_set_hook_exec +}; + +const struct cmd_entry cmd_show_hooks_entry = { + .name = "show-hooks", + .alias = NULL, + + .args = { "gt:", 0, 1 }, + .usage = "[-g] " CMD_TARGET_SESSION_USAGE, + + .tflag = CMD_SESSION, + + .flags = 0, + .exec = cmd_set_hook_exec +}; + +enum cmd_retval +cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + struct cmd_list *cmdlist; + struct hooks *hooks; + struct hook *hook; + char *cause, *tmp; + const char *name, *cmd; + + if (args_has(args, 'g')) + hooks = global_hooks; + else + hooks = cmdq->state.tflag.s->hooks; + + if (self->entry == &cmd_show_hooks_entry) { + hook = hooks_first(hooks); + while (hook != NULL) { + tmp = cmd_list_print(hook->cmdlist); + cmdq_print(cmdq, "%s -> %s", hook->name, tmp); + free(tmp); + + hook = hooks_next(hook); + } + return (CMD_RETURN_NORMAL); + } + + name = args->argv[0]; + if (*name == '\0') { + cmdq_error(cmdq, "invalid hook name"); + return (CMD_RETURN_ERROR); + } + if (args->argc < 2) + cmd = NULL; + else + cmd = args->argv[1]; + + if (args_has(args, 'u')) { + if (cmd != NULL) { + cmdq_error(cmdq, "command passed to unset hook: %s", + name); + return (CMD_RETURN_ERROR); + } + hooks_remove(hooks, name); + return (CMD_RETURN_NORMAL); + } + + if (cmd == NULL) { + cmdq_error(cmdq, "no command to set hook: %s", name); + return (CMD_RETURN_ERROR); + } + if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { + if (cause != NULL) { + cmdq_error(cmdq, "%s", cause); + free(cause); + } + return (CMD_RETURN_ERROR); + } + hooks_add(hooks, name, cmdlist); + cmd_list_free(cmdlist); + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd-set-option.c b/cmd-set-option.c index a46460a8..13de02a3 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -60,39 +60,47 @@ struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_q *, struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); +struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, + const struct options_table_entry *, struct options *, + const char *); const struct cmd_entry cmd_set_option_entry = { - "set-option", "set", - "agoqst:uw", 1, 2, - "[-agosquw] [-t target-session|target-window] option [value]", - 0, - NULL, - NULL, - cmd_set_option_exec + .name = "set-option", + .alias = "set", + + .args = { "agoqst:uw", 1, 2 }, + .usage = "[-agosquw] [-t target-window] option [value]", + + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, + .exec = cmd_set_option_exec }; const struct cmd_entry cmd_set_window_option_entry = { - "set-window-option", "setw", - "agoqt:u", 1, 2, - "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", - 0, - NULL, - NULL, - cmd_set_option_exec + .name = "set-window-option", + .alias = "setw", + + .args = { "agoqt:u", 1, 2 }, + .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", + + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, + .exec = cmd_set_option_exec }; enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - const struct options_table_entry *table, *oe; - struct session *s; - struct winlink *wl; - struct client *c; - struct options *oo; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; struct window *w; + struct client *c; + const struct options_table_entry *oe; + struct options *oo; const char *optstr, *valstr; - u_int i; /* Get the option name and value. */ optstr = args->argv[0]; @@ -110,36 +118,50 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (cmd_set_option_user(self, cmdq, optstr, valstr)); /* Find the option entry, try each table. */ - table = oe = NULL; - if (options_table_find(optstr, &table, &oe) != 0) { - cmdq_error(cmdq, "ambiguous option: %s", optstr); - return (CMD_RETURN_ERROR); + oe = NULL; + if (options_table_find(optstr, &oe) != 0) { + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "ambiguous option: %s", optstr); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } if (oe == NULL) { - cmdq_error(cmdq, "unknown option: %s", optstr); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "unknown option: %s", optstr); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } - /* Work out the tree from the table. */ - if (table == server_options_table) - oo = &global_options; - else if (table == window_options_table) { + /* Work out the tree from the scope of the option. */ + if (oe->scope == OPTIONS_TABLE_SERVER) + oo = global_options; + else if (oe->scope == OPTIONS_TABLE_WINDOW) { if (args_has(self->args, 'g')) - oo = &global_w_options; + oo = global_w_options; else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) + if (wl == NULL) { + cmdq_error(cmdq, + "couldn't set '%s'%s", optstr, + (!args_has(args, 't') && !args_has(args, + 'g')) ? " need target window or -g" : ""); return (CMD_RETURN_ERROR); - oo = &wl->window->options; + } + oo = wl->window->options; } - } else if (table == session_options_table) { + } else if (oe->scope == OPTIONS_TABLE_SESSION) { if (args_has(self->args, 'g')) - oo = &global_s_options; + oo = global_s_options; else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) + if (s == NULL) { + cmdq_error(cmdq, + "couldn't set '%s'%s", optstr, + (!args_has(args, 't') && !args_has(args, + 'g')) ? " need target session or -g" : ""); return (CMD_RETURN_ERROR); - oo = &s->options; + } + oo = s->options; } } else { cmdq_error(cmdq, "unknown table"); @@ -152,31 +174,37 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } else { if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { - if (!args_has(args, 'q')) - cmdq_print(cmdq, "already set: %s", optstr); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "already set: %s", optstr); + return (CMD_RETURN_ERROR); + } return (CMD_RETURN_NORMAL); } if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0) return (CMD_RETURN_ERROR); } - /* Start or stop timers when automatic-rename changed. */ - if (strcmp (oe->name, "automatic-rename") == 0) { - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; - if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); - else if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + /* Start or stop timers if necessary. */ + if (strcmp(oe->name, "automatic-rename") == 0) { + RB_FOREACH(w, windows, &windows) { + if (options_get_number(w->options, "automatic-rename")) + w->active->flags |= PANE_CHANGED; } } + if (strcmp(oe->name, "key-table") == 0) { + TAILQ_FOREACH(c, &clients, entry) + server_client_set_key_table(c, NULL); + } + if (strcmp(oe->name, "status") == 0 || + strcmp(oe->name, "status-interval") == 0) + status_timer_start_all(); + if (strcmp(oe->name, "monitor-silence") == 0) + alerts_reset_all(); /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session != NULL) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL) server_redraw_client(c); } @@ -185,41 +213,36 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Set user option. */ enum cmd_retval -cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, +cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, const char *valstr) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; struct options *oo; if (args_has(args, 's')) - oo = &global_options; + oo = global_options; else if (args_has(self->args, 'w') || self->entry == &cmd_set_window_option_entry) { if (args_has(self->args, 'g')) - oo = &global_w_options; - else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); - oo = &wl->window->options; - } + oo = global_w_options; + else + oo = wl->window->options; } else { if (args_has(self->args, 'g')) - oo = &global_s_options; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - oo = &s->options; - } + oo = global_s_options; + else + oo = s->options; } if (args_has(args, 'u')) { if (options_find1(oo, optstr) == NULL) { - cmdq_error(cmdq, "unknown option: %s", optstr); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "unknown option: %s", optstr); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } if (valstr != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", @@ -233,54 +256,64 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, return (CMD_RETURN_ERROR); } if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { - if (!args_has(args, 'q')) - cmdq_print(cmdq, "already set: %s", optstr); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "already set: %s", optstr); + return (CMD_RETURN_ERROR); + } return (CMD_RETURN_NORMAL); } options_set_string(oo, optstr, "%s", valstr); - if (!args_has(args, 'q')) { - cmdq_info(cmdq, "set option: %s -> %s", optstr, - valstr); - } } return (CMD_RETURN_NORMAL); } - /* Unset an option. */ int cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { struct args *args = self->args; - if (args_has(args, 'g')) { - cmdq_error(cmdq, "can't unset global option: %s", oe->name); - return (-1); - } if (value != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", oe->name); return (-1); } - options_remove(oo, oe->name); - if (!args_has(args, 'q')) - cmdq_info(cmdq, "unset option: %s", oe->name); + if (args_has(args, 'g') || oo == global_options) { + switch (oe->type) { + case OPTIONS_TABLE_STRING: + options_set_string(oo, oe->name, "%s", oe->default_str); + break; + case OPTIONS_TABLE_STYLE: + options_set_style(oo, oe->name, oe->default_str, 0); + break; + default: + options_set_number(oo, oe->name, oe->default_num); + break; + } + } else + options_remove(oo, oe->name); return (0); } /* Set an option. */ int cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) + const struct options_table_entry *oe, struct options *oo, + const char *value) { - struct args *args = self->args; struct options_entry *o; - const char *s; - if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) { - cmdq_error(cmdq, "empty value"); - return (-1); + switch (oe->type) { + case OPTIONS_TABLE_FLAG: + case OPTIONS_TABLE_CHOICE: + break; + default: + if (value == NULL) { + cmdq_error(cmdq, "empty value"); + return (-1); + } } o = NULL; @@ -296,9 +329,13 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, break; case OPTIONS_TABLE_COLOUR: o = cmd_set_option_colour(self, cmdq, oe, oo, value); + if (o != NULL) + style_update_new(oo, o->name, oe->style); break; case OPTIONS_TABLE_ATTRIBUTES: o = cmd_set_option_attributes(self, cmdq, oe, oo, value); + if (o != NULL) + style_update_new(oo, o->name, oe->style); break; case OPTIONS_TABLE_FLAG: o = cmd_set_option_flag(self, cmdq, oe, oo, value); @@ -306,20 +343,20 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, case OPTIONS_TABLE_CHOICE: o = cmd_set_option_choice(self, cmdq, oe, oo, value); break; + case OPTIONS_TABLE_STYLE: + o = cmd_set_option_style(self, cmdq, oe, oo, value); + break; } if (o == NULL) return (-1); - - s = options_table_print_entry(oe, o, 0); - if (!args_has(args, 'q')) - cmdq_info(cmdq, "set option: %s -> %s", oe->name, s); return (0); } /* Set a string option. */ struct options_entry * -cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) +cmd_set_option_string(struct cmd *self, __unused struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) { struct args *args = self->args; struct options_entry *o; @@ -339,8 +376,9 @@ cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, /* Set a number option. */ struct options_entry * -cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) +cmd_set_option_number(__unused struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) { long long ll; const char *errstr; @@ -356,12 +394,14 @@ cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, /* Set a key option. */ struct options_entry * -cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) +cmd_set_option_key(__unused struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) { - int key; + key_code key; - if ((key = key_string_lookup_string(value)) == KEYC_NONE) { + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { cmdq_error(cmdq, "bad key: %s", value); return (NULL); } @@ -371,8 +411,9 @@ cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, /* Set a colour option. */ struct options_entry * -cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) +cmd_set_option_colour(__unused struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) { int colour; @@ -386,8 +427,9 @@ cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, /* Set an attributes option. */ struct options_entry * -cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) +cmd_set_option_attributes(__unused struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) { int attr; @@ -401,8 +443,9 @@ cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, /* Set a flag option. */ struct options_entry * -cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *oe, struct options *oo, const char *value) +cmd_set_option_flag(__unused struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) { int flag; @@ -428,29 +471,55 @@ cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, /* Set a choice option. */ struct options_entry * -cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_choice(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { const char **choicep; int n, choice = -1; - n = 0; - for (choicep = oe->choices; *choicep != NULL; choicep++) { - n++; - if (strncmp(*choicep, value, strlen(value)) != 0) - continue; + if (value == NULL) { + choice = options_get_number(oo, oe->name); + if (choice < 2) + choice = !choice; + } else { + n = 0; + for (choicep = oe->choices; *choicep != NULL; choicep++) { + n++; + if (strncmp(*choicep, value, strlen(value)) != 0) + continue; - if (choice != -1) { - cmdq_error(cmdq, "ambiguous value: %s", value); + if (choice != -1) { + cmdq_error(cmdq, "ambiguous value: %s", value); + return (NULL); + } + choice = n - 1; + } + if (choice == -1) { + cmdq_error(cmdq, "unknown value: %s", value); return (NULL); } - choice = n - 1; - } - if (choice == -1) { - cmdq_error(cmdq, "unknown value: %s", value); - return (NULL); } return (options_set_number(oo, oe->name, choice)); } + +/* Set a style option. */ +struct options_entry * +cmd_set_option_style(struct cmd *self, struct cmd_q *cmdq, + const struct options_table_entry *oe, struct options *oo, + const char *value) +{ + struct args *args = self->args; + struct options_entry *o; + int append; + + append = args_has(args, 'a'); + if ((o = options_set_style(oo, oe->name, value, append)) == NULL) { + cmdq_error(cmdq, "bad style: %s", value); + return (NULL); + } + + style_update_old(oo, oe->name, &o->style); + return (o); +} diff --git a/cmd-show-environment.c b/cmd-show-environment.c index ffe98bcc..54baafe4 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -27,33 +27,77 @@ * Show environment. */ -enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); + +char *cmd_show_environment_escape(struct environ_entry *); +void cmd_show_environment_print(struct cmd *, struct cmd_q *, + struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { - "show-environment", "showenv", - "gt:", 0, 1, - "[-g] " CMD_TARGET_SESSION_USAGE " [name]", - 0, - NULL, - NULL, - cmd_show_environment_exec + .name = "show-environment", + .alias = "showenv", + + .args = { "gst:", 0, 1 }, + .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", + + .tflag = CMD_SESSION_CANFAIL, + + .flags = 0, + .exec = cmd_show_environment_exec }; +char * +cmd_show_environment_escape(struct environ_entry *envent) +{ + const char *value = envent->value; + char c, *out, *ret; + + out = ret = xmalloc(strlen(value) * 2 + 1); /* at most twice the size */ + while ((c = *value++) != '\0') { + /* POSIX interprets $ ` " and \ in double quotes. */ + if (c == '$' || c == '`' || c == '"' || c == '\\') + *out++ = '\\'; + *out++ = c; + } + *out = '\0'; + + return (ret); +} + +void +cmd_show_environment_print(struct cmd *self, struct cmd_q *cmdq, + struct environ_entry *envent) +{ + char *escaped; + + if (!args_has(self->args, 's')) { + if (envent->value != NULL) + cmdq_print(cmdq, "%s=%s", envent->name, envent->value); + else + cmdq_print(cmdq, "-%s", envent->name); + return; + } + + if (envent->value != NULL) { + escaped = cmd_show_environment_escape(envent); + cmdq_print(cmdq, "%s=\"%s\"; export %s;", envent->name, escaped, + envent->name); + free(escaped); + } else + cmdq_print(cmdq, "unset %s;", envent->name); +} + enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct environ *env; struct environ_entry *envent; - if (args_has(self->args, 'g')) - env = &global_environ; - else { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - env = &s->environ; - } + if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) + env = global_environ; + else + env = cmdq->state.tflag.s->environ; if (args->argc != 0) { envent = environ_find(env, args->argv[0]); @@ -61,19 +105,14 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "unknown variable: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - if (envent->value != NULL) - cmdq_print(cmdq, "%s=%s", envent->name, envent->value); - else - cmdq_print(cmdq, "-%s", envent->name); + cmd_show_environment_print(self, cmdq, envent); return (CMD_RETURN_NORMAL); } - RB_FOREACH(envent, environ, env) { - if (envent->value != NULL) - cmdq_print(cmdq, "%s=%s", envent->name, envent->value); - else - cmdq_print(cmdq, "-%s", envent->name); + envent = environ_first(env); + while (envent != NULL) { + cmd_show_environment_print(self, cmdq, envent); + envent = environ_next(envent); } - return (CMD_RETURN_NORMAL); } diff --git a/cmd-show-messages.c b/cmd-show-messages.c index bc2424ad..c80ccf16 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" @@ -30,30 +31,94 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { - "show-messages", "showmsgs", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - 0, - NULL, - NULL, - cmd_show_messages_exec + .name = "show-messages", + .alias = "showmsgs", + + .args = { "JTt:", 0, 0 }, + .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, + + .tflag = CMD_CLIENT, + + .flags = 0, + .exec = cmd_show_messages_exec }; +const struct cmd_entry cmd_server_info_entry = { + .name = "server-info", + .alias = "info", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_show_messages_exec +}; + +int cmd_show_messages_terminals(struct cmd_q *, int); +int cmd_show_messages_jobs(struct cmd_q *, int); + +int +cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) +{ + struct tty_term *term; + u_int i, n; + + n = 0; + LIST_FOREACH(term, &tty_terms, entry) { + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Terminal %u: %s [references=%u, flags=0x%x]:", + n, term->name, term->references, term->flags); + n++; + for (i = 0; i < tty_term_ncodes(); i++) + cmdq_print(cmdq, "%s", tty_term_describe(term, i)); + } + return (n != 0); +} + +int +cmd_show_messages_jobs(struct cmd_q *cmdq, int blank) +{ + struct job *job; + u_int n; + + n = 0; + LIST_FOREACH(job, &all_jobs, lentry) { + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Job %u: %s [fd=%d, pid=%d, status=%d]", + n, job->cmd, job->fd, job->pid, job->status); + n++; + } + return (n != 0); +} + enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct message_entry *msg; char *tim; - u_int i; + int done, blank; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { - msg = &ARRAY_ITEM(&c->message_log, i); + done = blank = 0; + if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { + blank = cmd_show_messages_terminals(cmdq, blank); + done = 1; + } + if (args_has(args, 'J') || self->entry == &cmd_server_info_entry) { + cmd_show_messages_jobs(cmdq, blank); + done = 1; + } + if (done) + return (CMD_RETURN_NORMAL); + TAILQ_FOREACH(msg, &c->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; diff --git a/cmd-show-options.c b/cmd-show-options.c index e2f78e12..e99574f8 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -32,67 +32,65 @@ enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_show_options_one(struct cmd *, struct cmd_q *, struct options *, int); enum cmd_retval cmd_show_options_all(struct cmd *, struct cmd_q *, - const struct options_table_entry *, struct options *); + struct options *, enum options_table_scope); const struct cmd_entry cmd_show_options_entry = { - "show-options", "show", - "gqst:vw", 0, 1, - "[-gqsvw] [-t target-session|target-window] [option]", - 0, - NULL, - NULL, - cmd_show_options_exec + .name = "show-options", + .alias = "show", + + .args = { "gqst:vw", 0, 1 }, + .usage = "[-gqsvw] [-t target-session|target-window] [option]", + + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, + .exec = cmd_show_options_exec }; const struct cmd_entry cmd_show_window_options_entry = { - "show-window-options", "showw", - "gvt:", 0, 1, - "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - 0, - NULL, - NULL, - cmd_show_options_exec + .name = "show-window-options", + .alias = "showw", + + .args = { "gvt:", 0, 1 }, + .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", + + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, + .exec = cmd_show_options_exec }; enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - const struct options_table_entry *table; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; struct options *oo; + enum options_table_scope scope; int quiet; if (args_has(self->args, 's')) { - oo = &global_options; - table = server_options_table; + oo = global_options; + scope = OPTIONS_TABLE_SERVER; } else if (args_has(self->args, 'w') || self->entry == &cmd_show_window_options_entry) { - table = window_options_table; + scope = OPTIONS_TABLE_WINDOW; if (args_has(self->args, 'g')) - oo = &global_w_options; - else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); - oo = &wl->window->options; - } + oo = global_w_options; + else + oo = wl->window->options; } else { - table = session_options_table; + scope = OPTIONS_TABLE_SESSION; if (args_has(self->args, 'g')) - oo = &global_s_options; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - oo = &s->options; - } + oo = global_s_options; + else + oo = s->options; } quiet = args_has(self->args, 'q'); if (args->argc == 0) - return (cmd_show_options_all(self, cmdq, table, oo)); + return (cmd_show_options_all(self, cmdq, oo, scope)); else return (cmd_show_options_one(self, cmdq, oo, quiet)); } @@ -102,15 +100,17 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, struct options *oo, int quiet) { struct args *args = self->args; - const struct options_table_entry *table, *oe; + const char *name = args->argv[0]; + const struct options_table_entry *oe; struct options_entry *o; const char *optval; - if (*args->argv[0] == '@') { - if ((o = options_find1(oo, args->argv[0])) == NULL) { +retry: + if (*name == '@') { + if ((o = options_find1(oo, name)) == NULL) { if (quiet) return (CMD_RETURN_NORMAL); - cmdq_error(cmdq, "unknown option: %s", args->argv[0]); + cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'v')) @@ -120,17 +120,21 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, return (CMD_RETURN_NORMAL); } - table = oe = NULL; - if (options_table_find(args->argv[0], &table, &oe) != 0) { - cmdq_error(cmdq, "ambiguous option: %s", args->argv[0]); + oe = NULL; + if (options_table_find(name, &oe) != 0) { + cmdq_error(cmdq, "ambiguous option: %s", name); return (CMD_RETURN_ERROR); } if (oe == NULL) { if (quiet) - return (CMD_RETURN_NORMAL); - cmdq_error(cmdq, "unknown option: %s", args->argv[0]); + return (CMD_RETURN_NORMAL); + cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } + if (oe->style != NULL) { + name = oe->style; + goto retry; + } if ((o = options_find1(oo, oe->name)) == NULL) return (CMD_RETURN_NORMAL); optval = options_table_print_entry(oe, o, args_has(self->args, 'v')); @@ -142,28 +146,33 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, } enum cmd_retval -cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *table, struct options *oo) +cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, struct options *oo, + enum options_table_scope scope) { const struct options_table_entry *oe; struct options_entry *o; const char *optval; + int vflag; - RB_FOREACH(o, options_tree, &oo->tree) { + o = options_first(oo); + while (o != NULL) { if (*o->name == '@') { if (args_has(self->args, 'v')) cmdq_print(cmdq, "%s", o->str); else cmdq_print(cmdq, "%s \"%s\"", o->name, o->str); } + o = options_next(o); } - for (oe = table; oe->name != NULL; oe++) { + vflag = args_has(self->args, 'v'); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->style != NULL || oe->scope != scope) + continue; if ((o = options_find1(oo, oe->name)) == NULL) continue; - optval = options_table_print_entry(oe, o, - args_has(self->args, 'v')); - if (args_has(self->args, 'v')) + optval = options_table_print_entry(oe, o, vflag); + if (vflag) cmdq_print(cmdq, "%s", optval); else cmdq_print(cmdq, "%s %s", oe->name, optval); diff --git a/cmd-source-file.c b/cmd-source-file.c index 827d4c00..9d2d6d68 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Tiago Cunha @@ -28,17 +28,17 @@ enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *); -void cmd_source_file_show(struct cmd_q *); void cmd_source_file_done(struct cmd_q *); const struct cmd_entry cmd_source_file_entry = { - "source-file", "source", - "", 1, 1, - "path", - 0, - NULL, - NULL, - cmd_source_file_exec + .name = "source-file", + .alias = "source", + + .args = { "", 1, 1 }, + .usage = "path", + + .flags = 0, + .exec = cmd_source_file_exec }; enum cmd_retval @@ -48,7 +48,7 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_q *cmdq1; char *cause; - cmdq1 = cmdq_new(NULL); + cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_source_file_done; cmdq1->data = cmdq; @@ -60,11 +60,12 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) free(cause); return (CMD_RETURN_ERROR); } - ARRAY_ADD(&cfg_causes, cause); + cfg_add_cause("%s", cause); + free(cause); /* FALLTHROUGH */ case 0: if (cfg_references == 0) - cmd_source_file_show(cmdq); + cfg_print_causes(cmdq); cmdq_free(cmdq1); return (CMD_RETURN_NORMAL); } @@ -76,25 +77,14 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_WAIT); } -void -cmd_source_file_show(struct cmd_q *cmdq) -{ - u_int i; - char *cause; - - for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { - cause = ARRAY_ITEM(&cfg_causes, i); - cmdq_print(cmdq, "%s", cause); - free(cause); - } - ARRAY_FREE(&cfg_causes); -} - void cmd_source_file_done(struct cmd_q *cmdq1) { struct cmd_q *cmdq = cmdq1->data; + if (cmdq1->client_exit >= 0) + cmdq->client_exit = cmdq1->client_exit; + cmdq_free(cmdq1); cfg_references--; @@ -103,6 +93,6 @@ cmd_source_file_done(struct cmd_q *cmdq1) return; if (cfg_references == 0) - cmd_source_file_show(cmdq); + cfg_print_causes(cmdq); cmdq_continue(cmdq); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 601dcb17..84b220fd 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -18,7 +18,10 @@ #include +#include +#include #include +#include #include #include "tmux.h" @@ -27,63 +30,73 @@ * Split a window (add a new pane). */ -void cmd_split_window_key_binding(struct cmd *, int); +#define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" + enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { - "split-window", "splitw", - "c:dF:l:hp:Pt:v", 0, 1, - "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " - CMD_TARGET_PANE_USAGE " [command]", - 0, - cmd_split_window_key_binding, - NULL, - cmd_split_window_exec -}; + .name = "split-window", + .alias = "splitw", -void -cmd_split_window_key_binding(struct cmd *self, int key) -{ - self->args = args_create(0); - if (key == '%') - args_set(self->args, 'h', NULL); -} + .args = { "bc:dF:l:hp:Pt:v", 0, -1 }, + .usage = "[-bdhvP] [-c start-directory] [-F format] " + "[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_split_window_exec +}; enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp, *new_wp = NULL; - struct environ env; - const char *cmd, *cwd, *shell; - char *cause, *new_cause; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct window_pane *wp = cmdq->state.tflag.wp, *new_wp = NULL; + struct environ *env; + const char *cmd, *path, *shell, *template, *cwd, *to_free; + char **argv, *cause, *new_cause, *cp; u_int hlimit; - int size, percentage; + int argc, size, percentage; enum layout_type type; struct layout_cell *lc; - const char *template; - struct client *c; struct format_tree *ft; - char *cp; + struct environ_entry *envent; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; server_unzoom_window(w); - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); - if (args->argc == 0) - cmd = options_get_string(&s->options, "default-command"); + if (args->argc == 0) { + cmd = options_get_string(s->options, "default-command"); + if (cmd != NULL && *cmd != '\0') { + argc = 1; + argv = (char **)&cmd; + } else { + argc = 0; + argv = NULL; + } + } else { + argc = args->argc; + argv = args->argv; + } + + to_free = NULL; + if (args_has(args, 'c')) { + ft = format_create(cmdq, 0); + format_defaults(ft, cmdq->state.c, s, NULL, NULL); + to_free = cwd = format_expand(ft, args_get(args, 'c')); + format_free(ft); + } else if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; else - cmd = args->argv[0]; - cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); + cwd = s->cwd; type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) @@ -111,22 +124,32 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) else size = (wp->sx * percentage) / 100; } - hlimit = options_get_number(&s->options, "history-limit"); + hlimit = options_get_number(s->options, "history-limit"); - shell = options_get_string(&s->options, "default-shell"); + shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - if ((lc = layout_split_pane(wp, type, size, 0)) == NULL) { + lc = layout_split_pane(wp, type, size, args_has(args, 'b')); + if (lc == NULL) { cause = xstrdup("pane too small"); goto error; } new_wp = window_add_pane(w, hlimit); - if (window_pane_spawn( - new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0) - goto error; layout_assign_pane(lc, new_wp); + path = NULL; + if (cmdq->client != NULL && cmdq->client->session == NULL) + envent = environ_find(cmdq->client->environ, "PATH"); + else + envent = environ_find(s->environ, "PATH"); + if (envent != NULL) + path = envent->value; + + if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env, + s->tio, &cause) != 0) + goto error; + server_redraw_window(w); if (!args_has(args, 'd')) { @@ -136,18 +159,14 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } else server_status_session(s); - environ_free(&env); + environ_free(env); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; - ft = format_create(); - if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) - format_client(ft, c); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, new_wp); + ft = format_create(cmdq, 0); + format_defaults(ft, cmdq->state.c, s, wl, new_wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); @@ -156,13 +175,21 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } notify_window_layout_changed(w); + + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - environ_free(&env); - if (new_wp != NULL) + environ_free(env); + if (new_wp != NULL) { + layout_close_pane(new_wp); window_remove_pane(w, new_wp); + } cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); + + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/cmd-string.c b/cmd-string.c index 7e84eda6..51554800 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -107,10 +107,11 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, case ' ': case '\t': if (buf != NULL) { - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); buf[len] = '\0'; - argv = xrealloc(argv, argc + 1, sizeof *argv); + argv = xreallocarray(argv, argc + 1, + sizeof *argv); argv[argc++] = buf; buf = NULL; @@ -125,7 +126,7 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, whitespace = argv[0] + strcspn(argv[0], " \t"); if (equals == NULL || equals > whitespace) break; - environ_put(&global_environ, argv[0]); + environ_put(global_environ, argv[0]); argc--; memmove(argv, argv + 1, argc * (sizeof *argv)); } @@ -151,7 +152,7 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, if (len >= SIZE_MAX - 2) goto error; - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); buf[len++] = ch; break; } @@ -179,7 +180,7 @@ cmd_string_copy(char **dst, char *src, size_t *len) srclen = strlen(src); - *dst = xrealloc(*dst, 1, *len + srclen + 1); + *dst = xrealloc(*dst, *len + srclen + 1); strlcpy(*dst + *len, src, srclen + 1); *len += srclen; @@ -231,11 +232,11 @@ cmd_string_string(const char *s, size_t *p, char endch, int esc) if (len >= SIZE_MAX - 2) goto error; - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); buf[len++] = ch; } - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); buf[len] = '\0'; return (buf); @@ -278,7 +279,7 @@ cmd_string_variable(const char *s, size_t *p) return (t); } - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); buf[len++] = ch; for (;;) { @@ -288,7 +289,7 @@ cmd_string_variable(const char *s, size_t *p) else { if (len >= SIZE_MAX - 3) goto error; - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); buf[len++] = ch; } } @@ -299,10 +300,10 @@ cmd_string_variable(const char *s, size_t *p) if (ch != EOF && fch != '{') cmd_string_ungetc(p); /* ch */ - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); buf[len] = '\0'; - envent = environ_find(&global_environ, buf); + envent = environ_find(global_environ, buf); free(buf); if (envent == NULL) return (xstrdup("")); @@ -318,26 +319,41 @@ cmd_string_expand_tilde(const char *s, size_t *p) { struct passwd *pw; struct environ_entry *envent; - char *home, *path, *username; + char *home, *path, *user, *cp; + int last; home = NULL; - if (cmd_string_getc(s, p) == '/') { - envent = environ_find(&global_environ, "HOME"); + + last = cmd_string_getc(s, p); + if (last == EOF || last == '/' || last == ' '|| last == '\t') { + envent = environ_find(global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') home = envent->value; else if ((pw = getpwuid(getuid())) != NULL) home = pw->pw_dir; } else { cmd_string_ungetc(p); - if ((username = cmd_string_string(s, p, '/', 0)) == NULL) - return (NULL); - if ((pw = getpwnam(username)) != NULL) + + cp = user = xmalloc(strlen(s)); + for (;;) { + last = cmd_string_getc(s, p); + if (last == EOF || last == '/' || last == ' '|| last == '\t') + break; + *cp++ = last; + } + *cp = '\0'; + + if ((pw = getpwnam(user)) != NULL) home = pw->pw_dir; - free(username); + free(user); } + if (home == NULL) return (NULL); - xasprintf(&path, "%s/", home); + if (last != EOF) + xasprintf(&path, "%s%c", home, last); + else + xasprintf(&path, "%s", home); return (path); } diff --git a/cmd-suspend-client.c b/cmd-suspend-client.c deleted file mode 100644 index 101658b1..00000000 --- a/cmd-suspend-client.c +++ /dev/null @@ -1,56 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * 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 - -#include -#include - -#include "tmux.h" - -/* - * Suspend client with SIGTSTP. - */ - -enum cmd_retval cmd_suspend_client_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_suspend_client_entry = { - "suspend-client", "suspendc", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - 0, - NULL, - NULL, - cmd_suspend_client_exec -}; - -enum cmd_retval -cmd_suspend_client_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - struct client *c; - - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - tty_stop_tty(&c->tty); - c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_SUSPEND, NULL, 0); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 6990b5d4..76090520 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -26,62 +26,51 @@ * Swap two panes. */ -void cmd_swap_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_pane_entry = { - "swap-pane", "swapp", - "dDs:t:U", 0, 0, - "[-dDU] " CMD_SRCDST_PANE_USAGE, - 0, - cmd_swap_pane_key_binding, - NULL, - cmd_swap_pane_exec -}; + .name = "swap-pane", + .alias = "swapp", -void -cmd_swap_pane_key_binding(struct cmd *self, int key) -{ - self->args = args_create(0); - if (key == '{') - args_set(self->args, 'U', NULL); - else if (key == '}') - args_set(self->args, 'D', NULL); -} + .args = { "dDs:t:U", 0, 0 }, + .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, + + .sflag = CMD_PANE_MARKED, + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_swap_pane_exec +}; enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *src_wl, *dst_wl; + struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &dst_wp); - if (dst_wl == NULL) - return (CMD_RETURN_ERROR); + dst_wl = cmdq->state.tflag.wl; dst_w = dst_wl->window; + dst_wp = cmdq->state.tflag.wp; + src_wl = cmdq->state.sflag.wl; + src_w = src_wl->window; + src_wp = cmdq->state.sflag.wp; server_unzoom_window(dst_w); - if (!args_has(args, 's')) { + if (args_has(self->args, 'D')) { + src_wl = dst_wl; src_w = dst_w; - if (args_has(self->args, 'D')) { - src_wp = TAILQ_NEXT(dst_wp, entry); - if (src_wp == NULL) - src_wp = TAILQ_FIRST(&dst_w->panes); - } else if (args_has(self->args, 'U')) { - src_wp = TAILQ_PREV(dst_wp, window_panes, entry); - if (src_wp == NULL) - src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - } else - return (CMD_RETURN_NORMAL); - } else { - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); - src_w = src_wl->window; + src_wp = TAILQ_NEXT(dst_wp, entry); + if (src_wp == NULL) + src_wp = TAILQ_FIRST(&dst_w->panes); + } else if (args_has(self->args, 'U')) { + src_wl = dst_wl; + src_w = dst_w; + src_wp = TAILQ_PREV(dst_wp, window_panes, entry); + if (src_wp == NULL) + src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } server_unzoom_window(src_w); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index a4eadfd9..2eb509ec 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -29,13 +29,17 @@ enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_window_entry = { - "swap-window", "swapw", - "ds:t:", 0, 0, - "[-d] " CMD_SRCDST_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_swap_window_exec + .name = "swap-window", + .alias = "swapw", + + .args = { "ds:t:", 0, 0 }, + .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, + + .sflag = CMD_WINDOW_MARKED, + .tflag = CMD_WINDOW, + + .flags = 0, + .exec = cmd_swap_window_exec }; enum cmd_retval @@ -45,24 +49,21 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "swap window is not supported with tmate"); return (CMD_RETURN_ERROR); #else - struct args *args = self->args; - const char *target_src, *target_dst; struct session *src, *dst; struct session_group *sg_src, *sg_dst; struct winlink *wl_src, *wl_dst; struct window *w; - target_src = args_get(args, 's'); - if ((wl_src = cmd_find_window(cmdq, target_src, &src)) == NULL) - return (CMD_RETURN_ERROR); - target_dst = args_get(args, 't'); - if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) - return (CMD_RETURN_ERROR); - + wl_src = cmdq->state.sflag.wl; + src = cmdq->state.sflag.s; sg_src = session_group_find(src); + + wl_dst = cmdq->state.tflag.wl; + dst = cmdq->state.tflag.s; sg_dst = session_group_find(dst); - if (src != dst && - sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) { + + if (src != dst && sg_src != NULL && sg_dst != NULL && + sg_src == sg_dst) { cmdq_error(cmdq, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 9adb2146..0f6acbf4 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,57 +27,50 @@ * Switch client to a different session. */ -void cmd_switch_client_key_binding(struct cmd *, int); enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { - "switch-client", "switchc", - "lc:npt:r", 0, 0, - "[-lnpr] [-c target-client] [-t target-session]", - CMD_READONLY, - cmd_switch_client_key_binding, - NULL, - cmd_switch_client_exec -}; + .name = "switch-client", + .alias = "switchc", -void -cmd_switch_client_key_binding(struct cmd *self, int key) -{ - self->args = args_create(0); - switch (key) { - case '(': - args_set(self->args, 'p', NULL); - break; - case ')': - args_set(self->args, 'n', NULL); - break; - case 'L': - args_set(self->args, 'l', NULL); - break; - } -} + .args = { "lc:Enpt:rT:", 0, 0 }, + .usage = "[-Elnpr] [-c target-client] [-t target-session] " + "[-T key-table]", + + .cflag = CMD_CLIENT, + .tflag = CMD_SESSION_WITHPANE, + + .flags = CMD_READONLY, + .exec = cmd_switch_client_exec +}; enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - struct session *s; + struct args *args = self->args; + struct cmd_state *state = &cmdq->state; + struct client *c = state->c; + struct session *s = cmdq->state.tflag.s; + struct window_pane *wp; + const char *tablename, *update; + struct key_table *table; - if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); + if (args_has(args, 'r')) + c->flags ^= CLIENT_READONLY; - if (args_has(args, 'r')) { - if (c->flags & CLIENT_READONLY) { - c->flags &= ~CLIENT_READONLY; - cmdq_info(cmdq, "made client writable"); - } else { - c->flags |= CLIENT_READONLY; - cmdq_info(cmdq, "made client read-only"); + tablename = args_get(args, 'T'); + if (tablename != NULL) { + table = key_bindings_get_table(tablename, 0); + if (table == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); } + table->references++; + key_bindings_unref_table(c->keytable); + c->keytable = table; + return (CMD_RETURN_NORMAL); } - s = NULL; if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(cmdq, "can't find next session"); @@ -91,24 +84,42 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } else if (args_has(args, 'l')) { if (c->last_session != NULL && session_alive(c->last_session)) s = c->last_session; + else + s = NULL; if (s == NULL) { cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } - } else - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + if (cmdq->client == NULL) + return (CMD_RETURN_NORMAL); - if (c->session != NULL) + s = state->tflag.s; + if (state->tflag.wl != NULL) { + wp = state->tflag.wp; + if (wp != NULL) + window_set_active_pane(wp->window, wp); + session_set_current(s, state->tflag.wl); + } + } + + if (c != NULL && !args_has(args, 'E')) { + update = options_get_string(s->options, "update-environment"); + environ_update(update, c->environ, s->environ); + } + + if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; - session_update_activity(s); + server_client_set_key_table(c, NULL); + status_timer_start(c); + session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); recalculate_sizes(); server_check_unattached(); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + alerts_check_session(s); return (CMD_RETURN_NORMAL); } diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index dc037dde..8e89f21a 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -26,65 +26,80 @@ * Unbind key from command. */ -enum cmd_retval cmd_unbind_key_check(struct args *); -enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, + key_code); const struct cmd_entry cmd_unbind_key_entry = { - "unbind-key", "unbind", - "acnt:", 0, 1, - "[-acn] [-t key-table] key", - 0, - NULL, - cmd_unbind_key_check, - cmd_unbind_key_exec -}; + .name = "unbind-key", + .alias = "unbind", -enum cmd_retval -cmd_unbind_key_check(struct args *args) -{ - if (args_has(args, 'a') && args->argc != 0) - return (CMD_RETURN_ERROR); - if (!args_has(args, 'a') && args->argc != 1) - return (CMD_RETURN_ERROR); - return (CMD_RETURN_NORMAL); -} + .args = { "acnt:T:", 0, 1 }, + .usage = "[-acn] [-t mode-table] [-T key-table] key", + + .flags = 0, + .exec = cmd_unbind_key_exec +}; enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct key_binding *bd; - int key; + struct args *args = self->args; + key_code key; + const char *tablename; if (!args_has(args, 'a')) { + if (args->argc != 1) { + cmdq_error(cmdq, "missing key"); + return (CMD_RETURN_ERROR); + } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - } else - key = KEYC_NONE; + } else { + if (args->argc != 0) { + cmdq_error(cmdq, "key given with -a"); + return (CMD_RETURN_ERROR); + } + key = KEYC_UNKNOWN; + } if (args_has(args, 't')) - return (cmd_unbind_key_table(self, cmdq, key)); + return (cmd_unbind_key_mode_table(self, cmdq, key)); - if (key == KEYC_NONE) { - while (!RB_EMPTY(&key_bindings)) { - bd = RB_ROOT(&key_bindings); - key_bindings_remove(bd->key); + if (key == KEYC_UNKNOWN) { + tablename = args_get(args, 'T'); + if (tablename == NULL) { + key_bindings_remove_table("root"); + key_bindings_remove_table("prefix"); + return (CMD_RETURN_NORMAL); } + if (key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + key_bindings_remove_table(tablename); return (CMD_RETURN_NORMAL); } - if (!args_has(args, 'n')) - key |= KEYC_PREFIX; - key_bindings_remove(key); + if (args_has(args, 'T')) { + tablename = args_get(args, 'T'); + if (key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + } else if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; + key_bindings_remove(tablename, key); return (CMD_RETURN_NORMAL); } enum cmd_retval -cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) { struct args *args = self->args; const char *tablename; @@ -97,7 +112,7 @@ cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) return (CMD_RETURN_ERROR); } - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { while (!RB_EMPTY(mtab->tree)) { mbind = RB_ROOT(mtab->tree); RB_REMOVE(mode_key_tree, mtab->tree, mbind); diff --git a/cmd-unlink-window.c b/cmd-unlink-window.c deleted file mode 100644 index 46e9df36..00000000 --- a/cmd-unlink-window.c +++ /dev/null @@ -1,75 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * 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 - -#include "tmux.h" - -/* - * Unlink a window, unless it would be destroyed by doing so (only one link). - */ - -enum cmd_retval cmd_unlink_window_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_unlink_window_entry = { - "unlink-window", "unlinkw", - "kt:", 0, 0, - "[-k] " CMD_TARGET_WINDOW_USAGE, - 0, - NULL, - NULL, - cmd_unlink_window_exec -}; - -enum cmd_retval -cmd_unlink_window_exec(struct cmd *self, struct cmd_q *cmdq) -{ -#ifdef TMATE - cmdq_error(cmdq, "unlink window is not supported with tmate"); - return (CMD_RETURN_ERROR); -#else - struct args *args = self->args; - struct winlink *wl; - struct window *w; - struct session *s, *s2; - struct session_group *sg; - u_int references; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - - sg = session_group_find(s); - if (sg != NULL) { - references = 0; - TAILQ_FOREACH(s2, &sg->sessions, gentry) - references++; - } else - references = 1; - - if (!args_has(self->args, 'k') && w->references == references) { - cmdq_error(cmdq, "window is only linked to one session"); - return (CMD_RETURN_ERROR); - } - - server_unlink_window(s, wl); - recalculate_sizes(); - - return (CMD_RETURN_NORMAL); -#endif -} diff --git a/cmd-wait-for.c b/cmd-wait-for.c index ea36255d..5e23a1b0 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2013 Nicholas Marriott @@ -32,18 +32,20 @@ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_wait_for_entry = { - "wait-for", "wait", - "LSU", 1, 1, - "[-LSU] channel", - 0, - NULL, - NULL, - cmd_wait_for_exec + .name = "wait-for", + .alias = "wait", + + .args = { "LSU", 1, 1 }, + .usage = "[-L|-S|-U] channel", + + .flags = 0, + .exec = cmd_wait_for_exec }; struct wait_channel { const char *name; int locked; + int woken; TAILQ_HEAD(, cmd_q) waiters; TAILQ_HEAD(, cmd_q) lockers; @@ -72,6 +74,46 @@ enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, struct wait_channel *); +struct wait_channel *cmd_wait_for_add(const char *); +void cmd_wait_for_remove(struct wait_channel *wc); + +struct wait_channel * +cmd_wait_for_add(const char *name) +{ + struct wait_channel *wc; + + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + + wc->locked = 0; + wc->woken = 0; + + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + + RB_INSERT(wait_channels, &wait_channels, wc); + + log_debug("add wait channel %s", wc->name); + + return (wc); +} + +void +cmd_wait_for_remove(struct wait_channel *wc) +{ + if (wc->locked) + return; + if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) + return; + + log_debug("remove wait channel %s", wc->name); + + RB_REMOVE(wait_channels, &wait_channels, wc); + + free((void *)wc->name); + free(wc); +} + enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -92,15 +134,20 @@ cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, +cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq, *wq1; - if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { - cmdq_error(cmdq, "no waiting clients on %s", name); - return (CMD_RETURN_ERROR); + if (wc == NULL) + wc = cmd_wait_for_add(name); + + if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { + log_debug("signal wait channel %s, no waiters", wc->name); + wc->woken = 1; + return (CMD_RETURN_NORMAL); } + log_debug("signal wait channel %s, with waiters", wc->name); TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { TAILQ_REMOVE(&wc->waiters, wq, waitentry); @@ -108,12 +155,7 @@ cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, cmdq_continue(wq); } - if (!wc->locked) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); - free(wc); - } - + cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } @@ -148,26 +190,27 @@ enum cmd_retval cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { + struct client *c = cmdq->client; #ifdef TMATE if (!strcmp(name, "tmate-ready") && tmate_session.decoder.ready) return (CMD_RETURN_NORMAL); #endif - - if (cmdq->client == NULL || cmdq->client->session != NULL) { + if (c == NULL || c->session != NULL) { cmdq_error(cmdq, "not able to wait"); return (CMD_RETURN_ERROR); } - if (wc == NULL) { - wc = xmalloc(sizeof *wc); - wc->name = xstrdup(name); - wc->locked = 0; - TAILQ_INIT(&wc->waiters); - TAILQ_INIT(&wc->lockers); - RB_INSERT(wait_channels, &wait_channels, wc); + if (wc == NULL) + wc = cmd_wait_for_add(name); + + if (wc->woken) { + log_debug("wait channel %s already woken (%p)", wc->name, c); + cmd_wait_for_remove(wc); + return (CMD_RETURN_NORMAL); } + log_debug("wait channel %s not woken (%p)", wc->name, c); TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; @@ -184,14 +227,8 @@ cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, return (CMD_RETURN_ERROR); } - if (wc == NULL) { - wc = xmalloc(sizeof *wc); - wc->name = xstrdup(name); - wc->locked = 0; - TAILQ_INIT(&wc->waiters); - TAILQ_INIT(&wc->lockers); - RB_INSERT(wait_channels, &wait_channels, wc); - } + if (wc == NULL) + wc = cmd_wait_for_add(name); if (wc->locked) { TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); @@ -220,13 +257,31 @@ cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, cmdq_continue(wq); } else { wc->locked = 0; - if (TAILQ_EMPTY(&wc->waiters)) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void*) wc->name); - free(wc); - } + cmd_wait_for_remove(wc); } return (CMD_RETURN_NORMAL); } +void +cmd_wait_for_flush(void) +{ + struct wait_channel *wc, *wc1; + struct cmd_q *wq, *wq1; + + RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + wc->woken = 1; + TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) { + TAILQ_REMOVE(&wc->lockers, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + wc->locked = 0; + cmd_wait_for_remove(wc); + } +} diff --git a/cmd.c b/cmd.c index eecac462..4b31b4b3 100644 --- a/cmd.c +++ b/cmd.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,6 +27,95 @@ #include "tmux.h" +extern const struct cmd_entry cmd_attach_session_entry; +extern const struct cmd_entry cmd_bind_key_entry; +extern const struct cmd_entry cmd_break_pane_entry; +extern const struct cmd_entry cmd_capture_pane_entry; +extern const struct cmd_entry cmd_choose_buffer_entry; +extern const struct cmd_entry cmd_choose_client_entry; +extern const struct cmd_entry cmd_choose_session_entry; +extern const struct cmd_entry cmd_choose_tree_entry; +extern const struct cmd_entry cmd_choose_window_entry; +extern const struct cmd_entry cmd_clear_history_entry; +extern const struct cmd_entry cmd_clock_mode_entry; +extern const struct cmd_entry cmd_command_prompt_entry; +extern const struct cmd_entry cmd_confirm_before_entry; +extern const struct cmd_entry cmd_copy_mode_entry; +extern const struct cmd_entry cmd_delete_buffer_entry; +extern const struct cmd_entry cmd_detach_client_entry; +extern const struct cmd_entry cmd_display_message_entry; +extern const struct cmd_entry cmd_display_panes_entry; +extern const struct cmd_entry cmd_down_pane_entry; +extern const struct cmd_entry cmd_find_window_entry; +extern const struct cmd_entry cmd_has_session_entry; +extern const struct cmd_entry cmd_if_shell_entry; +extern const struct cmd_entry cmd_join_pane_entry; +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_session_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_link_window_entry; +extern const struct cmd_entry cmd_list_buffers_entry; +extern const struct cmd_entry cmd_list_clients_entry; +extern const struct cmd_entry cmd_list_commands_entry; +extern const struct cmd_entry cmd_list_keys_entry; +extern const struct cmd_entry cmd_list_panes_entry; +extern const struct cmd_entry cmd_list_sessions_entry; +extern const struct cmd_entry cmd_list_windows_entry; +extern const struct cmd_entry cmd_load_buffer_entry; +extern const struct cmd_entry cmd_lock_client_entry; +extern const struct cmd_entry cmd_lock_server_entry; +extern const struct cmd_entry cmd_lock_session_entry; +extern const struct cmd_entry cmd_move_pane_entry; +extern const struct cmd_entry cmd_move_window_entry; +extern const struct cmd_entry cmd_new_session_entry; +extern const struct cmd_entry cmd_new_window_entry; +extern const struct cmd_entry cmd_next_layout_entry; +extern const struct cmd_entry cmd_next_window_entry; +extern const struct cmd_entry cmd_paste_buffer_entry; +extern const struct cmd_entry cmd_pipe_pane_entry; +extern const struct cmd_entry cmd_previous_layout_entry; +extern const struct cmd_entry cmd_previous_window_entry; +extern const struct cmd_entry cmd_refresh_client_entry; +extern const struct cmd_entry cmd_rename_session_entry; +extern const struct cmd_entry cmd_rename_window_entry; +extern const struct cmd_entry cmd_resize_pane_entry; +extern const struct cmd_entry cmd_respawn_pane_entry; +extern const struct cmd_entry cmd_respawn_window_entry; +extern const struct cmd_entry cmd_rotate_window_entry; +extern const struct cmd_entry cmd_run_shell_entry; +extern const struct cmd_entry cmd_save_buffer_entry; +extern const struct cmd_entry cmd_select_layout_entry; +extern const struct cmd_entry cmd_select_pane_entry; +extern const struct cmd_entry cmd_select_window_entry; +extern const struct cmd_entry cmd_send_keys_entry; +extern const struct cmd_entry cmd_send_prefix_entry; +extern const struct cmd_entry cmd_server_info_entry; +extern const struct cmd_entry cmd_set_buffer_entry; +extern const struct cmd_entry cmd_set_environment_entry; +extern const struct cmd_entry cmd_set_hook_entry; +extern const struct cmd_entry cmd_set_option_entry; +extern const struct cmd_entry cmd_set_window_option_entry; +extern const struct cmd_entry cmd_show_buffer_entry; +extern const struct cmd_entry cmd_show_environment_entry; +extern const struct cmd_entry cmd_show_hooks_entry; +extern const struct cmd_entry cmd_show_messages_entry; +extern const struct cmd_entry cmd_show_options_entry; +extern const struct cmd_entry cmd_show_window_options_entry; +extern const struct cmd_entry cmd_source_file_entry; +extern const struct cmd_entry cmd_split_window_entry; +extern const struct cmd_entry cmd_start_server_entry; +extern const struct cmd_entry cmd_suspend_client_entry; +extern const struct cmd_entry cmd_swap_pane_entry; +extern const struct cmd_entry cmd_swap_window_entry; +extern const struct cmd_entry cmd_switch_client_entry; +extern const struct cmd_entry cmd_unbind_key_entry; +extern const struct cmd_entry cmd_unlink_window_entry; +extern const struct cmd_entry cmd_up_pane_entry; +extern const struct cmd_entry cmd_wait_for_entry; + const struct cmd_entry *cmd_table[] = { &cmd_attach_session_entry, &cmd_bind_key_entry, @@ -34,7 +123,6 @@ const struct cmd_entry *cmd_table[] = { &cmd_capture_pane_entry, &cmd_choose_buffer_entry, &cmd_choose_client_entry, - &cmd_choose_list_entry, &cmd_choose_session_entry, &cmd_choose_tree_entry, &cmd_choose_window_entry, @@ -96,10 +184,12 @@ const struct cmd_entry *cmd_table[] = { &cmd_server_info_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, + &cmd_set_hook_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, &cmd_show_buffer_entry, &cmd_show_environment_entry, + &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, &cmd_show_window_options_entry, @@ -116,30 +206,15 @@ const struct cmd_entry *cmd_table[] = { NULL }; -int cmd_session_better(struct session *, struct session *, int); -struct session *cmd_choose_session_list(struct sessionslist *); -struct session *cmd_choose_session(int); -struct client *cmd_choose_client(struct clients *); -struct client *cmd_lookup_client(const char *); -struct session *cmd_lookup_session(const char *, int *); -struct session *cmd_lookup_session_id(const char *); -struct winlink *cmd_lookup_window(struct session *, const char *, int *); -int cmd_lookup_index(struct session *, const char *, int *); -struct window_pane *cmd_lookup_paneid(const char *); -struct winlink *cmd_lookup_winlink_windowid(struct session *, const char *); -struct window *cmd_lookup_windowid(const char *); -struct session *cmd_window_session(struct cmd_q *, struct window *, - struct winlink **); -struct winlink *cmd_find_window_offset(const char *, struct session *, int *); -int cmd_find_index_offset(const char *, struct session *, int *); -struct window_pane *cmd_find_pane_offset(const char *, struct winlink *); - int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { size_t arglen; int i; + if (argc == 0) + return (0); + *buf = '\0'; for (i = 0; i < argc; i++) { if (strlcpy(buf, argv[i], len) >= len) @@ -179,14 +254,14 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) } char ** -cmd_copy_argv(int argc, char *const *argv) +cmd_copy_argv(int argc, char **argv) { char **new_argv; int i; if (argc == 0) return (NULL); - new_argv = xcalloc(argc, sizeof *new_argv); + new_argv = xcalloc(argc + 1, sizeof *new_argv); for (i = 0; i < argc; i++) { if (argv[i] != NULL) new_argv[i] = xstrdup(argv[i]); @@ -206,6 +281,32 @@ cmd_free_argv(int argc, char **argv) free(argv); } +char * +cmd_stringify_argv(int argc, char **argv) +{ + char *buf; + int i; + size_t len; + + if (argc == 0) + return (xstrdup("")); + + len = 0; + buf = NULL; + + for (i = 0; i < argc; i++) { + len += strlen(argv[i]) + 1; + buf = xrealloc(buf, len); + + if (i == 0) + *buf = '\0'; + else + strlcat(buf, " ", len); + strlcat(buf, argv[i], len); + } + return (buf); +} + struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { @@ -247,14 +348,12 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) return (NULL); } - args = args_parse(entry->args_template, argc, argv); + args = args_parse(entry->args.template, argc, argv); if (args == NULL) goto usage; - if (entry->args_lower != -1 && args->argc < entry->args_lower) + if (entry->args.lower != -1 && args->argc < entry->args.lower) goto usage; - if (entry->args_upper != -1 && args->argc > entry->args_upper) - goto usage; - if (entry->check != NULL && entry->check(args) != 0) + if (entry->args.upper != -1 && args->argc > entry->args.upper) goto usage; cmd = xcalloc(1, sizeof *cmd); @@ -288,953 +387,217 @@ usage: return (NULL); } -size_t -cmd_print(struct cmd *cmd, char *buf, size_t len) +static int +cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, + const char *target, struct cmd_q *cmdq) { - size_t off, used; + int targetflags, error; - off = xsnprintf(buf, len, "%s ", cmd->entry->name); - if (off < len) { - used = args_print(cmd->args, buf + off, len - off); - if (used == 0) - off--; + if (flag == CMD_SESSION_WITHPANE) { + if (target != NULL && target[strcspn(target, ":.")] != '\0') + flag = CMD_PANE; else - off += used; - buf[off] = '\0'; + flag = CMD_SESSION; } - return (off); + + switch (flag) { + case CMD_NONE: + case CMD_CLIENT: + case CMD_CLIENT_CANFAIL: + return (0); + case CMD_SESSION: + case CMD_SESSION_CANFAIL: + case CMD_SESSION_PREFERUNATTACHED: + case CMD_SESSION_WITHPANE: + targetflags = 0; + if (flag == CMD_SESSION_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_SESSION_PREFERUNATTACHED) + targetflags |= CMD_FIND_PREFER_UNATTACHED; + + error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, + targetflags); + if (error != 0 && flag != CMD_SESSION_CANFAIL) + return (-1); + break; + case CMD_MOVEW_R: + error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, + CMD_FIND_QUIET); + if (error == 0) + break; + flag = CMD_WINDOW_INDEX; + /* FALLTHROUGH */ + case CMD_WINDOW: + case CMD_WINDOW_CANFAIL: + case CMD_WINDOW_MARKED: + case CMD_WINDOW_INDEX: + targetflags = 0; + if (flag == CMD_WINDOW_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_WINDOW_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + if (flag == CMD_WINDOW_INDEX) + targetflags |= CMD_FIND_WINDOW_INDEX; + + error = cmd_find_target(fs, cmdq, target, CMD_FIND_WINDOW, + targetflags); + if (error != 0 && flag != CMD_WINDOW_CANFAIL) + return (-1); + break; + case CMD_PANE: + case CMD_PANE_CANFAIL: + case CMD_PANE_MARKED: + targetflags = 0; + if (flag == CMD_PANE_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_PANE_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + + error = cmd_find_target(fs, cmdq, target, CMD_FIND_PANE, + targetflags); + if (error != 0 && flag != CMD_PANE_CANFAIL) + return (-1); + break; + } + return (0); } -/* - * Figure out the current session. Use: 1) the current session, if the command - * context has one; 2) the most recently used session containing the pty of the - * calling client, if any; 3) the session specified in the TMUX variable from - * the environment (as passed from the client); 4) the most recently used - * session from all sessions. - */ -struct session * -cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) -{ - struct msg_command_data *data = cmdq->msgdata; - struct client *c = cmdq->client; - struct session *s; - struct sessionslist ss; - struct winlink *wl; - struct window_pane *wp; - const char *path; - int found; - - if (c != NULL && c->session != NULL) - return (c->session); - - /* - * If the name of the calling client's pty is known, build a list of - * the sessions that contain it and if any choose either the first or - * the newest. - */ - path = c == NULL ? NULL : c->tty.path; - if (path != NULL) { - ARRAY_INIT(&ss); - RB_FOREACH(s, sessions, &sessions) { - found = 0; - RB_FOREACH(wl, winlinks, &s->windows) { - TAILQ_FOREACH(wp, &wl->window->panes, entry) { - if (strcmp(wp->tty, path) == 0) { - found = 1; - break; - } - } - if (found) - break; - } - if (found) - ARRAY_ADD(&ss, s); - } - - s = cmd_choose_session_list(&ss); - ARRAY_FREE(&ss); - if (s != NULL) - return (s); - } - - /* Use the session from the TMUX environment variable. */ - if (data != NULL && data->pid == getpid() && data->session_id != -1) { - s = session_find_by_id(data->session_id); - if (s != NULL) - return (s); - } - - return (cmd_choose_session(prefer_unattached)); -} - -/* Is this session better? */ int -cmd_session_better(struct session *s, struct session *best, - int prefer_unattached) +cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { - if (best == NULL) - return (1); - if (prefer_unattached) { - if (!(best->flags & SESSION_UNATTACHED) && - (s->flags & SESSION_UNATTACHED)) - return (1); - else if ((best->flags & SESSION_UNATTACHED) && - !(s->flags & SESSION_UNATTACHED)) - return (0); + const struct cmd_entry *entry = cmd->entry; + struct cmd_state *state = &cmdq->state; + char *tmp; + enum cmd_entry_flag flag; + const char *s; + int error; + + tmp = cmd_print(cmd); + log_debug("preparing state for %s (client %p)", tmp, cmdq->client); + free(tmp); + + state->c = NULL; + cmd_find_clear_state(&state->tflag, NULL, 0); + cmd_find_clear_state(&state->sflag, NULL, 0); + + flag = cmd->entry->cflag; + if (flag == CMD_NONE) { + flag = cmd->entry->tflag; + if (flag == CMD_CLIENT || flag == CMD_CLIENT_CANFAIL) + s = args_get(cmd->args, 't'); + else + s = NULL; + } else + s = args_get(cmd->args, 'c'); + switch (flag) { + case CMD_CLIENT: + state->c = cmd_find_client(cmdq, s, 0); + if (state->c == NULL) + return (-1); + break; + default: + state->c = cmd_find_client(cmdq, s, 1); + break; } - return (timercmp(&s->activity_time, &best->activity_time, >)); + + s = args_get(cmd->args, 't'); + log_debug("preparing -t state: target %s", s == NULL ? "none" : s); + + error = cmd_prepare_state_flag(&state->tflag, entry->tflag, s, cmdq); + if (error != 0) + return (error); + + s = args_get(cmd->args, 's'); + log_debug("preparing -s state: target %s", s == NULL ? "none" : s); + + error = cmd_prepare_state_flag(&state->sflag, entry->sflag, s, cmdq); + if (error != 0) + return (error); + + return (0); } -/* - * Find the most recently used session, preferring unattached if the flag is - * set. - */ -struct session * -cmd_choose_session(int prefer_unattached) +char * +cmd_print(struct cmd *cmd) { - struct session *s, *best; + char *out, *s; - best = NULL; - RB_FOREACH(s, sessions, &sessions) { - if (cmd_session_better(s, best, prefer_unattached)) - best = s; - } - return (best); + s = args_print(cmd->args); + if (*s != '\0') + xasprintf(&out, "%s %s", cmd->entry->name, s); + else + out = xstrdup(cmd->entry->name); + free(s); + + return (out); } -/* Find the most recently used session from a list. */ -struct session * -cmd_choose_session_list(struct sessionslist *ss) -{ - struct session *s, *sbest; - struct timeval *tv = NULL; - u_int i; - - sbest = NULL; - for (i = 0; i < ARRAY_LENGTH(ss); i++) { - if ((s = ARRAY_ITEM(ss, i)) == NULL) - continue; - - if (tv == NULL || timercmp(&s->activity_time, tv, >)) { - sbest = s; - tv = &s->activity_time; - } - } - - return (sbest); -} - -/* - * Find the current client. First try the current client if set, then pick the - * most recently used of the clients attached to the current session if any, - * then of all clients. - */ -struct client * -cmd_current_client(struct cmd_q *cmdq) -{ - struct session *s; - struct client *c; - struct clients cc; - u_int i; - - if (cmdq->client != NULL && cmdq->client->session != NULL) - return (cmdq->client); - - /* - * No current client set. Find the current session and return the - * newest of its clients. - */ - s = cmd_current_session(cmdq, 0); - if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { - ARRAY_INIT(&cc); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if ((c = ARRAY_ITEM(&clients, i)) == NULL) - continue; - if (s == c->session) - ARRAY_ADD(&cc, c); - } - - c = cmd_choose_client(&cc); - ARRAY_FREE(&cc); - if (c != NULL) - return (c); - } - - return (cmd_choose_client(&clients)); -} - -/* Choose the most recently used client from a list. */ -struct client * -cmd_choose_client(struct clients *cc) -{ - struct client *c, *cbest; - struct timeval *tv = NULL; - u_int i; - - cbest = NULL; - for (i = 0; i < ARRAY_LENGTH(cc); i++) { - if ((c = ARRAY_ITEM(cc, i)) == NULL) - continue; - if (c->session == NULL) - continue; - - if (tv == NULL || timercmp(&c->activity_time, tv, >)) { - cbest = c; - tv = &c->activity_time; - } - } - - return (cbest); -} - -/* Find the target client or report an error and return NULL. */ -struct client * -cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet) -{ - struct client *c; - char *tmparg; - size_t arglen; - - /* A NULL argument means the current client. */ - if (arg == NULL) { - c = cmd_current_client(cmdq); - if (c == NULL && !quiet) - cmdq_error(cmdq, "no clients"); - return (c); - } - tmparg = xstrdup(arg); - - /* Trim a single trailing colon if any. */ - arglen = strlen(tmparg); - if (arglen != 0 && tmparg[arglen - 1] == ':') - tmparg[arglen - 1] = '\0'; - - /* Find the client, if any. */ - c = cmd_lookup_client(tmparg); - - /* If no client found, report an error. */ - if (c == NULL && !quiet) - cmdq_error(cmdq, "client not found: %s", tmparg); - - free(tmparg); - return (c); -} - -/* - * Lookup a client by device path. Either of a full match and a match without a - * leading _PATH_DEV ("/dev/") is accepted. - */ -struct client * -cmd_lookup_client(const char *name) -{ - struct client *c; - const char *path; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL || c->tty.path == NULL) - continue; - path = c->tty.path; - - /* Check for exact matches. */ - if (strcmp(name, path) == 0) - return (c); - - /* Check without leading /dev if present. */ - if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) - continue; - if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0) - return (c); - } - - return (NULL); -} - -/* Find the target session or report an error and return NULL. */ -struct session * -cmd_lookup_session_id(const char *arg) -{ - char *endptr; - long id; - - if (arg[0] != '$') - return (NULL); - id = strtol(arg + 1, &endptr, 10); - if (arg[1] != '\0' && *endptr == '\0') - return (session_find_by_id(id)); - return (NULL); -} - -/* Lookup a session by name. If no session is found, NULL is returned. */ -struct session * -cmd_lookup_session(const char *name, int *ambiguous) -{ - struct session *s, *sfound; - - *ambiguous = 0; - - /* Look for $id first. */ - if ((s = cmd_lookup_session_id(name)) != NULL) - return (s); - - /* - * Look for matches. First look for exact matches - session names must - * be unique so an exact match can't be ambigious and can just be - * returned. - */ - if ((s = session_find(name)) != NULL) - return (s); - - /* - * Otherwise look for partial matches, returning early if it is found to - * be ambiguous. - */ - sfound = NULL; - RB_FOREACH(s, sessions, &sessions) { - if (strncmp(name, s->name, strlen(name)) == 0 || - fnmatch(name, s->name, 0) == 0) { - if (sfound != NULL) { - *ambiguous = 1; - return (NULL); - } - sfound = s; - } - } - return (sfound); -} - -/* - * Lookup a window or return -1 if not found or ambigious. First try as an - * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in - * idx if the window index is a valid number but there is no window with that - * index. - */ -struct winlink * -cmd_lookup_window(struct session *s, const char *name, int *ambiguous) -{ - struct winlink *wl, *wlfound; - const char *errstr; - u_int idx; - - *ambiguous = 0; - - /* Try as a window id. */ - if ((wl = cmd_lookup_winlink_windowid(s, name)) != NULL) - return (wl); - - /* First see if this is a valid window index in this session. */ - idx = strtonum(name, 0, INT_MAX, &errstr); - if (errstr == NULL) { - if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL) - return (wl); - } - - /* Look for exact matches, error if more than one. */ - wlfound = NULL; - RB_FOREACH(wl, winlinks, &s->windows) { - if (strcmp(name, wl->window->name) == 0) { - if (wlfound != NULL) { - *ambiguous = 1; - return (NULL); - } - wlfound = wl; - } - } - if (wlfound != NULL) - return (wlfound); - - /* Now look for pattern matches, again error if multiple. */ - wlfound = NULL; - RB_FOREACH(wl, winlinks, &s->windows) { - if (strncmp(name, wl->window->name, strlen(name)) == 0 || - fnmatch(name, wl->window->name, 0) == 0) { - if (wlfound != NULL) { - *ambiguous = 1; - return (NULL); - } - wlfound = wl; - } - } - if (wlfound != NULL) - return (wlfound); - - return (NULL); -} - -/* - * Find a window index - if the window doesn't exist, check if it is a - * potential index and return it anyway. - */ +/* Adjust current mouse position for a pane. */ int -cmd_lookup_index(struct session *s, const char *name, int *ambiguous) +cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, + u_int *yp, int last) { - struct winlink *wl; - const char *errstr; - u_int idx; + u_int x, y; - if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL) - return (wl->idx); - if (*ambiguous) + if (last) { + x = m->lx; + y = m->ly; + } else { + x = m->x; + y = m->y; + } + + if (m->statusat == 0 && y > 0) + y--; + else if (m->statusat > 0 && y >= (u_int)m->statusat) + y = m->statusat - 1; + + if (x < wp->xoff || x >= wp->xoff + wp->sx) + return (-1); + if (y < wp->yoff || y >= wp->yoff + wp->sy) return (-1); - idx = strtonum(name, 0, INT_MAX, &errstr); - if (errstr == NULL) - return (idx); - - return (-1); + *xp = x - wp->xoff; + *yp = y - wp->yoff; + return (0); } -/* Lookup pane id. An initial % means a pane id. */ -struct window_pane * -cmd_lookup_paneid(const char *arg) -{ - const char *errstr; - u_int paneid; - - if (*arg != '%') - return (NULL); - - paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr); - if (errstr != NULL) - return (NULL); - return (window_pane_find_by_id(paneid)); -} - -/* Lookup window id in a session. An initial @ means a window id. */ +/* Get current mouse window if any. */ struct winlink * -cmd_lookup_winlink_windowid(struct session *s, const char *arg) +cmd_mouse_window(struct mouse_event *m, struct session **sp) { - const char *errstr; - u_int windowid; + struct session *s; + struct window *w; - if (*arg != '@') + if (!m->valid || m->s == -1 || m->w == -1) + return (NULL); + if ((s = session_find_by_id(m->s)) == NULL) + return (NULL); + if ((w = window_find_by_id(m->w)) == NULL) return (NULL); - windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr); - if (errstr != NULL) - return (NULL); - return (winlink_find_by_window_id(&s->windows, windowid)); + if (sp != NULL) + *sp = s; + return (winlink_find_by_window(&s->windows, w)); } -/* Lookup window id. An initial @ means a window id. */ -struct window * -cmd_lookup_windowid(const char *arg) +/* Get current mouse pane if any. */ +struct window_pane * +cmd_mouse_pane(struct mouse_event *m, struct session **sp, + struct winlink **wlp) { - const char *errstr; - u_int windowid; - - if (*arg != '@') - return (NULL); - - windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr); - if (errstr != NULL) - return (NULL); - return (window_find_by_id(windowid)); -} - -/* Find session and winlink for window. */ -struct session * -cmd_window_session(struct cmd_q *cmdq, struct window *w, struct winlink **wlp) -{ - struct session *s; - struct sessionslist ss; struct winlink *wl; + struct window_pane *wp; - /* If this window is in the current session, return that winlink. */ - s = cmd_current_session(cmdq, 0); - if (s != NULL) { - wl = winlink_find_by_window(&s->windows, w); - if (wl != NULL) { - if (wlp != NULL) - *wlp = wl; - return (s); - } - } + if ((wl = cmd_mouse_window(m, sp)) == NULL) + return (NULL); + if ((wp = window_pane_find_by_id(m->wp)) == NULL) + return (NULL); + if (!window_has_pane(wl->window, wp)) + return (NULL); - /* Otherwise choose from all sessions with this window. */ - ARRAY_INIT(&ss); - RB_FOREACH(s, sessions, &sessions) { - if (winlink_find_by_window(&s->windows, w) != NULL) - ARRAY_ADD(&ss, s); - } - s = cmd_choose_session_list(&ss); - ARRAY_FREE(&ss); if (wlp != NULL) - *wlp = winlink_find_by_window(&s->windows, w); - return (s); -} - -/* Find the target session or report an error and return NULL. */ -struct session * -cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached) -{ - struct session *s; - struct window_pane *wp; - struct window *w; - struct client *c; - char *tmparg; - size_t arglen; - int ambiguous; - - /* A NULL argument means the current session. */ - if (arg == NULL) - return (cmd_current_session(cmdq, prefer_unattached)); - - /* Lookup as pane id or window id. */ - if ((wp = cmd_lookup_paneid(arg)) != NULL) - return (cmd_window_session(cmdq, wp->window, NULL)); - if ((w = cmd_lookup_windowid(arg)) != NULL) - return (cmd_window_session(cmdq, w, NULL)); - - /* Trim a single trailing colon if any. */ - tmparg = xstrdup(arg); - arglen = strlen(tmparg); - if (arglen != 0 && tmparg[arglen - 1] == ':') - tmparg[arglen - 1] = '\0'; - - /* An empty session name is the current session. */ - if (*tmparg == '\0') { - free(tmparg); - return (cmd_current_session(cmdq, prefer_unattached)); - } - - /* Find the session, if any. */ - s = cmd_lookup_session(tmparg, &ambiguous); - - /* If it doesn't, try to match it as a client. */ - if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL) - s = c->session; - - /* If no session found, report an error. */ - if (s == NULL) { - if (ambiguous) - cmdq_error(cmdq, "more than one session: %s", tmparg); - else - cmdq_error(cmdq, "session not found: %s", tmparg); - } - - free(tmparg); - return (s); -} - -/* Find the target session and window or report an error and return NULL. */ -struct winlink * -cmd_find_window(struct cmd_q *cmdq, const char *arg, struct session **sp) -{ - struct session *s; - struct winlink *wl; - struct window_pane *wp; - const char *winptr; - char *sessptr = NULL; - int ambiguous = 0; - - /* - * Find the current session. There must always be a current session, if - * it can't be found, report an error. - */ - if ((s = cmd_current_session(cmdq, 0)) == NULL) { - cmdq_error(cmdq, "can't establish current session"); - return (NULL); - } - - /* A NULL argument means the current session and window. */ - if (arg == NULL) { - if (sp != NULL) - *sp = s; - return (s->curw); - } - - /* Lookup as pane id. */ - if ((wp = cmd_lookup_paneid(arg)) != NULL) { - s = cmd_window_session(cmdq, wp->window, &wl); - if (sp != NULL) - *sp = s; - return (wl); - } - - /* Time to look at the argument. If it is empty, that is an error. */ - if (*arg == '\0') - goto not_found; - - /* Find the separating colon and split into window and session. */ - winptr = strchr(arg, ':'); - if (winptr == NULL) - goto no_colon; - winptr++; /* skip : */ - sessptr = xstrdup(arg); - *strchr(sessptr, ':') = '\0'; - - /* Try to lookup the session if present. */ - if (*sessptr != '\0') { - if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) - goto no_session; - } - if (sp != NULL) - *sp = s; - - /* - * Then work out the window. An empty string is the current window, - * otherwise try special cases then to look it up in the session. - */ - if (*winptr == '\0') - wl = s->curw; - else if (winptr[0] == '!' && winptr[1] == '\0') - wl = TAILQ_FIRST(&s->lastw); - else if (winptr[0] == '^' && winptr[1] == '\0') - wl = RB_MIN(winlinks, &s->windows); - else if (winptr[0] == '$' && winptr[1] == '\0') - wl = RB_MAX(winlinks, &s->windows); - else if (winptr[0] == '+' || winptr[0] == '-') - wl = cmd_find_window_offset(winptr, s, &ambiguous); - else - wl = cmd_lookup_window(s, winptr, &ambiguous); - if (wl == NULL) - goto not_found; - - if (sessptr != NULL) - free(sessptr); - return (wl); - -no_colon: - /* - * No colon in the string, first try special cases, then as a window - * and lastly as a session. - */ - if (arg[0] == '!' && arg[1] == '\0') { - if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) - goto not_found; - } else if (arg[0] == '+' || arg[0] == '-') { - if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL) - goto lookup_session; - } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) - goto lookup_session; - - if (sp != NULL) - *sp = s; - - return (wl); - -lookup_session: - if (ambiguous) - goto not_found; - if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL) - goto no_session; - - if (sp != NULL) - *sp = s; - - return (s->curw); - -no_session: - if (ambiguous) - cmdq_error(cmdq, "multiple sessions: %s", arg); - else - cmdq_error(cmdq, "session not found: %s", arg); - free(sessptr); - return (NULL); - -not_found: - if (ambiguous) - cmdq_error(cmdq, "multiple windows: %s", arg); - else - cmdq_error(cmdq, "window not found: %s", arg); - free(sessptr); - return (NULL); -} - -struct winlink * -cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous) -{ - struct winlink *wl; - int offset = 1; - - if (winptr[1] != '\0') - offset = strtonum(winptr + 1, 1, INT_MAX, NULL); - if (offset == 0) - wl = cmd_lookup_window(s, winptr, ambiguous); - else { - if (winptr[0] == '+') - wl = winlink_next_by_number(s->curw, s, offset); - else - wl = winlink_previous_by_number(s->curw, s, offset); - } - - return (wl); -} - -/* - * Find the target session and window index, whether or not it exists in the - * session. Return -2 on error or -1 if no window index is specified. This is - * used when parsing an argument for a window target that may not exist (for - * example if it is going to be created). - */ -int -cmd_find_index(struct cmd_q *cmdq, const char *arg, struct session **sp) -{ - struct session *s; - struct winlink *wl; - const char *winptr; - char *sessptr = NULL; - int idx, ambiguous = 0; - - /* - * Find the current session. There must always be a current session, if - * it can't be found, report an error. - */ - if ((s = cmd_current_session(cmdq, 0)) == NULL) { - cmdq_error(cmdq, "can't establish current session"); - return (-2); - } - - /* A NULL argument means the current session and "no window" (-1). */ - if (arg == NULL) { - if (sp != NULL) - *sp = s; - return (-1); - } - - /* Time to look at the argument. If it is empty, that is an error. */ - if (*arg == '\0') - goto not_found; - - /* Find the separating colon. If none, assume the current session. */ - winptr = strchr(arg, ':'); - if (winptr == NULL) - goto no_colon; - winptr++; /* skip : */ - sessptr = xstrdup(arg); - *strchr(sessptr, ':') = '\0'; - - /* Try to lookup the session if present. */ - if (sessptr != NULL && *sessptr != '\0') { - if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) - goto no_session; - } - if (sp != NULL) - *sp = s; - - /* - * Then work out the window. An empty string is a new window otherwise - * try to look it up in the session. - */ - if (*winptr == '\0') - idx = -1; - else if (winptr[0] == '!' && winptr[1] == '\0') { - if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) - goto not_found; - idx = wl->idx; - } else if (winptr[0] == '+' || winptr[0] == '-') { - if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0) - goto invalid_index; - } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) - goto invalid_index; - - free(sessptr); - return (idx); - -no_colon: - /* - * No colon in the string, first try special cases, then as a window - * and lastly as a session. - */ - if (arg[0] == '!' && arg[1] == '\0') { - if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) - goto not_found; - idx = wl->idx; - } else if (arg[0] == '+' || arg[0] == '-') { - if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0) - goto lookup_session; - } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) - goto lookup_session; - - if (sp != NULL) - *sp = s; - - return (idx); - -lookup_session: - if (ambiguous) - goto not_found; - if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL) - goto no_session; - - if (sp != NULL) - *sp = s; - - return (-1); - -no_session: - if (ambiguous) - cmdq_error(cmdq, "multiple sessions: %s", arg); - else - cmdq_error(cmdq, "session not found: %s", arg); - free(sessptr); - return (-2); - -invalid_index: - if (ambiguous) - goto not_found; - cmdq_error(cmdq, "invalid index: %s", arg); - - free(sessptr); - return (-2); - -not_found: - if (ambiguous) - cmdq_error(cmdq, "multiple windows: %s", arg); - else - cmdq_error(cmdq, "window not found: %s", arg); - free(sessptr); - return (-2); -} - -int -cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous) -{ - int idx, offset = 1; - - if (winptr[1] != '\0') - offset = strtonum(winptr + 1, 1, INT_MAX, NULL); - if (offset == 0) - idx = cmd_lookup_index(s, winptr, ambiguous); - else { - if (winptr[0] == '+') { - if (s->curw->idx == INT_MAX) - idx = cmd_lookup_index(s, winptr, ambiguous); - else - idx = s->curw->idx + offset; - } else { - if (s->curw->idx == 0) - idx = cmd_lookup_index(s, winptr, ambiguous); - else - idx = s->curw->idx - offset; - } - } - - return (idx); -} - -/* - * Find the target session, window and pane number or report an error and - * return NULL. The pane number is separated from the session:window by a ., - * such as mysession:mywindow.0. - */ -struct winlink * -cmd_find_pane(struct cmd_q *cmdq, - const char *arg, struct session **sp, struct window_pane **wpp) -{ - struct session *s; - struct winlink *wl; - const char *period, *errstr; - char *winptr, *paneptr; - u_int idx; - - /* Get the current session. */ - if ((s = cmd_current_session(cmdq, 0)) == NULL) { - cmdq_error(cmdq, "can't establish current session"); - return (NULL); - } - if (sp != NULL) - *sp = s; - - /* A NULL argument means the current session, window and pane. */ - if (arg == NULL) { - *wpp = s->curw->window->active; - return (s->curw); - } - - /* Lookup as pane id. */ - if ((*wpp = cmd_lookup_paneid(arg)) != NULL) { - s = cmd_window_session(cmdq, (*wpp)->window, &wl); - if (sp != NULL) - *sp = s; - return (wl); - } - - /* Look for a separating period. */ - if ((period = strrchr(arg, '.')) == NULL) - goto no_period; - - /* Pull out the window part and parse it. */ - winptr = xstrdup(arg); - winptr[period - arg] = '\0'; - if (*winptr == '\0') - wl = s->curw; - else if ((wl = cmd_find_window(cmdq, winptr, sp)) == NULL) - goto error; - - /* Find the pane section and look it up. */ - paneptr = winptr + (period - arg) + 1; - if (*paneptr == '\0') - *wpp = wl->window->active; - else if (paneptr[0] == '+' || paneptr[0] == '-') - *wpp = cmd_find_pane_offset(paneptr, wl); - else { - idx = strtonum(paneptr, 0, INT_MAX, &errstr); - if (errstr != NULL) - goto lookup_string; - *wpp = window_pane_at_index(wl->window, idx); - if (*wpp == NULL) - goto lookup_string; - } - - free(winptr); - return (wl); - -lookup_string: - /* Try pane string description. */ - if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) { - cmdq_error(cmdq, "can't find pane: %s", paneptr); - goto error; - } - - free(winptr); - return (wl); - -no_period: - /* Try as a pane number alone. */ - idx = strtonum(arg, 0, INT_MAX, &errstr); - if (errstr != NULL) - goto lookup_window; - - /* Try index in the current session and window. */ - if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL) - goto lookup_window; - - return (s->curw); - -lookup_window: - /* Try pane string description. */ - if ((*wpp = window_find_string(s->curw->window, arg)) != NULL) - return (s->curw); - - /* Try as a window and use the active pane. */ - if ((wl = cmd_find_window(cmdq, arg, sp)) != NULL) - *wpp = wl->window->active; - return (wl); - -error: - free(winptr); - return (NULL); -} - -struct window_pane * -cmd_find_pane_offset(const char *paneptr, struct winlink *wl) -{ - struct window *w = wl->window; - struct window_pane *wp = w->active; - u_int offset = 1; - - if (paneptr[1] != '\0') - offset = strtonum(paneptr + 1, 1, INT_MAX, NULL); - if (offset > 0) { - if (paneptr[0] == '+') - wp = window_pane_next_by_number(w, wp, offset); - else - wp = window_pane_previous_by_number(w, wp, offset); - } - + *wlp = wl; return (wp); } @@ -1267,98 +630,14 @@ cmd_template_replace(const char *template, const char *s, int idx) ptr++; len += strlen(s); - buf = xrealloc(buf, 1, len + 1); + buf = xrealloc(buf, len + 1); strlcat(buf, s, len + 1); continue; } - buf = xrealloc(buf, 1, len + 2); + buf = xrealloc(buf, len + 2); buf[len++] = ch; buf[len] = '\0'; } return (buf); } - -/* - * Return the default path for a new pane, using the given path or the - * default-path option if it is NULL. Several special values are accepted: the - * empty string or relative path for the current pane's working directory, ~ - * for the user's home, - for the session working directory, . for the tmux - * server's working directory. The default on failure is the session's working - * directory. - */ -const char * -cmd_get_default_path(struct cmd_q *cmdq, const char *cwd) -{ - struct client *c = cmdq->client; - struct session *s; - struct environ_entry *envent; - const char *root; - char tmp[MAXPATHLEN]; - struct passwd *pw; - int n; - size_t skip; - static char path[MAXPATHLEN]; - - if ((s = cmd_current_session(cmdq, 0)) == NULL) - return (NULL); - - if (cwd == NULL) - cwd = options_get_string(&s->options, "default-path"); - - skip = 1; - if (strcmp(cwd, "$HOME") == 0 || strncmp(cwd, "$HOME/", 6) == 0) { - /* User's home directory - $HOME. */ - skip = 5; - goto find_home; - } else if (cwd[0] == '~' && (cwd[1] == '\0' || cwd[1] == '/')) { - /* User's home directory - ~. */ - goto find_home; - } else if (cwd[0] == '-' && (cwd[1] == '\0' || cwd[1] == '/')) { - /* Session working directory. */ - root = s->cwd; - goto complete_path; - } else if (cwd[0] == '.' && (cwd[1] == '\0' || cwd[1] == '/')) { - /* Server working directory. */ - if (getcwd(tmp, sizeof tmp) != NULL) { - root = tmp; - goto complete_path; - } - return (s->cwd); - } else if (*cwd == '/') { - /* Absolute path. */ - return (cwd); - } else { - /* Empty or relative path. */ - if (c != NULL && c->session == NULL && c->cwd != NULL) - root = c->cwd; - else if (s->curw != NULL) - root = osdep_get_cwd(s->curw->window->active->fd); - else - return (s->cwd); - skip = 0; - if (root != NULL) - goto complete_path; - } - - return (s->cwd); - -find_home: - envent = environ_find(&global_environ, "HOME"); - if (envent != NULL && *envent->value != '\0') - root = envent->value; - else if ((pw = getpwuid(getuid())) != NULL) - root = pw->pw_dir; - else - return (s->cwd); - -complete_path: - if (root[skip] == '\0') { - strlcpy(path, root, sizeof path); - return (path); - } - n = snprintf(path, sizeof path, "%s/%s", root, cwd + skip); - if (n > 0 && (size_t)n < sizeof path) - return (path); - return (s->cwd); -} diff --git a/colour.c b/colour.c index eef31a30..a56ddce9 100644 --- a/colour.c +++ b/colour.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -29,91 +29,305 @@ * of the 256 colour palette. */ -/* An RGB colour. */ struct colour_rgb { + u_char i; u_char r; u_char g; u_char b; }; -/* 256 colour RGB table, generated on first use. */ -struct colour_rgb *colour_rgb_256; +const struct colour_rgb colour_from_256[] = { + { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, + { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, + { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, + { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, + { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, + { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, + { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, + { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, + { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, + { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, + { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, + { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, + { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, + { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, + { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, + { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, + { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, + { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, + { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f }, + { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf }, + { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff }, + { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f }, + { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf }, + { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff }, + { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f }, + { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf }, + { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff }, + { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f }, + { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf }, + { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff }, + { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f }, + { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf }, + { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff }, + { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f }, + { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf }, + { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff }, + { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f }, + { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf }, + { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff }, + { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f }, + { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf }, + { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff }, + { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f }, + { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf }, + { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff }, + { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f }, + { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf }, + { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff }, + { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f }, + { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf }, + { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff }, + { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f }, + { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf }, + { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff }, + { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f }, + { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf }, + { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff }, + { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f }, + { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf }, + { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff }, + { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f }, + { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf }, + { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff }, + { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f }, + { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf }, + { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff }, + { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f }, + { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf }, + { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff }, + { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f }, + { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf }, + { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff }, + { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f }, + { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf }, + { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff }, + { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f }, + { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf }, + { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff }, + { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f }, + { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf }, + { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff }, + { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f }, + { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf }, + { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff }, + { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f }, + { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf }, + { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff }, + { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f }, + { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf }, + { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff }, + { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, + { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, + { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, + { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, + { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, + { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, + { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, + { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, + { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, + { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, + { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, + { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, + { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, + { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, + { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, + { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, + { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, + { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, + { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, + { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, + { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, + { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, + { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 }, + { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, + { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a }, + { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, + { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 }, + { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, + { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda }, + { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, +}; +const struct colour_rgb colour_to_256[] = { + { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, + { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, + { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, + { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, + { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, + { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, + { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, + { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, + { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, + { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, + { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, + { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, + { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, + { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, + { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, + { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, + { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, + { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, + { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, + { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, + { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, + { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, + { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 }, + { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 }, + { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 }, + { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 }, + { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 }, + { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 }, + { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 }, + { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 }, + { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 }, + { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 }, + { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 }, + { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 }, + { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 }, + { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 }, + { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 }, + { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 }, + { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 }, + { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 }, + { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 }, + { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, + { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 }, + { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 }, + { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 }, + { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 }, + { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 }, + { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 }, + { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 }, + { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 }, + { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 }, + { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 }, + { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 }, + { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 }, + { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 }, + { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 }, + { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 }, + { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 }, + { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 }, + { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 }, + { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a }, + { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, + { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 }, + { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 }, + { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 }, + { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 }, + { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 }, + { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 }, + { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 }, + { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 }, + { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 }, + { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 }, + { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 }, + { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 }, + { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 }, + { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 }, + { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 }, + { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 }, + { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 }, + { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 }, + { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 }, + { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, + { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 }, + { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 }, + { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 }, + { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 }, + { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 }, + { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 }, + { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 }, + { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 }, + { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 }, + { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 }, + { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 }, + { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 }, + { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 }, + { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 }, + { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 }, + { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 }, + { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 }, + { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 }, + { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda }, + { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, + { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, + { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, + { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, + { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, + { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, + { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, + { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, + { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, + { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, + { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, + { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, + { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, + { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, + { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, + { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, + { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, + { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, + { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, +}; -void colour_rgb_generate256(void); -u_int colour_rgb_distance(struct colour_rgb *, struct colour_rgb *); -int colour_rgb_find(struct colour_rgb *); +int colour_cmp_rgb(const void *, const void *); -/* Generate 256 colour RGB table. */ -void -colour_rgb_generate256(void) +/* Compare function for bsearch(). */ +int +colour_cmp_rgb(const void *lhs0, const void *rhs0) { - struct colour_rgb *rgb; - u_int i, r, g, b; + const struct colour_rgb *lhs = lhs0, *rhs = rhs0; - /* - * Allocate the table. The first 16 colours are often changed by users - * and terminals so don't include them. - */ - colour_rgb_256 = xcalloc(240, sizeof *colour_rgb_256); + if (lhs->r < rhs->r) + return (-1); + if (lhs->r > rhs->r) + return (1); - /* Add the colours first. */ - r = g = b = 0; - for (i = 240; i > 24; i--) { - rgb = &colour_rgb_256[240 - i]; + if (lhs->g < rhs->g) + return (-1); + if (lhs->g > rhs->g) + return (1); - if (r != 0) - rgb->r = (r * 40) + 55; - if (g != 0) - rgb->g = (g * 40) + 55; - if (b != 0) - rgb->b = (b * 40) + 55; + if (lhs->b < rhs->b) + return (-1); + if (lhs->b > rhs->b) + return (1); - b++; - if (b > 5) { - b = 0; - g++; - } - if (g > 5) { - g = 0; - r++; - } - } - - /* Then add the greys. */ - for (i = 24; i > 0; i--) { - rgb = &colour_rgb_256[240 - i]; - - rgb->r = 8 + (24 - i) * 10; - rgb->g = 8 + (24 - i) * 10; - rgb->b = 8 + (24 - i) * 10; - } -} - -/* Get colour RGB distance. */ -u_int -colour_rgb_distance(struct colour_rgb *rgb1, struct colour_rgb *rgb2) -{ - int r, g, b; - - r = rgb1->r - rgb2->r; - g = rgb1->g - rgb2->g; - b = rgb1->b - rgb2->b; - return (r * r + g * g + b * b); + return (0); } /* Work out the nearest colour from the 256 colour set. */ int -colour_rgb_find(struct colour_rgb *rgb) +colour_find_rgb(u_char r, u_char g, u_char b) { - u_int distance, lowest, colour, i; + struct colour_rgb rgb = { .r = r, .g = g, .b = b }, *found; + u_int distance, lowest, colour, i; + int dr, dg, db; - if (colour_rgb_256 == NULL) - colour_rgb_generate256(); + found = bsearch(&rgb, colour_to_256, nitems(colour_to_256), + sizeof colour_to_256[0], colour_cmp_rgb); + if (found != NULL) + return (16 + found->i); colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { - distance = colour_rgb_distance(&colour_rgb_256[i], rgb); + dr = (int)colour_from_256[i].r - r; + dg = (int)colour_from_256[i].g - g; + db = (int)colour_from_256[i].b - b; + + distance = dr * dr + dg * dg + db * db; if (distance < lowest) { lowest = distance; colour = 16 + i; @@ -147,7 +361,7 @@ colour_tostring(int c) static char s[32]; if (c & 0x100) { - xsnprintf(s, sizeof s, "colour%u", c & ~0x100); + xsnprintf(s, sizeof s, "colour%d", c & ~0x100); return (s); } @@ -194,20 +408,20 @@ colour_tostring(int c) int colour_fromstring(const char *s) { - const char *errstr; - const char *cp; - struct colour_rgb rgb; - int n; + const char *errstr; + const char *cp; + int n; + u_char r, g, b; if (*s == '#' && strlen(s) == 7) { for (cp = s + 1; isxdigit((u_char) *cp); cp++) ; if (*cp != '\0') return (-1); - n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &rgb.r, &rgb.g, &rgb.b); + n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b); if (n != 3) return (-1); - return (colour_rgb_find(&rgb) | 0x100); + return (colour_find_rgb(r, g, b) | 0x100); } if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { @@ -217,47 +431,39 @@ colour_fromstring(const char *s) return (n | 0x100); } - if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0')) + if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0) return (0); - if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0')) + if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) return (1); - if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0')) + if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) return (2); - if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0')) + if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) return (3); - if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0')) + if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) return (4); - if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0')) + if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) return (5); - if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0')) + if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) return (6); - if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0')) + if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) return (7); - if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0')) + if (strcasecmp(s, "default") == 0 || strcmp(s, "8") == 0) return (8); - if (strcasecmp(s, "brightblack") == 0 || - (s[0] == '9' && s[1] == '0' && s[1] == '\0')) + if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) return (90); - if (strcasecmp(s, "brightred") == 0 || - (s[0] == '9' && s[1] == '1' && s[1] == '\0')) + if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) return (91); - if (strcasecmp(s, "brightgreen") == 0 || - (s[0] == '9' && s[1] == '2' && s[1] == '\0')) + if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) return (92); - if (strcasecmp(s, "brightyellow") == 0 || - (s[0] == '9' && s[1] == '3' && s[1] == '\0')) + if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) return (93); - if (strcasecmp(s, "brightblue") == 0 || - (s[0] == '9' && s[1] == '4' && s[1] == '\0')) + if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) return (94); - if (strcasecmp(s, "brightmagenta") == 0 || - (s[0] == '9' && s[1] == '5' && s[1] == '\0')) + if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) return (95); - if (strcasecmp(s, "brightcyan") == 0 || - (s[0] == '9' && s[1] == '6' && s[1] == '\0')) + if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) return (96); - if (strcasecmp(s, "brightwhite") == 0 || - (s[0] == '9' && s[1] == '7' && s[1] == '\0')) + if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) return (97); return (-1); } @@ -287,29 +493,3 @@ colour_256to16(u_char c) return (table[c]); } - -/* Convert 256 colour palette to 88. */ -u_char -colour_256to88(u_char c) -{ - static const u_char table[256] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 17, 18, 18, 19, 20, 21, 21, 22, 22, 23, 20, 21, 21, 22, - 22, 23, 24, 25, 25, 26, 26, 27, 24, 25, 25, 26, 26, 27, 28, 29, - 29, 30, 30, 31, 32, 33, 33, 34, 34, 35, 36, 37, 37, 38, 38, 39, - 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, 40, 41, 41, 42, - 42, 43, 44, 45, 45, 46, 46, 47, 32, 33, 33, 34, 34, 35, 36, 37, - 37, 38, 38, 39, 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, - 40, 41, 41, 42, 42, 43, 44, 45, 45, 46, 46, 47, 48, 49, 49, 50, - 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, 54, 55, 56, 57, - 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, 61, 62, 62, 63, - 48, 49, 49, 50, 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, - 54, 55, 56, 57, 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, - 61, 62, 62, 63, 64, 65, 65, 66, 66, 67, 68, 69, 69, 70, 70, 71, - 68, 69, 69, 70, 70, 71, 72, 73, 73, 74, 74, 75, 72, 73, 73, 74, - 74, 75, 76, 77, 77, 78, 78, 79, 0, 0, 80, 80, 80, 81, 81, 81, - 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87 - }; - - return (table[c]); -} diff --git a/compat.h b/compat.h index b9ee7ba1..6812e9a5 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2007 Nicholas Marriott * @@ -23,6 +21,9 @@ #define __attribute__(a) #endif +#ifndef __unused +#define __unused __attribute__ ((__unused__)) +#endif #ifndef __dead #define __dead __attribute__ ((__noreturn__)) #endif @@ -30,6 +31,10 @@ #define __packed __attribute__ ((__packed__)) #endif +#ifndef ECHOPRT +#define ECHOPRT 0 +#endif + #ifndef HAVE_BSD_TYPES typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; @@ -121,6 +126,10 @@ typedef uint64_t u_int64_t; #define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) #endif +#ifndef O_DIRECTORY +#define O_DIRECTORY 0 +#endif + #ifndef INFTIM #define INFTIM -1 #endif @@ -152,13 +161,31 @@ typedef uint64_t u_int64_t; } while (0) #endif +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + #ifndef TTY_NAME_MAX #define TTY_NAME_MAX 32 #endif -#ifndef HAVE_BZERO -#undef bzero -#define bzero(buf, len) memset(buf, 0, len); +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + +#ifndef HAVE_FLOCK +#define LOCK_SH 0 +#define LOCK_EX 0 +#define LOCK_NB 0 +#define flock(fd, op) (0) #endif #ifndef HAVE_CLOSEFROM @@ -198,6 +225,7 @@ int daemon(int, int); #ifndef HAVE_B64_NTOP /* b64_ntop.c */ +#undef b64_ntop /* for Cygwin */ int b64_ntop(const char *, size_t, char *, size_t); #endif @@ -218,12 +246,32 @@ int vasprintf(char **, const char *, va_list); char *fgetln(FILE *, size_t *); #endif +#ifndef HAVE_FPARSELN +char *fparseln(FILE *, size_t *, size_t *, const char *, int); +#endif + #ifndef HAVE_SETENV /* setenv.c */ int setenv(const char *, const char *, int); int unsetenv(const char *); #endif +#ifndef HAVE_CFMAKERAW +/* cfmakeraw.c */ +void cfmakeraw(struct termios *); +#endif + +#ifndef HAVE_OPENAT +/* openat.c */ +#define AT_FDCWD -100 +int openat(int, const char *, int, ...); +#endif + +#ifndef HAVE_REALLOCARRAY +/* reallocarray.c */ +void *reallocarray(void *, size_t, size_t size); +#endif + #ifdef HAVE_GETOPT #include #else diff --git a/compat/asprintf.c b/compat/asprintf.c index 66f00996..09020b35 100644 --- a/compat/asprintf.c +++ b/compat/asprintf.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2006 Nicholas Marriott * @@ -56,10 +54,12 @@ vasprintf(char **ret, const char *fmt, va_list ap) free(*ret); goto error; } + va_end(ap2); return (n); error: + va_end(ap2); *ret = NULL; return (-1); } diff --git a/compat/bitstring.h b/compat/bitstring.h index 28817c08..8fc3423e 100644 --- a/compat/bitstring.h +++ b/compat/bitstring.h @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: bitstring.h,v 1.5 2003/06/02 19:34:12 millert Exp $ */ /* $NetBSD: bitstring.h,v 1.5 1997/05/14 15:49:55 pk Exp $ */ diff --git a/cmd-start-server.c b/compat/cfmakeraw.c similarity index 61% rename from cmd-start-server.c rename to compat/cfmakeraw.c index cba2403b..85b2c9bc 100644 --- a/cmd-start-server.c +++ b/compat/cfmakeraw.c @@ -1,7 +1,6 @@ -/* $Id$ */ - /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2013 Dagobert Michelsen + * Copyright (c) 2013 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,28 +15,16 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include "tmux.h" -/* - * Start the server and do nothing else. - */ - -enum cmd_retval cmd_start_server_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_start_server_entry = { - "start-server", "start", - "", 0, 0, - "", - CMD_STARTSERVER, - NULL, - NULL, - cmd_start_server_exec -}; - -enum cmd_retval -cmd_start_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) +void +cfmakeraw(struct termios *tio) { - return (CMD_RETURN_NORMAL); + tio->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + tio->c_oflag &= ~OPOST; + tio->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + tio->c_cflag &= ~(CSIZE|PARENB); + tio->c_cflag |= CS8; } diff --git a/compat/closefrom.c b/compat/closefrom.c index 74714c13..8c650aa5 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2004-2005 Todd C. Miller * @@ -16,8 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "tmux.h" - #ifndef HAVE_CLOSEFROM #include @@ -49,6 +45,8 @@ # endif #endif +#include "tmux.h" + #ifndef OPEN_MAX # define OPEN_MAX 256 #endif diff --git a/compat/daemon.c b/compat/daemon.c index 661f7fd5..3e87874b 100644 --- a/compat/daemon.c +++ b/compat/daemon.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: daemon.c,v 1.6 2005/08/08 08:05:33 espie Exp $ */ /*- * Copyright (c) 1990, 1993 diff --git a/compat/fgetln.c b/compat/fgetln.c index 08ddc840..a5c2489d 100644 --- a/compat/fgetln.c +++ b/compat/fgetln.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $NetBSD: fgetln.c,v 1.3 2007/08/07 02:06:58 lukem Exp $ */ /*- diff --git a/compat/forkpty-aix.c b/compat/forkpty-aix.c index db9c2e71..6894aa44 100644 --- a/compat/forkpty-aix.c +++ b/compat/forkpty-aix.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2009 Nicholas Marriott * @@ -23,21 +21,29 @@ #include #include #include +#include #include "tmux.h" pid_t forkpty(int *master, unused char *name, struct termios *tio, struct winsize *ws) { - int slave, fd; - char *path; + int slave = -1, fd, pipe_fd[2]; + char *path, dummy; pid_t pid; - if ((*master = open("/dev/ptc", O_RDWR|O_NOCTTY)) == -1) + if (pipe(pipe_fd) == -1) return (-1); + if ((*master = open("/dev/ptc", O_RDWR|O_NOCTTY)) == -1) + goto out; + if ((path = ttyname(*master)) == NULL) goto out; + + if (name != NULL) + strlcpy(name, path, TTY_NAME_MAX); + if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; @@ -47,6 +53,13 @@ forkpty(int *master, unused char *name, struct termios *tio, struct winsize *ws) case 0: close(*master); + close(pipe_fd[1]); + while (read(pipe_fd[0], &dummy, 1) == -1) { + if (errno != EINTR) + break; + } + close(pipe_fd[0]); + fd = open(_PATH_TTY, O_RDWR|O_NOCTTY); if (fd >= 0) { ioctl(fd, TIOCNOTTY, NULL); @@ -80,10 +93,14 @@ forkpty(int *master, unused char *name, struct termios *tio, struct winsize *ws) dup2(slave, 2); if (slave > 2) close(slave); + return (0); } close(slave); + + close(pipe_fd[0]); + close(pipe_fd[1]); return (pid); out: @@ -91,5 +108,8 @@ out: close(*master); if (slave != -1) close(slave); + + close(pipe_fd[0]); + close(pipe_fd[1]); return (-1); } diff --git a/compat/forkpty-hpux.c b/compat/forkpty-hpux.c index 90452f8d..59130e1b 100644 --- a/compat/forkpty-hpux.c +++ b/compat/forkpty-hpux.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2008 Nicholas Marriott * @@ -29,7 +27,7 @@ pid_t forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) { - int slave; + int slave = -1; char *path; pid_t pid; diff --git a/compat/forkpty-sunos.c b/compat/forkpty-sunos.c index 90452f8d..554e51ac 100644 --- a/compat/forkpty-sunos.c +++ b/compat/forkpty-sunos.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2008 Nicholas Marriott * @@ -21,6 +19,7 @@ #include #include +#include #include #include @@ -29,7 +28,7 @@ pid_t forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) { - int slave; + int slave = -1; char *path; pid_t pid; diff --git a/compat/fparseln.c b/compat/fparseln.c new file mode 100644 index 00000000..348bfa14 --- /dev/null +++ b/compat/fparseln.c @@ -0,0 +1,221 @@ +/* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */ +/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christos Zoulas. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libutil/fparseln.c */ + +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * fparseln() specific operation flags. + */ +#define FPARSELN_UNESCESC 0x01 +#define FPARSELN_UNESCCONT 0x02 +#define FPARSELN_UNESCCOMM 0x04 +#define FPARSELN_UNESCREST 0x08 +#define FPARSELN_UNESCALL 0x0f + +static int isescaped(const char *, const char *, int); + +/* isescaped(): + * Return true if the character in *p that belongs to a string + * that starts in *sp, is escaped by the escape character esc. + */ +static int +isescaped(const char *sp, const char *p, int esc) +{ + const char *cp; + size_t ne; + + /* No escape character */ + if (esc == '\0') + return 1; + + /* Count the number of escape characters that precede ours */ + for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) + continue; + + /* Return true if odd number of escape characters */ + return (ne & 1) != 0; +} + + +/* fparseln(): + * Read a line from a file parsing continuations ending in \ + * and eliminating trailing newlines, or comments starting with + * the comment char. + */ +char * +fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], + int flags) +{ + static const char dstr[3] = { '\\', '\\', '#' }; + char *buf = NULL, *ptr, *cp, esc, con, nl, com; + size_t s, len = 0; + int cnt = 1; + + if (str == NULL) + str = dstr; + + esc = str[0]; + con = str[1]; + com = str[2]; + + /* + * XXX: it would be cool to be able to specify the newline character, + * but unfortunately, fgetln does not let us + */ + nl = '\n'; + + while (cnt) { + cnt = 0; + + if (lineno) + (*lineno)++; + + if ((ptr = fgetln(fp, &s)) == NULL) + break; + + if (s && com) { /* Check and eliminate comments */ + for (cp = ptr; cp < ptr + s; cp++) + if (*cp == com && !isescaped(ptr, cp, esc)) { + s = cp - ptr; + cnt = s == 0 && buf == NULL; + break; + } + } + + if (s && nl) { /* Check and eliminate newlines */ + cp = &ptr[s - 1]; + + if (*cp == nl) + s--; /* forget newline */ + } + + if (s && con) { /* Check and eliminate continuations */ + cp = &ptr[s - 1]; + + if (*cp == con && !isescaped(ptr, cp, esc)) { + s--; /* forget escape */ + cnt = 1; + } + } + + if (s == 0 && buf != NULL) + continue; + + if ((cp = realloc(buf, len + s + 1)) == NULL) { + free(buf); + return NULL; + } + buf = cp; + + (void) memcpy(buf + len, ptr, s); + len += s; + buf[len] = '\0'; + } + + if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && + strchr(buf, esc) != NULL) { + ptr = cp = buf; + while (cp[0] != '\0') { + int skipesc; + + while (cp[0] != '\0' && cp[0] != esc) + *ptr++ = *cp++; + if (cp[0] == '\0' || cp[1] == '\0') + break; + + skipesc = 0; + if (cp[1] == com) + skipesc += (flags & FPARSELN_UNESCCOMM); + if (cp[1] == con) + skipesc += (flags & FPARSELN_UNESCCONT); + if (cp[1] == esc) + skipesc += (flags & FPARSELN_UNESCESC); + if (cp[1] != com && cp[1] != con && cp[1] != esc) + skipesc = (flags & FPARSELN_UNESCREST); + + if (skipesc) + cp++; + else + *ptr++ = *cp++; + *ptr++ = *cp++; + } + *ptr = '\0'; + len = strlen(buf); + } + + if (size) + *size = len; + return buf; +} + +#ifdef TEST + +int main(int, char **); + +int +main(argc, argv) + int argc; + char **argv; +{ + char *ptr; + size_t size, line; + + line = 0; + while ((ptr = fparseln(stdin, &size, &line, NULL, + FPARSELN_UNESCALL)) != NULL) + printf("line %d (%d) |%s|\n", line, size, ptr); + return 0; +} + +/* + +# This is a test +line 1 +line 2 \ +line 3 # Comment +line 4 \# Not comment \\\\ + +# And a comment \ +line 5 \\\ +line 6 + +*/ + +#endif /* TEST */ diff --git a/compat/getopt.c b/compat/getopt.c index 38c317cc..9cd3b3d2 100644 --- a/compat/getopt.c +++ b/compat/getopt.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. @@ -54,7 +52,7 @@ char *BSDoptarg; /* argument associated with option */ int BSDgetopt(int nargc, char *const *nargv, const char *ostr) { - static char *place = EMSG; /* option letter processing */ + static const char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ if (ostr == NULL) @@ -96,7 +94,7 @@ BSDgetopt(int nargc, char *const *nargv, const char *ostr) } else { /* need an argument */ if (*place) /* no white space */ - BSDoptarg = place; + BSDoptarg = (char *)place; else if (nargc <= ++BSDoptind) { /* no arg */ place = EMSG; if (*ostr == ':') @@ -107,7 +105,7 @@ BSDgetopt(int nargc, char *const *nargv, const char *ostr) __progname, BSDoptopt); return (BADCH); } - else /* white space */ + else /* white space */ BSDoptarg = nargv[BSDoptind]; place = EMSG; ++BSDoptind; diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 24db311d..241c4f4d 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -1,5 +1,4 @@ -/* $Id$ */ -/* $OpenBSD: imsg-buffer.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ +/* $OpenBSD: imsg-buffer.c,v 1.7 2015/07/12 18:40:49 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -17,16 +16,18 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include #include +#include #include #include #include #include #include "tmux.h" +#include "imsg.h" int ibuf_realloc(struct ibuf *, size_t); void ibuf_enqueue(struct msgbuf *, struct ibuf *); @@ -73,7 +74,7 @@ ibuf_realloc(struct ibuf *buf, size_t len) /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { - errno = ENOMEM; + errno = ERANGE; return (-1); } @@ -148,7 +149,7 @@ ibuf_write(struct msgbuf *msgbuf) unsigned int i = 0; ssize_t n; - bzero(&iov, sizeof(iov)); + memset(&iov, 0, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; @@ -157,22 +158,23 @@ ibuf_write(struct msgbuf *msgbuf) i++; } +again: if ((n = writev(msgbuf->fd, iov, i)) == -1) { - if (errno == EAGAIN || errno == ENOBUFS || - errno == EINTR) /* try later */ - return (0); - else - return (-1); + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); } if (n == 0) { /* connection closed */ errno = 0; - return (-2); + return (0); } msgbuf_drain(msgbuf, n); - return (0); + return (1); } void @@ -231,8 +233,9 @@ msgbuf_write(struct msgbuf *msgbuf) char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; - bzero(&iov, sizeof(iov)); - bzero(&msg, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; @@ -256,17 +259,18 @@ msgbuf_write(struct msgbuf *msgbuf) *(int *)CMSG_DATA(cmsg) = buf->fd; } +again: if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { - if (errno == EAGAIN || errno == ENOBUFS || - errno == EINTR) /* try later */ - return (0); - else - return (-1); + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); } if (n == 0) { /* connection closed */ errno = 0; - return (-2); + return (0); } /* @@ -280,7 +284,7 @@ msgbuf_write(struct msgbuf *msgbuf) msgbuf_drain(msgbuf, n); - return (0); + return (1); } void diff --git a/compat/imsg.c b/compat/imsg.c index 0feff660..6c9bee68 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -1,5 +1,4 @@ -/* $Id$ */ -/* $OpenBSD: imsg.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ +/* $OpenBSD: imsg.c,v 1.9 2015/07/12 18:40:49 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -17,7 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include #include @@ -27,14 +26,51 @@ #include #include "tmux.h" +#include "imsg.h" + +int imsg_fd_overhead = 0; int imsg_get_fd(struct imsgbuf *); +int available_fds(unsigned int); + +/* + * The original code calls getdtablecount() which is OpenBSD specific. Use + * available_fds() from OpenSMTPD instead. + */ +int +available_fds(unsigned int n) +{ + unsigned int i; + int ret, fds[256]; + + if (n > (sizeof(fds)/sizeof(fds[0]))) + return (1); + + ret = 0; + for (i = 0; i < n; i++) { + fds[i] = -1; + if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) + fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); + if (fds[i] < 0) { + ret = 1; + break; + } + } + } + + for (i = 0; i < n && fds[i] >= 0; i++) + close(fds[i]); + + return (ret); +} + void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); - bzero(&ibuf->r, sizeof(ibuf->r)); + memset(&ibuf->r, 0, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); @@ -48,14 +84,15 @@ imsg_read(struct imsgbuf *ibuf) struct cmsghdr *cmsg; union { struct cmsghdr hdr; - char buf[CMSG_SPACE(sizeof(int) * 16)]; + char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; - ssize_t n; + ssize_t n = -1; int fd; struct imsg_fd *ifd; - bzero(&msg, sizeof(msg)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; @@ -64,11 +101,23 @@ imsg_read(struct imsgbuf *ibuf) msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); + if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) + return (-1); + +again: + if (available_fds(imsg_fd_overhead + + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { + errno = EAGAIN; + free(ifd); + return (-1); + } + if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { - if (errno != EINTR && errno != EAGAIN) { - return (-1); - } - return (-2); + if (errno == EMSGSIZE) + goto fail; + if (errno != EINTR && errno != EAGAIN) + goto fail; + goto again; } ibuf->r.wpos += n; @@ -77,17 +126,33 @@ imsg_read(struct imsgbuf *ibuf) cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - fd = (*(int *)CMSG_DATA(cmsg)); - if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) { - close(fd); - return (-1); + int i; + int j; + + /* + * We only accept one file descriptor. Due to C + * padding rules, our control buffer might contain + * more than one fd, and we must close them. + */ + j = ((char *)cmsg + cmsg->cmsg_len - + (char *)CMSG_DATA(cmsg)) / sizeof(int); + for (i = 0; i < j; i++) { + fd = ((int *)CMSG_DATA(cmsg))[i]; + if (ifd != NULL) { + ifd->fd = fd; + TAILQ_INSERT_TAIL(&ibuf->fds, ifd, + entry); + ifd = NULL; + } else + close(fd); } - ifd->fd = fd; - TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); } /* we do not handle other ctl data level */ } +fail: + if (ifd) + free(ifd); return (n); } @@ -111,7 +176,7 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) return (0); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; - if ((imsg->data = malloc(datalen)) == NULL && datalen != 0) + if ((imsg->data = malloc(datalen)) == NULL) return (-1); if (imsg->hdr.flags & IMSGF_HASFD) @@ -133,7 +198,7 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) int imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, - pid_t pid, int fd, void *data, u_int16_t datalen) + pid_t pid, int fd, const void *data, u_int16_t datalen) { struct ibuf *wbuf; @@ -203,7 +268,7 @@ imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, } int -imsg_add(struct ibuf *msg, void *data, u_int16_t datalen) +imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen) { if (datalen) if (ibuf_add(msg, data, datalen) == -1) { @@ -255,7 +320,7 @@ int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) + if (msgbuf_write(&ibuf->w) <= 0) return (-1); return (0); } diff --git a/compat/imsg.h b/compat/imsg.h index f8a78e22..06fe1bc1 100644 --- a/compat/imsg.h +++ b/compat/imsg.h @@ -1,5 +1,4 @@ -/* $Id$ */ -/* $OpenBSD: imsg.h,v 1.4 2010/05/26 13:56:07 nicm Exp $ */ +/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -21,6 +20,9 @@ #include "tmux.h" +#ifndef _IMSG_H_ +#define _IMSG_H_ + #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 @@ -98,13 +100,15 @@ void imsg_init(struct imsgbuf *, int); ssize_t imsg_read(struct imsgbuf *); ssize_t imsg_get(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, - int, void *, u_int16_t); + int, const void *, u_int16_t); int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, int, const struct iovec *, int); struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, u_int16_t); -int imsg_add(struct ibuf *, void *, u_int16_t); +int imsg_add(struct ibuf *, const void *, u_int16_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_flush(struct imsgbuf *); void imsg_clear(struct imsgbuf *); + +#endif diff --git a/compat/openat.c b/compat/openat.c new file mode 100644 index 00000000..6b04eedc --- /dev/null +++ b/compat/openat.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Nicholas Marriott + * + * 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 +#include +#include +#include + +#include "tmux.h" + +int +openat(int fd, const char *path, int flags, ...) +{ + mode_t mode; + va_list ap; + int dotfd, retval, saved_errno; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } else + mode = 0; + + dotfd = -1; + if (fd != AT_FDCWD) { + dotfd = open(".", O_RDONLY); + if (dotfd == -1) + return (-1); + if (fchdir(fd) != 0) { + saved_errno = errno; + close(dotfd); + errno = saved_errno; + return (-1); + } + } + + retval = open(path, flags, mode); + + if (dotfd != -1) { + if (fchdir(dotfd) != 0) { + saved_errno = errno; + close(retval); + close(dotfd); + errno = saved_errno; + return (-1); + } + close(dotfd); + } + + return (retval); +} diff --git a/compat/reallocarray.c b/compat/reallocarray.c new file mode 100644 index 00000000..f4705fcd --- /dev/null +++ b/compat/reallocarray.c @@ -0,0 +1,40 @@ +/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * 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 +#include +#include +#include + +#include "tmux.h" + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} diff --git a/compat/setenv.c b/compat/setenv.c index 43122ba5..6c7d29ec 100644 --- a/compat/setenv.c +++ b/compat/setenv.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2010 Dagobert Michelsen * Copyright (c) 2010 Nicholas Marriott diff --git a/compat/strcasestr.c b/compat/strcasestr.c index 5e03bbb0..aa74c017 100644 --- a/compat/strcasestr.c +++ b/compat/strcasestr.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: strcasestr.c,v 1.3 2006/03/31 05:34:55 deraadt Exp $ */ /* $NetBSD: strcasestr.c,v 1.2 2005/02/09 21:35:47 kleink Exp $ */ diff --git a/compat/strlcat.c b/compat/strlcat.c index 1ba236e2..e081c4f6 100644 --- a/compat/strlcat.c +++ b/compat/strlcat.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* diff --git a/compat/strlcpy.c b/compat/strlcpy.c index 80e13a8f..d6e8e77c 100644 --- a/compat/strlcpy.c +++ b/compat/strlcpy.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ /* diff --git a/compat/strsep.c b/compat/strsep.c index 0a7aa328..c44bc5b2 100644 --- a/compat/strsep.c +++ b/compat/strsep.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ /*- diff --git a/compat/strtonum.c b/compat/strtonum.c index 860e5508..4c7b0c9f 100644 --- a/compat/strtonum.c +++ b/compat/strtonum.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ /* diff --git a/compat/tree.h b/compat/tree.h index 73041340..80d0f538 100644 --- a/compat/tree.h +++ b/compat/tree.h @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ /* * Copyright 2002 Niels Provos diff --git a/compat/unvis.c b/compat/unvis.c index 874b2275..e1f09ec9 100644 --- a/compat/unvis.c +++ b/compat/unvis.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* $OpenBSD: unvis.c,v 1.12 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1989, 1993 diff --git a/compat/vis.c b/compat/vis.c index da4ac7cd..82b42be9 100644 --- a/compat/vis.c +++ b/compat/vis.c @@ -1,5 +1,4 @@ -/* $Id$ */ -/* $OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 millert Exp $ */ +/* $OpenBSD: vis.c,v 1.24 2015/07/20 01:52:28 millert Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -30,14 +29,17 @@ */ #include -#include +#include #include +#include #include +#include #include "tmux.h" #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') -#define isvisible(c) \ +#define isvisible(c,flag) \ + (((c) == '\\' || (flag & VIS_ALL) == 0) && \ (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ @@ -46,7 +48,7 @@ ((flag & VIS_NL) == 0 && (c) == '\n') || \ ((flag & VIS_SAFE) && ((c) == '\b' || \ (c) == '\007' || (c) == '\r' || \ - isgraph((u_char)(c))))) + isgraph((u_char)(c)))))) /* * vis - visually encode characters @@ -54,10 +56,11 @@ char * vis(char *dst, int c, int flag, int nextc) { - if (isvisible(c)) { - *dst++ = c; - if (c == '\\' && (flag & VIS_NOSLASH) == 0) + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) *dst++ = '\\'; + *dst++ = c; *dst = '\0'; return (dst); } @@ -137,10 +140,10 @@ done: /* * strvis, strnvis, strvisx - visually encode characters from src into dst - * + * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, - * is returned. + * is returned. * * Strnvis will write no more than siz-1 bytes (and will NULL terminate). * The number of bytes needed to fully encode the string is returned. @@ -169,19 +172,18 @@ strnvis(char *dst, const char *src, size_t siz, int flag) i = 0; for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { - if (isvisible(c)) { - i = 1; - *dst++ = c; - if (c == '\\' && (flag & VIS_NOSLASH) == 0) { + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) { /* need space for the extra '\\' */ - if (dst < end) - *dst++ = '\\'; - else { - dst--; + if (dst + 1 >= end) { i = 2; break; } + *dst++ = '\\'; } + i = 1; + *dst++ = c; src++; } else { i = vis(tbuf, c, flag, *++src) - tbuf; @@ -204,6 +206,25 @@ strnvis(char *dst, const char *src, size_t siz, int flag) return (dst - start); } +int +stravis(char **outp, const char *src, int flag) +{ + char *buf; + int len, serrno; + + buf = calloc(4, strlen(src) + 1); + if (buf == NULL) + return -1; + len = strvis(buf, src, flag); + serrno = errno; + *outp = realloc(buf, len + 1); + if (*outp == NULL) { + *outp = buf; + errno = serrno; + } + return (len); +} + int strvisx(char *dst, const char *src, size_t len, int flag) { diff --git a/compat/vis.h b/compat/vis.h index e43e9adc..9f12d236 100644 --- a/compat/vis.h +++ b/compat/vis.h @@ -1,5 +1,4 @@ -/* $Id$ */ -/* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 millert Exp $ */ +/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ /*- @@ -51,6 +50,8 @@ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ +#define VIS_DQ 0x200 /* backslash-escape double quotes */ +#define VIS_ALL 0x400 /* encode all characters */ /* * other @@ -74,6 +75,7 @@ char *vis(char *, int, int, int); int strvis(char *, const char *, int); +int stravis(char **, const char *, int); int strnvis(char *, const char *, size_t, int); int strvisx(char *, const char *, size_t, int); int strunvis(char *, const char *); diff --git a/configure.ac b/configure.ac index 2941fe92..ed55b42e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,9 @@ -# $Id$ +# configure.ac -# Miscellaneous autofoo bullshit. -AC_INIT(tmate, 1.8.10) +AC_INIT(tmate, 2.2.0) AC_CONFIG_AUX_DIR(etc) -AM_INIT_AUTOMAKE([foreign]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) AC_CANONICAL_HOST @@ -16,64 +15,44 @@ AC_CANONICAL_HOST # Set up the compiler in two different ways and say yes we may want to install. AC_PROG_CC AM_PROG_CC_C_O +AC_PROG_CPP +AC_PROG_EGREP AC_PROG_INSTALL +PKG_PROG_PKG_CONFIG -# Check for various headers. Alternatives included from compat.h. -AC_CHECK_HEADERS( - [ \ - bitstring.h \ - curses.h \ - dirent.h \ - fcntl.h \ - inttypes.h \ - libutil.h \ - ncurses.h \ - ndir.h \ - paths.h \ - pty.h \ - stdint.h \ - sys/dir.h \ - sys/ndir.h \ - sys/tree.h \ - term.h \ - util.h \ - ] -) +# Default tmux.conf goes in /etc not ${prefix}/etc. +test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc -# Is this a debug build? -#found_debug=yes +# Is this --enable-debug? +found_debug=yes AC_ARG_ENABLE( debug, - AC_HELP_STRING(--enable-debug, create a debug build), + AC_HELP_STRING(--enable-debug, enable debug build flags), found_debug=$enable_debug ) AM_CONDITIONAL(IS_DEBUG, test "x$found_debug" = xyes) +# Is this --enable-coverage? +AC_ARG_ENABLE( + coverage, + AC_HELP_STRING(--enable-coverage, enable coverage build flags), + found_coverage=$enable_coverage +) +AM_CONDITIONAL(IS_COVERAGE, test "x$found_coverage" = xyes) + # Is this a static build? AC_ARG_ENABLE( - static, - AC_HELP_STRING(--enable-static, create a static build), - found_static=$enable_static + static, + AC_HELP_STRING(--enable-static, create a static build), + found_static=$enable_static ) if test "x$found_static" = xyes; then - LDFLAGS="$LDFLAGS -static" + LDFLAGS="$LDFLAGS -static" + PKG_CONFIG="pkg-config --static" fi # Is this gcc? AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) -AC_MSG_CHECKING(for gcc that whines about -I) -AC_EGREP_CPP( - yes, - [ - #if __GNUC__ > 3 - yes - #endif - ], - found_gcc4=yes, - found_gcc4=no -) -AM_CONDITIONAL(IS_GCC4, test "x$found_gcc4" = xyes) -AC_MSG_RESULT($found_gcc4) # Is this Sun CC? AC_EGREP_CPP( @@ -104,16 +83,45 @@ AC_EGREP_CPP( AM_CONDITIONAL(IS_GLIBC, test "x$found_glibc" = xyes) AC_MSG_RESULT($found_glibc) +# Check for various headers. Alternatives included from compat.h. +AC_CHECK_HEADERS( + [ \ + bitstring.h \ + curses.h \ + dirent.h \ + fcntl.h \ + inttypes.h \ + libutil.h \ + ncurses.h \ + ndir.h \ + paths.h \ + pty.h \ + stdint.h \ + sys/dir.h \ + sys/ndir.h \ + sys/tree.h \ + term.h \ + util.h \ + ] +) + +# Look for library needed for flock. +AC_SEARCH_LIBS(flock, bsd) + +# Check for some functions that are replaced or omitted. +AC_CHECK_FUNCS( + [ \ + dirfd \ + flock \ + setproctitle \ + sysconf \ + cfmakeraw \ + ] +) + # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) -AC_CHECK_LIB(z, inflate, [], - [AC_MSG_ERROR([zlib library required])]) -AC_CHECK_LIB(crypto,CRYPTO_new_ex_data, [], - [AC_MSG_ERROR([OpenSSL library required])]) -AC_CHECK_LIB(ssl, SSL_library_init, [], - [AC_MSG_ERROR([OpenSSL library required])]) - # Look for libevent. PKG_CHECK_MODULES( LIBEVENT, @@ -136,20 +144,38 @@ if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi -# Look for curses. -AC_SEARCH_LIBS( - setupterm, - [terminfo curses ncurses], - found_curses=yes, - found_curses=no +# Look for ncurses +PKG_CHECK_MODULES( + LIBNCURSES, + ncurses, + [ + CPPFLAGS="$LIBNCURSES_CFLAGS $CPPFLAGS" + LIBS="$LIBNCURSES_LIBS $LIBS" + found_curses=yes + ], + [ + AC_SEARCH_LIBS( + setupterm, + [ncurses curses terminfo], + found_curses=yes, + found_curses=no + ) + ] ) if test "x$found_curses" = xno; then - AC_MSG_ERROR("curses not found") + AC_MSG_ERROR("curses not found") +fi + +# Look for utempter. +AC_CHECK_HEADER(utempter.h, have_utempter=yes, have_utempter=no) +if test "x$have_utempter" = xyes; then + AC_DEFINE(HAVE_UTEMPTER) + LIBS="$LIBS -lutempter" fi PKG_CHECK_MODULES( MSGPACK, - msgpack, + msgpack >= 1.1.0, [ CPPFLAGS="$MSGPACK_CFLAGS $CPPFLAGS" LIBS="$MSGPACK_LIBS $LIBS" @@ -158,13 +184,12 @@ PKG_CHECK_MODULES( found_msgpack=no ) if test "x$found_msgpack" = xno; then - AC_MSG_ERROR("msgpack not found") + AC_MSG_ERROR("msgpack >= 1.1.0 not found") fi - PKG_CHECK_MODULES( LIBSSH, - libssh, + libssh >= 0.6.0, [ CPPFLAGS="$LIBSSH_CFLAGS $CPPFLAGS" LIBS="$LIBSSH_LIBS $LIBS" @@ -173,7 +198,7 @@ PKG_CHECK_MODULES( found_libssh=no ) if test "x$found_libssh" = xno; then - AC_MSG_ERROR("libssh not found") + AC_MSG_ERROR("libssh >= 0.6.0 not found") fi # Check for b64_ntop. @@ -253,7 +278,7 @@ if test "x$found_cmsg_data" = xno; then if test "x$found_cmsg_data" = xyes; then XOPEN_DEFINES="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED" else - AC_MSG_ERROR("CMSG_DATA not found") + AC_MSG_ERROR("CMSG_DATA not found") fi fi AC_SUBST(XOPEN_DEFINES) @@ -321,6 +346,13 @@ if test "x$found_fgetln" = xyes; then fi AM_CONDITIONAL(NO_FGETLN, [test "x$found_fgetln" = xno]) +# Look for fparseln, compat/fparseln.c used if missing. +AC_CHECK_FUNC(fparseln, found_fparseln=yes, found_fparseln=no) +if test "x$found_fparseln" = xyes; then + AC_DEFINE(HAVE_FPARSELN) +fi +AM_CONDITIONAL(NO_FPARSELN, [test "x$found_fparseln" = xno]) + # Look for strcasestr, compat/strcasestr.c used if missing. AC_CHECK_FUNC(strcasestr, found_strcasestr=yes, found_strcasestr=no) if test "x$found_strcasestr" = xyes; then @@ -342,12 +374,43 @@ if test "x$found_strtonum" = xyes; then fi AM_CONDITIONAL(NO_STRTONUM, [test "x$found_strtonum" = xno]) -# Look for strnvis, compat/{vis,unvis}.c used if missing. -AC_CHECK_FUNC(strnvis, found_strnvis=yes, found_strnvis=no) -if test "x$found_strnvis" = xyes; then +# Look for stravis, compat/{vis,unvis}.c used if missing. +AC_CHECK_FUNC(stravis, found_stravis=yes, found_stravis=no) +if test "x$found_stravis" = xyes; then + AC_MSG_CHECKING(if strnvis is broken) + AC_EGREP_HEADER([strnvis\(char \*, const char \*, size_t, int\)], + vis.h, + AC_MSG_RESULT(no), + [found_stravis=no]) + if test "x$found_stravis" = xno; then + AC_MSG_RESULT(yes) + fi +fi +if test "x$found_stravis" = xyes; then AC_DEFINE(HAVE_VIS) fi -AM_CONDITIONAL(NO_VIS, [test "x$found_strnvis" = xno]) +AM_CONDITIONAL(NO_VIS, [test "x$found_stravis" = xno]) + +# Look for cfmakeraw, compat/cfmakeraw.c used if missing. +AC_CHECK_FUNC(cfmakeraw, found_cfmakeraw=yes, found_cfmakeraw=no) +if test "x$found_cfmakeraw" = xyes; then + AC_DEFINE(HAVE_CFMAKERAW) +fi +AM_CONDITIONAL(NO_CFMAKERAW, [test "x$found_cfmakeraw" = xno]) + +# Look for openat, compat/openat.c used if missing. +AC_CHECK_FUNC(openat, found_openat=yes, found_openat=no) +if test "x$found_openat" = xyes; then + AC_DEFINE(HAVE_OPENAT) +fi +AM_CONDITIONAL(NO_OPENAT, [test "x$found_openat" = xno]) + +# Look for reallocarray, compat/reallocarray.c used if missing. +AC_CHECK_FUNC(reallocarray, found_reallocarray=yes, found_reallocarray=no) +if test "x$found_reallocarray" = xyes; then + AC_DEFINE(HAVE_REALLOCARRAY) +fi +AM_CONDITIONAL(NO_REALLOCARRAY, [test "x$found_reallocarray" = xno]) # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. @@ -374,16 +437,6 @@ if test "x$found_getopt" != xno; then fi AM_CONDITIONAL(NO_GETOPT, [test "x$found_getopt" = xno]) -# Check for some functions that are replaced or omitted. -AC_CHECK_FUNCS( - [ \ - bzero \ - dirfd \ - setproctitle \ - sysconf \ - ] -) - # Check for BSD-style integer types. AC_MSG_CHECKING(for BSD-style unsigned types) AC_COMPILE_IFELSE([AC_LANG_SOURCE( @@ -393,10 +446,10 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE( #include #else #include - #endif + #endif int main(void) { u_int8_t u8; u_int16_t u16; u_int32_t u32; u_int64_t u64; } - ])], + ])], [AC_DEFINE(HAVE_BSD_TYPES) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no) ) @@ -430,7 +483,7 @@ AC_LINK_IFELSE([AC_LANG_SOURCE( printf("%s\n", cp); exit(0); } - ])], + ])], [AC_DEFINE(HAVE___PROGNAME) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no) ) @@ -452,6 +505,10 @@ else AC_MSG_RESULT(no) fi +# Man page defaults to mdoc. +MANFORMAT=mdoc +AC_SUBST(MANFORMAT) + # Figure out the platform for osdep-*.c and forkpty-*.c. AC_MSG_CHECKING(platform) case "$host_os" in @@ -491,11 +548,16 @@ case "$host_os" in *solaris*) AC_MSG_RESULT(sunos) PLATFORM=sunos + MANFORMAT=man ;; *hpux*) AC_MSG_RESULT(hpux) PLATFORM=hpux ;; + *cygwin*) + AC_MSG_RESULT(cygwin) + PLATFORM=cygwin + ;; *) AC_MSG_RESULT(unknown) PLATFORM=unknown @@ -513,5 +575,5 @@ AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos) AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux) AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) -# autoconf should create a Makefile. A shock! +# autoconf should create a Makefile. AC_OUTPUT(Makefile) diff --git a/control-notify.c b/control-notify.c index a298cdc5..a40f8d7c 100644 --- a/control-notify.c +++ b/control-notify.c @@ -19,6 +19,8 @@ #include +#include + #include "tmux.h" #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ @@ -64,11 +66,13 @@ control_notify_window_layout_changed(struct window *w) struct session *s; struct format_tree *ft; struct winlink *wl; - u_int i; const char *template; + char *expanded; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + template = "%layout-change #{window_id} #{window_layout} " + "#{window_visible_layout} #{window_flags}"; + + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; s = c->session; @@ -83,42 +87,44 @@ control_notify_window_layout_changed(struct window *w) */ if (w->layout_root == NULL) continue; - template = "%layout-change #{window_id} #{window_layout}"; - ft = format_create(); + ft = format_create(NULL, 0); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { - format_winlink(ft, c->session, wl); - control_write(c, "%s", format_expand(ft, template)); + format_defaults(ft, c, NULL, wl, NULL); + expanded = format_expand(ft, template); + control_write(c, "%s", expanded); + free(expanded); } format_free(ft); } } void -control_notify_window_unlinked(unused struct session *s, struct window *w) +control_notify_window_unlinked(__unused struct session *s, struct window *w) { struct client *c; - u_int i; + struct session *cs; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; + cs = c->session; - control_write(c, "%%window-close @%u", w->id); + if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) + control_write(c, "%%window-close @%u", w->id); + else + control_write(c, "%%unlinked-window-close @%u", w->id); } } void -control_notify_window_linked(unused struct session *s, struct window *w) +control_notify_window_linked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; @@ -134,14 +140,20 @@ void control_notify_window_renamed(struct window *w) { struct client *c; - u_int i; + struct session *cs; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; + cs = c->session; - control_write(c, "%%window-renamed @%u %s", w->id, w->name); + if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) { + control_write(c, "%%window-renamed @%u %s", w->id, + w->name); + } else { + control_write(c, "%%unlinked-window-renamed @%u %s", + w->id, w->name); + } } } @@ -161,10 +173,8 @@ void control_notify_session_renamed(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; @@ -173,13 +183,11 @@ control_notify_session_renamed(struct session *s) } void -control_notify_session_created(unused struct session *s) +control_notify_session_created(__unused struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; @@ -188,13 +196,11 @@ control_notify_session_created(unused struct session *s) } void -control_notify_session_close(unused struct session *s) +control_notify_session_close(__unused struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; diff --git a/control.c b/control.c index ba243fd3..e799a4cb 100644 --- a/control.c +++ b/control.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2012 Nicholas Marriott @@ -27,7 +27,7 @@ #include "tmux.h" /* Write a line. */ -void printflike2 +void control_write(struct client *c, const char *fmt, ...) { va_list ap; @@ -37,7 +37,7 @@ control_write(struct client *c, const char *fmt, ...) va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } /* Write a buffer, adding a terminal newline. Empties buffer. */ @@ -46,15 +46,16 @@ control_write_buffer(struct client *c, struct evbuffer *buffer) { evbuffer_add_buffer(c->stdout_data, buffer); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } /* Control input callback. Read lines and fire commands. */ void -control_callback(struct client *c, int closed, unused void *data) +control_callback(struct client *c, int closed, __unused void *data) { char *line, *cause; struct cmd_list *cmdlist; + struct cmd *cmd; if (closed) c->flags |= CLIENT_EXIT; @@ -72,13 +73,15 @@ control_callback(struct client *c, int closed, unused void *data) c->cmdq->time = time(NULL); c->cmdq->number++; - cmdq_guard(c->cmdq, "begin"); + cmdq_guard(c->cmdq, "begin", 1); control_write(c, "parse error: %s", cause); - cmdq_guard(c->cmdq, "error"); + cmdq_guard(c->cmdq, "error", 1); free(cause); } else { - cmdq_run(c->cmdq, cmdlist); + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) + cmd->flags |= CMD_CONTROL; + cmdq_run(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); } diff --git a/environ.c b/environ.c index 3c0a5cb2..de560896 100644 --- a/environ.c +++ b/environ.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -27,6 +27,9 @@ * Environment - manipulate a set of environment variables. */ +RB_HEAD(environ, environ_entry); +int environ_cmp(struct environ_entry *, struct environ_entry *); +RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); RB_GENERATE(environ, environ_entry, entry, environ_cmp); int @@ -36,25 +39,42 @@ environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) } /* Initialise the environment. */ -void -environ_init(struct environ *env) +struct environ * +environ_create(void) { + struct environ *env; + + env = xcalloc(1, sizeof *env); RB_INIT(env); + + return (env); } /* Free an environment. */ void environ_free(struct environ *env) { - struct environ_entry *envent; + struct environ_entry *envent, *envent1; - while (!RB_EMPTY(env)) { - envent = RB_ROOT(env); + RB_FOREACH_SAFE(envent, environ, env, envent1) { RB_REMOVE(environ, env, envent); free(envent->name); free(envent->value); free(envent); } + free(env); +} + +struct environ_entry * +environ_first(struct environ *env) +{ + return (RB_MIN(environ, env)); +} + +struct environ_entry * +environ_next(struct environ_entry *envent) +{ + return (RB_NEXT(environ, env, envent)); } /* Copy one environment into another. */ @@ -63,8 +83,12 @@ environ_copy(struct environ *srcenv, struct environ *dstenv) { struct environ_entry *envent; - RB_FOREACH(envent, environ, srcenv) - environ_set(dstenv, envent->name, envent->value); + RB_FOREACH(envent, environ, srcenv) { + if (envent->value == NULL) + environ_clear(dstenv, envent->name); + else + environ_set(dstenv, envent->name, "%s", envent->value); + } } /* Find an environment variable. */ @@ -79,23 +103,37 @@ environ_find(struct environ *env, const char *name) /* Set an environment variable. */ void -environ_set(struct environ *env, const char *name, const char *value) +environ_set(struct environ *env, const char *name, const char *fmt, ...) +{ + struct environ_entry *envent; + va_list ap; + + va_start(ap, fmt); + if ((envent = environ_find(env, name)) != NULL) { + free(envent->value); + xvasprintf(&envent->value, fmt, ap); + } else { + envent = xmalloc(sizeof *envent); + envent->name = xstrdup(name); + xvasprintf(&envent->value, fmt, ap); + RB_INSERT(environ, env, envent); + } + va_end(ap); +} + +/* Clear an environment variable. */ +void +environ_clear(struct environ *env, const char *name) { struct environ_entry *envent; if ((envent = environ_find(env, name)) != NULL) { free(envent->value); - if (value != NULL) - envent->value = xstrdup(value); - else - envent->value = NULL; + envent->value = NULL; } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); - if (value != NULL) - envent->value = xstrdup(value); - else - envent->value = NULL; + envent->value = NULL; RB_INSERT(environ, env, envent); } } @@ -114,7 +152,7 @@ environ_put(struct environ *env, const char *var) name = xstrdup(var); name[strcspn(name, "=")] = '\0'; - environ_set(env, name, value); + environ_set(env, name, "%s", value); free(name); } @@ -137,7 +175,8 @@ environ_unset(struct environ *env, const char *name) * environment. */ void -environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv) +environ_update(const char *vars, struct environ *srcenv, + struct environ *dstenv) { struct environ_entry *envent; char *copyvars, *var, *next; @@ -145,9 +184,9 @@ environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv) copyvars = next = xstrdup(vars); while ((var = strsep(&next, " ")) != NULL) { if ((envent = environ_find(srcenv, var)) == NULL) - environ_set(dstenv, var, NULL); + environ_clear(dstenv, var); else - environ_set(dstenv, envent->name, envent->value); + environ_set(dstenv, envent->name, "%s", envent->value); } free(copyvars); } @@ -156,20 +195,16 @@ environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv) void environ_push(struct environ *env) { - ARRAY_DECL(, char *) varlist; - struct environ_entry *envent; - char **varp, *var; - u_int i; + struct environ_entry *envent; + char **vp, *v; - ARRAY_INIT(&varlist); - for (varp = environ; *varp != NULL; varp++) { - var = xstrdup(*varp); - var[strcspn(var, "=")] = '\0'; - ARRAY_ADD(&varlist, var); + for (vp = environ; *vp != NULL; vp++) { + v = xstrdup(*vp); + v[strcspn(v, "=")] = '\0'; + + unsetenv(v); + free(v); } - for (i = 0; i < ARRAY_LENGTH(&varlist); i++) - unsetenv(ARRAY_ITEM(&varlist, i)); - ARRAY_FREE(&varlist); RB_FOREACH(envent, environ, env) { if (envent->value != NULL) diff --git a/examples/tmux.vim b/examples/tmux.vim index 076115c1..d9b60408 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -31,71 +31,257 @@ syn keyword tmuxAction any current none syn keyword tmuxBoolean off on syn keyword tmuxCmds - \ attach[-session] detach[-client] has[-session] kill-server - \ kill-session lsc list-clients lscm list-commands ls list-sessions - \ lockc lock-client locks lock-session new[-session] refresh[-client] - \ rename[-session] showmsgs show-messages source[-file] start[-server] - \ suspendc suspend-client switchc switch-client + \ attach + \ attach-session + \ bind + \ bind-key + \ break-pane + \ breakp + \ capture-pane + \ capturep + \ choose-buffer + \ choose-client + \ choose-session + \ choose-tree + \ choose-window + \ clear-history + \ clearhist + \ clock-mode + \ command-prompt + \ confirm + \ confirm-before \ copy-mode - \ breakp break-pane capturep capture-pane choose-client choose-session - \ choose-tree choose-window displayp display-panes findw find-window - \ joinp join-pane killp kill-pane killw kill-window lastp last-pane - \ last[-window] linkw link-window lsp list-panes lsw list-windows movep - \ move-pane movew move-window neww new-window nextl next-layout - \ next[-window] pipep pipe-pane prevl previous-layout prev[ious-window] - \ renamew rename-window resizep resize-pane respawnp respawn-pane - \ respawnw respawn-window rotatew rotate-window selectl select-layout - \ selectp select-pane selectw select-window splitw split-window swapp - \ swap-pane swapw swap-window unlinkw unlink-window - \ bind[-key] lsk list-keys send[-keys] send-prefix unbind[-key] - \ set[-option] setw set-window-option show[-options] showw + \ delete-buffer + \ deleteb + \ detach + \ detach-client + \ display + \ display-message + \ display-panes + \ displayp + \ find-window + \ findw + \ has + \ has-session + \ if + \ if-shell + \ info + \ join-pane + \ joinp + \ kill-pane + \ kill-server + \ kill-session + \ kill-window + \ killp + \ killw + \ last + \ last-pane + \ last-window + \ lastp + \ link-window + \ linkw + \ list-buffers + \ list-clients + \ list-commands + \ list-keys + \ list-panes + \ list-sessions + \ list-windows + \ load-buffer + \ loadb + \ lock + \ lock-client + \ lock-server + \ lock-session + \ lockc + \ locks + \ ls + \ lsb + \ lsc + \ lscm + \ lsk + \ lsp + \ lsw + \ move-pane + \ move-window + \ movep + \ movew + \ new + \ new-session + \ new-window + \ neww + \ next + \ next-layout + \ next-window + \ nextl + \ paste-buffer + \ pasteb + \ path + \ pipe-pane + \ pipep + \ prev + \ previous-layout + \ previous-window + \ prevl + \ refresh + \ refresh-client + \ rename + \ rename-session + \ rename-window + \ renamew + \ resize-pane + \ resizep + \ respawn-pane + \ respawn-window + \ respawnp + \ respawnw + \ rotate-window + \ rotatew + \ run + \ run-shell + \ save-buffer + \ saveb + \ select-layout + \ select-pane + \ select-window + \ selectl + \ selectp + \ selectw + \ send + \ send-keys + \ send-prefix + \ server-info + \ set + \ set-buffer + \ set-environment + \ set-option + \ set-window-option + \ setb + \ setenv + \ setw + \ show + \ show-buffer + \ show-environment + \ show-messages + \ show-options \ show-window-options - \ setenv set-environment showenv show-environment - \ command-prompt confirm[-before] display[-message] - \ choose-buffer clearhist clear-history deleteb delete-buffer lsb - \ list-buffers loadb load-buffer pasteb paste-buffer saveb save-buffer - \ setb set-buffer showb show-buffer - \ clock-mode if[-shell] lock[-server] run[-shell] server-info info - \ choose-list + \ showb + \ showenv + \ showmsgs + \ showw + \ source + \ source-file + \ split-window + \ splitw + \ start + \ start-server + \ suspend-client + \ suspendc + \ swap-pane + \ swap-window + \ swapp + \ swapw + \ switch-client + \ switchc + \ unbind + \ unbind-key + \ unlink-window + \ unlinkw + \ wait + \ wait-for syn keyword tmuxOptsSet - \ buffer-limit escape-time exit-unattached exit-unattached quiet + \ assume-paste-time + \ base-index + \ bell-action + \ bell-on-alert + \ buffer-limit + \ default-command + \ default-shell + \ default-terminal + \ destroy-unattached + \ detach-on-destroy + \ display-panes-active-colour + \ display-panes-colour + \ display-panes-time + \ display-time + \ escape-time + \ exit-unattached + \ focus-events + \ history-file + \ history-limit + \ lock-after-time + \ lock-command + \ message-command-style + \ message-limit + \ message-style + \ mouse + \ mouse-utf8 + \ prefix + \ prefix2 + \ quiet + \ renumber-windows + \ repeat-time \ set-clipboard - \ base-index bell-action bell-on-alert default-command default-path - \ default-shell default-terminal destroy-unattached detach-on-destroy - \ display-panes-active-colour display-panes-colour display-panes-time - \ display-time history-limit - \ lock-after-time lock-command lock-server - \ message-command-attr message-attr message-command-bg message-bg - \ message-command-fg message-fg message-limit - \ mouse-resize-pane mouse-select-pane mouse-select-window mouse-utf8 - \ pane-active-border-bg pane-border-bg pane-active-border-fg - \ pane-border-fg prefix prefix2 - \ renumber-windows repeat-time set-remain-on-exit set-titles - \ set-titles-string status status-attr status-bg status-fg - \ status-interval status-justify status-keys status-left - \ status-left-attr status-left-bg status-left-fg status-left-length - \ status-position status-right status-right-attr status-right-bg - \ status-right-fg status-right-length status-utf8 terminal-overrides - \ update-environment visual-activity visual-bell visual-content - \ visual-silence word-separators + \ set-remain-on-exit + \ set-titles + \ set-titles-string + \ status + \ status-interval + \ status-justify + \ status-keys + \ status-left + \ status-left-length + \ status-left-style + \ status-position + \ status-right + \ status-right-length + \ status-right-style + \ status-style + \ status-utf8 + \ terminal-overrides + \ update-environment + \ visual-activity + \ visual-bell + \ visual-silence + \ word-separators syn keyword tmuxOptsSetw - \ aggressive-resize alternate-screen automatic-rename - \ c0-change-interval c0-change-trigger clock-mode-colour - \ clock-mode-style force-height force-width layout-history-limit - \ main-pane-height main-pane-width mode-attr mode-bg mode-fg move-keys - \ mode-mouse monitor-activity monitor-content monitor-silence - \ other-pane-height other-pane-width pane-base-index remain-on-exit - \ synchronize-panes utf8 window-status-bell-attr window-status-bell-bg - \ window-status-bell-fg window-status-content-attr - \ window-status-content-bg window-status-content-fg - \ window-status-activity-attr window-status-activity-bg - \ window-status-activity-fg window-status-attr - \ window-status-current-attr window-status-attr window-status-current-bg - \ window-status-bg window-status-current-fg window-status-fg - \ window-status-current-format window-status-format - \ window-status-separator xterm-keys wrap-search + \ aggressive-resize + \ allow-rename + \ alternate-screen + \ automatic-rename + \ automatic-rename-format + \ clock-mode-colour + \ clock-mode-style + \ force-height + \ force-width + \ main-pane-height + \ main-pane-width + \ mode-keys + \ mode-style + \ monitor-activity + \ monitor-silence + \ other-pane-height + \ other-pane-width + \ pane-active-border-style + \ pane-base-index + \ pane-border-style + \ remain-on-exit + \ synchronize-panes + \ utf8 + \ window-active-style + \ window-status-activity-style + \ window-status-bell-style + \ window-status-current-format + \ window-status-current-style + \ window-status-format + \ window-status-last-style + \ window-status-separator + \ window-status-style + \ window-style + \ wrap-search + \ xterm-keys syn keyword tmuxTodo FIXME NOTE TODO XXX contained @@ -105,7 +291,9 @@ syn match tmuxOptions /\s-\a\+/ display syn match tmuxVariable /\w\+=/ display syn match tmuxVariableExpansion /\${\=\w\+}\=/ display -syn region tmuxComment start=/#/ end=/$/ contains=tmuxTodo display oneline +" Comments can span multiple lines, when the newline is escaped +" (with a single) backslash at the end. +syn region tmuxComment start=/#/ skip=/\\\@=\e[1;*A" + execute "set =\e[1;*B" + execute "set =\e[1;*C" + execute "set =\e[1;*D" + + execute "set =\e[1;*H" + execute "set =\e[1;*F" + + execute "set =\e[2;*~" + execute "set =\e[3;*~" + execute "set =\e[5;*~" + execute "set =\e[6;*~" + + execute "set =\e[1;*P" + execute "set =\e[1;*Q" + execute "set =\e[1;*R" + execute "set =\e[1;*S" + + execute "set =\e[15;*~" + execute "set =\e[17;*~" + execute "set =\e[18;*~" + execute "set =\e[19;*~" + execute "set =\e[20;*~" + execute "set =\e[21;*~" + execute "set =\e[23;*~" + execute "set =\e[24;*~" +endfunction + +if exists('$TMUX') + call s:SetXtermCapabilities() +endif diff --git a/format.c b/format.c index 4acae393..2a65cdc6 100644 --- a/format.c +++ b/format.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -17,7 +17,12 @@ */ #include +#include +#include +#include +#include +#include #include #include #include @@ -33,22 +38,104 @@ * string. */ -int format_replace(struct format_tree *, const char *, size_t, char **, - size_t *, size_t *); -void format_window_pane_tabs(struct format_tree *, struct window_pane *); +struct format_entry; +typedef void (*format_cb)(struct format_tree *, struct format_entry *); -/* Format key-value replacement entry. */ -RB_GENERATE(format_tree, format_entry, entry, format_cmp); +void format_job_callback(struct job *); +char *format_job_get(struct format_tree *, const char *); +void format_job_timer(int, short, void *); -/* Format tree comparison function. */ +void format_cb_host(struct format_tree *, struct format_entry *); +void format_cb_host_short(struct format_tree *, struct format_entry *); +void format_cb_pid(struct format_tree *, struct format_entry *); +void format_cb_session_alerts(struct format_tree *, struct format_entry *); +void format_cb_window_layout(struct format_tree *, struct format_entry *); +void format_cb_window_visible_layout(struct format_tree *, + struct format_entry *); +void format_cb_start_command(struct format_tree *, struct format_entry *); +void format_cb_current_command(struct format_tree *, struct format_entry *); +void format_cb_current_path(struct format_tree *, struct format_entry *); +void format_cb_history_bytes(struct format_tree *, struct format_entry *); +void format_cb_pane_tabs(struct format_tree *, struct format_entry *); + +char *format_find(struct format_tree *, const char *, int); +void format_add_cb(struct format_tree *, const char *, format_cb); +void format_add_tv(struct format_tree *, const char *, struct timeval *); +int format_replace(struct format_tree *, const char *, size_t, char **, + size_t *, size_t *); +char *format_time_string(time_t); + +void format_defaults_pane_tabs(struct format_tree *, struct window_pane *); +void format_defaults_session(struct format_tree *, struct session *); +void format_defaults_client(struct format_tree *, struct client *); +void format_defaults_winlink(struct format_tree *, struct session *, + struct winlink *); + +/* Entry in format job tree. */ +struct format_job { + const char *cmd; + + time_t last; + char *out; + + struct job *job; + int status; + + RB_ENTRY(format_job) entry; +}; + +/* Format job tree. */ +struct event format_job_event; +int format_job_cmp(struct format_job *, struct format_job *); +RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); +RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp); +RB_GENERATE(format_job_tree, format_job, entry, format_job_cmp); + +/* Format job tree comparison function. */ int -format_cmp(struct format_entry *fe1, struct format_entry *fe2) +format_job_cmp(struct format_job *fj1, struct format_job *fj2) +{ + return (strcmp(fj1->cmd, fj2->cmd)); +} + +/* Format modifiers. */ +#define FORMAT_TIMESTRING 0x1 +#define FORMAT_BASENAME 0x2 +#define FORMAT_DIRNAME 0x4 +#define FORMAT_SUBSTITUTE 0x8 + +/* Entry in format tree. */ +struct format_entry { + char *key; + char *value; + time_t t; + format_cb cb; + RB_ENTRY(format_entry) entry; +}; + +/* Format entry tree. */ +struct format_tree { + struct window *w; + struct session *s; + struct window_pane *wp; + + int flags; + + RB_HEAD(format_entry_tree, format_entry) tree; +}; +int format_entry_cmp(struct format_entry *, struct format_entry *); +RB_PROTOTYPE(format_entry_tree, format_entry, entry, format_entry_cmp); +RB_GENERATE(format_entry_tree, format_entry, entry, format_entry_cmp); + +/* Format entry tree comparison function. */ +int +format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2) { return (strcmp(fe1->key, fe2->key)); } -/* Single-character aliases. */ -const char *format_aliases[26] = { +/* Single-character uppercase aliases. */ +const char *format_upper[] = { NULL, /* A */ NULL, /* B */ NULL, /* C */ @@ -77,18 +164,346 @@ const char *format_aliases[26] = { NULL /* Z */ }; +/* Single-character lowercase aliases. */ +const char *format_lower[] = { + NULL, /* a */ + NULL, /* b */ + NULL, /* c */ + NULL, /* d */ + NULL, /* e */ + NULL, /* f */ + NULL, /* g */ + "host_short", /* h */ + NULL, /* i */ + NULL, /* j */ + NULL, /* k */ + NULL, /* l */ + NULL, /* m */ + NULL, /* n */ + NULL, /* o */ + NULL, /* p */ + NULL, /* q */ + NULL, /* r */ + NULL, /* s */ + NULL, /* t */ + NULL, /* u */ + NULL, /* v */ + NULL, /* w */ + NULL, /* x */ + NULL, /* y */ + NULL /* z */ +}; + +/* Format job callback. */ +void +format_job_callback(struct job *job) +{ + struct format_job *fj = job->data; + char *line, *buf; + size_t len; + struct client *c; + + fj->job = NULL; + free(fj->out); + + buf = NULL; + if ((line = evbuffer_readline(job->event->input)) == NULL) { + len = EVBUFFER_LENGTH(job->event->input); + buf = xmalloc(len + 1); + if (len != 0) + memcpy(buf, EVBUFFER_DATA(job->event->input), len); + buf[len] = '\0'; + } else + buf = line; + fj->out = buf; + + if (fj->status) { + TAILQ_FOREACH(c, &clients, entry) + server_status_client(c); + fj->status = 0; + } + + log_debug("%s: %s: %s", __func__, fj->cmd, fj->out); +} + +/* Find a job. */ +char * +format_job_get(struct format_tree *ft, const char *cmd) +{ + struct format_job fj0, *fj; + time_t t; + + fj0.cmd = cmd; + if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) { + fj = xcalloc(1, sizeof *fj); + fj->cmd = xstrdup(cmd); + + xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); + + RB_INSERT(format_job_tree, &format_jobs, fj); + } + + t = time(NULL); + if (fj->job == NULL && ((ft->flags & FORMAT_FORCE) || fj->last != t)) { + fj->job = job_run(fj->cmd, NULL, NULL, format_job_callback, + NULL, fj); + if (fj->job == NULL) { + free(fj->out); + xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); + } + fj->last = t; + } + + if (ft->flags & FORMAT_STATUS) + fj->status = 1; + + return (format_expand(ft, fj->out)); +} + +/* Remove old jobs. */ +void +format_job_timer(__unused int fd, __unused short events, __unused void *arg) +{ + struct format_job *fj, *fj1; + time_t now; + struct timeval tv = { .tv_sec = 60 }; + + now = time(NULL); + RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) { + if (fj->last > now || now - fj->last < 3600) + continue; + RB_REMOVE(format_job_tree, &format_jobs, fj); + + log_debug("%s: %s", __func__, fj->cmd); + + if (fj->job != NULL) + job_free(fj->job); + + free((void *)fj->cmd); + free(fj->out); + + free(fj); + } + + evtimer_del(&format_job_event); + evtimer_add(&format_job_event, &tv); +} + +/* Callback for host. */ +void +format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) +{ + char host[HOST_NAME_MAX + 1]; + + if (gethostname(host, sizeof host) != 0) + fe->value = xstrdup(""); + else + fe->value = xstrdup(host); +} + +/* Callback for host_short. */ +void +format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) +{ + char host[HOST_NAME_MAX + 1], *cp; + + if (gethostname(host, sizeof host) != 0) + fe->value = xstrdup(""); + else { + if ((cp = strchr(host, '.')) != NULL) + *cp = '\0'; + fe->value = xstrdup(host); + } +} + +/* Callback for pid. */ +void +format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) +{ + xasprintf(&fe->value, "%ld", (long)getpid()); +} + +/* Callback for session_alerts. */ +void +format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) +{ + struct session *s = ft->s; + struct winlink *wl; + char alerts[256], tmp[16]; + + if (s == NULL) + return; + + *alerts = '\0'; + RB_FOREACH(wl, winlinks, &s->windows) { + if ((wl->flags & WINLINK_ALERTFLAGS) == 0) + continue; + xsnprintf(tmp, sizeof tmp, "%u", wl->idx); + + if (*alerts != '\0') + strlcat(alerts, ",", sizeof alerts); + strlcat(alerts, tmp, sizeof alerts); + if (wl->flags & WINLINK_ACTIVITY) + strlcat(alerts, "#", sizeof alerts); + if (wl->flags & WINLINK_BELL) + strlcat(alerts, "!", sizeof alerts); + if (wl->flags & WINLINK_SILENCE) + strlcat(alerts, "~", sizeof alerts); + } + fe->value = xstrdup(alerts); +} + +/* Callback for window_layout. */ +void +format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->w; + + if (w == NULL) + return; + + if (w->saved_layout_root != NULL) + fe->value = layout_dump(w->saved_layout_root); + else + fe->value = layout_dump(w->layout_root); +} + +/* Callback for window_visible_layout. */ +void +format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->w; + + if (w == NULL) + return; + + fe->value = layout_dump(w->layout_root); +} + +/* Callback for pane_start_command. */ +void +format_cb_start_command(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + + if (wp == NULL) + return; + + fe->value = cmd_stringify_argv(wp->argc, wp->argv); +} + +/* Callback for pane_current_command. */ +void +format_cb_current_command(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + char *cmd; + + if (wp == NULL) + return; + + cmd = osdep_get_name(wp->fd, wp->tty); + if (cmd == NULL || *cmd == '\0') { + free(cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); + if (cmd == NULL || *cmd == '\0') { + free(cmd); + cmd = xstrdup(wp->shell); + } + } + fe->value = parse_window_name(cmd); + free(cmd); +} + +/* Callback for pane_current_path. */ +void +format_cb_current_path(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + char *cwd; + + if (wp == NULL) + return; + + cwd = osdep_get_cwd(wp->fd); + if (cwd != NULL) + fe->value = xstrdup(cwd); +} + +/* Callback for history_bytes. */ +void +format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct grid *gd; + struct grid_line *gl; + unsigned long long size; + u_int i; + + if (wp == NULL) + return; + gd = wp->base.grid; + + size = 0; + for (i = 0; i < gd->hsize; i++) { + gl = &gd->linedata[i]; + size += gl->cellsize * sizeof *gl->celldata; + size += gl->extdsize * sizeof *gl->extddata; + } + size += gd->hsize * sizeof *gd->linedata; + + xasprintf(&fe->value, "%llu", size); +} + +/* Callback for pane_tabs. */ +void +format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct evbuffer *buffer; + u_int i; + int size; + + if (wp == NULL) + return; + + buffer = evbuffer_new(); + for (i = 0; i < wp->base.grid->sx; i++) { + if (!bit_test(wp->base.tabs, i)) + continue; + + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%u", i); + } + size = EVBUFFER_LENGTH(buffer); + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Create a new tree. */ struct format_tree * -format_create(void) +format_create(struct cmd_q *cmdq, int flags) { struct format_tree *ft; - char host[MAXHOSTNAMELEN]; - ft = xmalloc(sizeof *ft); - RB_INIT(ft); + if (!event_initialized(&format_job_event)) { + evtimer_set(&format_job_event, format_job_timer, NULL); + format_job_timer(-1, 0, NULL); + } - if (gethostname(host, sizeof host) == 0) - format_add(ft, "host", "%s", host); + ft = xcalloc(1, sizeof *ft); + RB_INIT(&ft->tree); + ft->flags = flags; + + format_add_cb(ft, "host", format_cb_host); + format_add_cb(ft, "host_short", format_cb_host_short); + format_add_cb(ft, "pid", format_cb_pid); + format_add(ft, "socket_path", "%s", socket_path); + format_add_tv(ft, "start_time", &start_time); + + if (cmdq != NULL && cmdq->cmd != NULL) + format_add(ft, "command_name", "%s", cmdq->cmd->entry->name); return (ft); } @@ -97,20 +512,16 @@ format_create(void) void format_free(struct format_tree *ft) { - struct format_entry *fe, *fe_next; + struct format_entry *fe, *fe1; - fe_next = RB_MIN(format_tree, ft); - while (fe_next != NULL) { - fe = fe_next; - fe_next = RB_NEXT(format_tree, ft, fe); - - RB_REMOVE(format_tree, ft, fe); + RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { + RB_REMOVE(format_entry_tree, &ft->tree, fe); free(fe->value); free(fe->key); free(fe); } - free (ft); + free(ft); } /* Add a key-value pair. */ @@ -118,29 +529,166 @@ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) { struct format_entry *fe; + struct format_entry *fe_now; va_list ap; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); + if (fe_now != NULL) { + free(fe->key); + free(fe); + free(fe_now->value); + fe = fe_now; + } + + fe->cb = NULL; + fe->t = 0; + va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); +} - RB_INSERT(format_tree, ft, fe); +/* Add a key and time. */ +void +format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) +{ + struct format_entry *fe; + struct format_entry *fe_now; + + fe = xmalloc(sizeof *fe); + fe->key = xstrdup(key); + + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); + if (fe_now != NULL) { + free(fe->key); + free(fe); + free(fe_now->value); + fe = fe_now; + } + + fe->cb = NULL; + fe->t = tv->tv_sec; + + fe->value = NULL; +} + +/* Add a key and function. */ +void +format_add_cb(struct format_tree *ft, const char *key, format_cb cb) +{ + struct format_entry *fe; + struct format_entry *fe_now; + + fe = xmalloc(sizeof *fe); + fe->key = xstrdup(key); + + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); + if (fe_now != NULL) { + free(fe->key); + free(fe); + free(fe_now->value); + fe = fe_now; + } + + fe->cb = cb; + fe->t = 0; + + fe->value = NULL; } /* Find a format entry. */ -const char * -format_find(struct format_tree *ft, const char *key) +char * +format_find(struct format_tree *ft, const char *key, int modifiers) { struct format_entry *fe, fe_find; + struct options_entry *o; + struct environ_entry *envent; + static char s[64]; + const char *found; + char *copy, *saved; + + found = NULL; + + if (~modifiers & FORMAT_TIMESTRING) { + o = options_find(global_options, key); + if (o == NULL && ft->w != NULL) + o = options_find(ft->w->options, key); + if (o == NULL) + o = options_find(global_w_options, key); + if (o == NULL && ft->s != NULL) + o = options_find(ft->s->options, key); + if (o == NULL) + o = options_find(global_s_options, key); + if (o != NULL) { + switch (o->type) { + case OPTIONS_STRING: + found = o->str; + goto found; + case OPTIONS_NUMBER: + xsnprintf(s, sizeof s, "%lld", o->num); + found = s; + goto found; + case OPTIONS_STYLE: + found = style_tostring(&o->style); + goto found; + } + } + } fe_find.key = (char *) key; - fe = RB_FIND(format_tree, ft, &fe_find); - if (fe == NULL) + fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); + if (fe != NULL) { + if (modifiers & FORMAT_TIMESTRING) { + if (fe->t == 0) + return (NULL); + ctime_r(&fe->t, s); + s[strcspn(s, "\n")] = '\0'; + found = s; + goto found; + } + if (fe->t != 0) { + xsnprintf(s, sizeof s, "%lld", (long long)fe->t); + found = s; + goto found; + } + if (fe->value == NULL && fe->cb != NULL) + fe->cb(ft, fe); + found = fe->value; + goto found; + } + + if (~modifiers & FORMAT_TIMESTRING) { + envent = NULL; + if (ft->s != NULL) + envent = environ_find(ft->s->environ, key); + if (envent == NULL) + envent = environ_find(global_environ, key); + if (envent != NULL) { + found = envent->value; + goto found; + } + } + + return (NULL); + +found: + if (found == NULL) return (NULL); - return (fe->value); + copy = xstrdup(found); + if (modifiers & FORMAT_BASENAME) { + saved = copy; + copy = xstrdup(basename(saved)); + free(saved); + } + if (modifiers & FORMAT_DIRNAME) { + saved = copy; + copy = xstrdup(dirname(saved)); + free(saved); + } + return (copy); } /* @@ -148,18 +696,74 @@ format_find(struct format_tree *ft, const char *key) * #{?blah,a,b} is replace with a if blah exists and is nonzero else b. */ int -format_replace(struct format_tree *ft, - const char *key, size_t keylen, char **buf, size_t *len, size_t *off) +format_replace(struct format_tree *ft, const char *key, size_t keylen, + char **buf, size_t *len, size_t *off) { - char *copy, *ptr; - const char *value; - size_t valuelen; + char *copy, *copy0, *endptr, *ptr, *found, *new, *value; + char *from = NULL, *to = NULL; + size_t valuelen, newlen, fromlen, tolen, used; + u_long limit = 0; + int modifiers = 0, brackets; /* Make a copy of the key. */ - copy = xmalloc(keylen + 1); + copy0 = copy = xmalloc(keylen + 1); memcpy(copy, key, keylen); copy[keylen] = '\0'; + /* Is there a length limit or whatnot? */ + switch (copy[0]) { + case '=': + errno = 0; + limit = strtoul(copy + 1, &endptr, 10); + if (errno == ERANGE && limit == ULONG_MAX) + break; + if (*endptr != ':') + break; + copy = endptr + 1; + break; + case 'b': + if (copy[1] != ':') + break; + modifiers |= FORMAT_BASENAME; + copy += 2; + break; + case 'd': + if (copy[1] != ':') + break; + modifiers |= FORMAT_DIRNAME; + copy += 2; + break; + case 't': + if (copy[1] != ':') + break; + modifiers |= FORMAT_TIMESTRING; + copy += 2; + break; + case 's': + if (copy[1] != '/') + break; + from = copy + 2; + for (copy = from; *copy != '\0' && *copy != '/'; copy++) + /* nothing */; + if (copy[0] != '/' || copy == from) { + copy = copy0; + break; + } + copy[0] = '\0'; + to = copy + 1; + for (copy = to; *copy != '\0' && *copy != '/'; copy++) + /* nothing */; + if (copy[0] != '/' || copy[1] != ':') { + copy = copy0; + break; + } + copy[0] = '\0'; + + modifiers |= FORMAT_SUBSTITUTE; + copy += 2; + break; + } + /* * Is this a conditional? If so, check it exists and extract either the * first or second element. If not, look up the key directly. @@ -170,50 +774,125 @@ format_replace(struct format_tree *ft, goto fail; *ptr = '\0'; - value = format_find(ft, copy + 1); - if (value != NULL && (value[0] != '0' || value[1] != '\0')) { - value = ptr + 1; - ptr = strchr(value, ','); - if (ptr == NULL) - goto fail; - *ptr = '\0'; - } else { - ptr = strchr(ptr + 1, ','); - if (ptr == NULL) - goto fail; - value = ptr + 1; + value = ptr + 1; + found = format_find(ft, copy + 1, modifiers); + + brackets = 0; + for (ptr = ptr + 1; *ptr != '\0'; ptr++) { + if (*ptr == '{') + brackets++; + if (*ptr == '}') + brackets--; + if (*ptr == ',' && brackets == 0) + break; } + if (*ptr == '\0') + goto fail; + + if (found != NULL && *found != '\0' && + (found[0] != '0' || found[1] != '\0')) { + *ptr = '\0'; + } else + value = ptr + 1; + value = format_expand(ft, value); + free(found); } else { - value = format_find(ft, copy); + value = format_find(ft, copy, modifiers); if (value == NULL) - value = ""; + value = xstrdup(""); + } + + /* Perform substitution if any. */ + if (modifiers & FORMAT_SUBSTITUTE) { + fromlen = strlen(from); + tolen = strlen(to); + + newlen = strlen(value) + 1; + copy = new = xmalloc(newlen); + for (ptr = value; *ptr != '\0'; /* nothing */) { + if (strncmp(ptr, from, fromlen) != 0) { + *new++ = *ptr++; + continue; + } + used = new - copy; + + newlen += tolen; + copy = xrealloc(copy, newlen); + + new = copy + used; + memcpy(new, to, tolen); + + new += tolen; + ptr += fromlen; + } + *new = '\0'; + free(value); + value = copy; + } + + /* Truncate the value if needed. */ + if (limit != 0) { + new = utf8_trimcstr(value, limit); + free(value); + value = new; } - valuelen = strlen(value); /* Expand the buffer and copy in the value. */ + valuelen = strlen(value); while (*len - *off < valuelen + 1) { - *buf = xrealloc(*buf, 2, *len); + *buf = xreallocarray(*buf, 2, *len); *len *= 2; } memcpy(*buf + *off, value, valuelen); *off += valuelen; - free(copy); + free(value); + free(copy0); return (0); fail: - free(copy); + free(copy0); return (-1); } +/* Expand keys in a template, passing through strftime first. */ +char * +format_expand_time(struct format_tree *ft, const char *fmt, time_t t) +{ + char *tmp, *expanded; + size_t tmplen; + struct tm *tm; + + if (fmt == NULL || *fmt == '\0') + return (xstrdup("")); + + tm = localtime(&t); + + tmp = NULL; + tmplen = strlen(fmt); + + do { + tmp = xreallocarray(tmp, 2, tmplen); + tmplen *= 2; + } while (strftime(tmp, tmplen, fmt, tm) == 0); + + expanded = format_expand(ft, tmp); + free(tmp); + + return (expanded); +} + /* Expand keys in a template. */ char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *ptr; - const char *s; - size_t off, len, n; - int ch; + char *buf, *tmp, *cmd, *out; + const char *ptr, *s, *saved = fmt; + size_t off, len, n, outlen; + int ch, brackets; + + if (fmt == NULL) + return (xstrdup("")); #ifdef TMATE tmate_format(ft); @@ -226,7 +905,7 @@ format_expand(struct format_tree *ft, const char *fmt) while (*fmt != '\0') { if (*fmt != '#') { while (len - off < 2) { - buf = xrealloc(buf, 2, len); + buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = *fmt++; @@ -236,9 +915,49 @@ format_expand(struct format_tree *ft, const char *fmt) ch = (u_char) *fmt++; switch (ch) { + case '(': + brackets = 1; + for (ptr = fmt; *ptr != '\0'; ptr++) { + if (*ptr == '(') + brackets++; + if (*ptr == ')' && --brackets == 0) + break; + } + if (*ptr != ')' || brackets != 0) + break; + n = ptr - fmt; + + tmp = xmalloc(n + 1); + memcpy(tmp, fmt, n); + tmp[n] = '\0'; + cmd = format_expand(ft, tmp); + + out = format_job_get(ft, cmd); + outlen = strlen(out); + + free(cmd); + free(tmp); + + while (len - off < outlen + 1) { + buf = xreallocarray(buf, 2, len); + len *= 2; + } + memcpy(buf + off, out, outlen); + off += outlen; + + free(out); + + fmt += n + 1; + continue; case '{': - ptr = strchr(fmt, '}'); - if (ptr == NULL) + brackets = 1; + for (ptr = fmt; *ptr != '\0'; ptr++) { + if (*ptr == '{') + brackets++; + if (*ptr == '}' && --brackets == 0) + break; + } + if (*ptr != '}' || brackets != 0) break; n = ptr - fmt; @@ -246,23 +965,31 @@ format_expand(struct format_tree *ft, const char *fmt) break; fmt += n + 1; continue; - default: - if (ch >= 'A' && ch <= 'Z') { - s = format_aliases[ch - 'A']; - if (s != NULL) { - n = strlen(s); - if (format_replace ( - ft, s, n, &buf, &len, &off) != 0) - break; - continue; - } - } - while (len - off < 3) { - buf = xrealloc(buf, 2, len); + case '#': + while (len - off < 2) { + buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = '#'; - buf[off++] = ch; + continue; + default: + s = NULL; + if (ch >= 'A' && ch <= 'Z') + s = format_upper[ch - 'A']; + else if (ch >= 'a' && ch <= 'z') + s = format_lower[ch - 'a']; + if (s == NULL) { + while (len - off < 3) { + buf = xreallocarray(buf, 2, len); + len *= 2; + } + buf[off++] = '#'; + buf[off++] = ch; + continue; + } + n = strlen(s); + if (format_replace(ft, s, n, &buf, &len, &off) != 0) + break; continue; } @@ -270,16 +997,39 @@ format_expand(struct format_tree *ft, const char *fmt) } buf[off] = '\0'; + log_debug("format '%s' -> '%s'", saved, buf); return (buf); } +/* Set defaults for any of arguments that are not NULL. */ +void +format_defaults(struct format_tree *ft, struct client *c, struct session *s, + struct winlink *wl, struct window_pane *wp) +{ + if (s == NULL && c != NULL) + s = c->session; + if (wl == NULL && s != NULL) + wl = s->curw; + if (wp == NULL && wl != NULL) + wp = wl->window->active; + + if (c != NULL) + format_defaults_client(ft, c); + if (s != NULL) + format_defaults_session(ft, s); + if (s != NULL && wl != NULL) + format_defaults_winlink(ft, s, wl); + if (wp != NULL) + format_defaults_pane(ft, wp); +} + /* Set default format keys for a session. */ void -format_session(struct format_tree *ft, struct session *s) +format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; - char *tim; - time_t t; + + ft->s = s; format_add(ft, "session_name", "%s", s->name); format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); @@ -292,45 +1042,45 @@ format_session(struct format_tree *ft, struct session *s) if (sg != NULL) format_add(ft, "session_group", "%u", session_group_index(sg)); - t = s->creation_time.tv_sec; - format_add(ft, "session_created", "%ld", (long) t); - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - format_add(ft, "session_created_string", "%s", tim); + format_add_tv(ft, "session_created", &s->creation_time); + format_add_tv(ft, "session_last_attached", &s->last_attached_time); + format_add_tv(ft, "session_activity", &s->activity_time); - if (s->flags & SESSION_UNATTACHED) - format_add(ft, "session_attached", "%d", 0); - else - format_add(ft, "session_attached", "%d", 1); + format_add(ft, "session_attached", "%u", s->attached); + format_add(ft, "session_many_attached", "%d", s->attached > 1); + + format_add_cb(ft, "session_alerts", format_cb_session_alerts); } /* Set default format keys for a client. */ void -format_client(struct format_tree *ft, struct client *c) +format_defaults_client(struct format_tree *ft, struct client *c) { - char *tim; - time_t t; struct session *s; + const char *name; - format_add(ft, "client_cwd", "%s", c->cwd); + if (ft->s == NULL) + ft->s = c->session; + + format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); - format_add(ft, "client_tty", "%s", c->tty.path); - format_add(ft, "client_termname", "%s", c->tty.termname); + if (c->tty.path != NULL) + format_add(ft, "client_tty", "%s", c->tty.path); + if (c->tty.termname != NULL) + format_add(ft, "client_termname", "%s", c->tty.termname); + format_add(ft, "client_control_mode", "%d", + !!(c->flags & CLIENT_CONTROL)); - t = c->creation_time.tv_sec; - format_add(ft, "client_created", "%ld", (long) t); - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - format_add(ft, "client_created_string", "%s", tim); + format_add_tv(ft, "client_created", &c->creation_time); + format_add_tv(ft, "client_activity", &c->activity_time); - t = c->activity_time.tv_sec; - format_add(ft, "client_activity", "%ld", (long) t); - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - format_add(ft, "client_activity_string", "%s", tim); - - format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX)); + name = server_client_get_key_table(c); + if (strcmp(c->keytable->name, name) == 0) + format_add(ft, "client_prefix", "%d", 0); + else + format_add(ft, "client_prefix", "%d", 1); + format_add(ft, "client_key_table", "%s", c->keytable->name); if (c->tty.flags & TTY_UTF8) format_add(ft, "client_utf8", "%d", 1); @@ -350,72 +1100,72 @@ format_client(struct format_tree *ft, struct client *c) format_add(ft, "client_last_session", "%s", s->name); } -/* Set default format keys for a winlink. */ +/* Set default format keys for a window. */ void -format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) +format_defaults_window(struct format_tree *ft, struct window *w) { - struct window *w = wl->window; - char *layout, *flags; - - layout = layout_dump(w); - flags = window_printable_flags(s, wl); + ft->w = w; + format_add_tv(ft, "window_activity", &w->activity_time); format_add(ft, "window_id", "@%u", w->id); - format_add(ft, "window_index", "%d", wl->idx); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); - format_add(ft, "window_flags", "%s", flags); - format_add(ft, "window_layout", "%s", layout); - format_add(ft, "window_active", "%d", wl == s->curw); + format_add_cb(ft, "window_layout", format_cb_window_layout); + format_add_cb(ft, "window_visible_layout", + format_cb_window_visible_layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); - - free(flags); - free(layout); + format_add(ft, "window_zoomed_flag", "%d", + !!(w->flags & WINDOW_ZOOMED)); } -/* Add window pane tabs. */ +/* Set default format keys for a winlink. */ void -format_window_pane_tabs(struct format_tree *ft, struct window_pane *wp) +format_defaults_winlink(struct format_tree *ft, struct session *s, + struct winlink *wl) { - struct evbuffer *buffer; - u_int i; + struct window *w = wl->window; + char *flags; - buffer = evbuffer_new(); - for (i = 0; i < wp->base.grid->sx; i++) { - if (!bit_test(wp->base.tabs, i)) - continue; + if (ft->w == NULL) + ft->w = wl->window; - if (EVBUFFER_LENGTH(buffer) > 0) - evbuffer_add(buffer, ",", 1); - evbuffer_add_printf(buffer, "%d", i); - } + flags = window_printable_flags(s, wl); - format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer), - EVBUFFER_DATA(buffer)); - evbuffer_free(buffer); + format_defaults_window(ft, w); + + format_add(ft, "window_index", "%d", wl->idx); + format_add(ft, "window_flags", "%s", flags); + format_add(ft, "window_active", "%d", wl == s->curw); + + format_add(ft, "window_bell_flag", "%d", + !!(wl->flags & WINLINK_BELL)); + format_add(ft, "window_activity_flag", "%d", + !!(wl->flags & WINLINK_ACTIVITY)); + format_add(ft, "window_silence_flag", "%d", + !!(wl->flags & WINLINK_SILENCE)); + format_add(ft, "window_last_flag", "%d", + !!(wl == TAILQ_FIRST(&s->lastw))); + format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); + + free(flags); } /* Set default format keys for a window pane. */ void -format_window_pane(struct format_tree *ft, struct window_pane *wp) +format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { - struct grid *gd = wp->base.grid; - struct grid_line *gl; - unsigned long long size; - u_int i, idx; - const char *cwd; - char *cmd; + struct grid *gd = wp->base.grid; + u_int idx; + int status, scroll_position; + + if (ft->w == NULL) + ft->w = wp->window; + ft->wp = wp; - size = 0; - for (i = 0; i < gd->hsize; i++) { - gl = &gd->linedata[i]; - size += gl->cellsize * sizeof *gl->celldata; - } - size += gd->hsize * sizeof *gd->linedata; format_add(ft, "history_size", "%u", gd->hsize); format_add(ft, "history_limit", "%u", gd->hlimit); - format_add(ft, "history_bytes", "%llu", size); + format_add_cb(ft, "history_bytes", format_cb_history_bytes); if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); @@ -426,34 +1176,42 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_title", "%s", wp->base.title); format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_active", "%d", wp == wp->window->active); + format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); + + status = wp->status; + if (wp->fd == -1 && WIFEXITED(status)) + format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); format_add(ft, "pane_dead", "%d", wp->fd == -1); - format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); - - if (wp->tty != NULL) - format_add(ft, "pane_tty", "%s", wp->tty); - format_add(ft, "pane_pid", "%ld", (long) wp->pid); - if (wp->cmd != NULL) - format_add(ft, "pane_start_command", "%s", wp->cmd); - if (wp->cwd != NULL) - format_add(ft, "pane_start_path", "%s", wp->cwd); - if ((cwd = osdep_get_cwd(wp->fd)) != NULL) - format_add(ft, "pane_current_path", "%s", cwd); - if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) { - format_add(ft, "pane_current_command", "%s", cmd); - free(cmd); + if (window_pane_visible(wp)) { + format_add(ft, "pane_left", "%u", wp->xoff); + format_add(ft, "pane_top", "%u", wp->yoff); + format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); + format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); } - format_add(ft, "cursor_x", "%d", wp->base.cx); - format_add(ft, "cursor_y", "%d", wp->base.cy); - format_add(ft, "scroll_region_upper", "%d", wp->base.rupper); - format_add(ft, "scroll_region_lower", "%d", wp->base.rlower); - format_add(ft, "saved_cursor_x", "%d", wp->ictx.old_cx); - format_add(ft, "saved_cursor_y", "%d", wp->ictx.old_cy); + format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); + format_add(ft, "pane_synchronized", "%d", + !!options_get_number(wp->window->options, "synchronize-panes")); + + format_add(ft, "pane_tty", "%s", wp->tty); + format_add(ft, "pane_pid", "%ld", (long) wp->pid); + format_add_cb(ft, "pane_start_command", format_cb_start_command); + format_add_cb(ft, "pane_current_command", format_cb_current_command); + format_add_cb(ft, "pane_current_path", format_cb_current_path); + + format_add(ft, "cursor_x", "%u", wp->base.cx); + format_add(ft, "cursor_y", "%u", wp->base.cy); + format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); + format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); + + scroll_position = window_copy_scroll_position(wp); + if (scroll_position != -1) + format_add(ft, "scroll_position", "%d", scroll_position); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); - format_add(ft, "alternate_saved_x", "%d", wp->saved_cx); - format_add(ft, "alternate_saved_y", "%d", wp->saved_cy); + format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); + format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); format_add(ft, "cursor_flag", "%d", !!(wp->base.mode & MODE_CURSOR)); @@ -466,26 +1224,28 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "wrap_flag", "%d", !!(wp->base.mode & MODE_WRAP)); + format_add(ft, "mouse_any_flag", "%d", + !!(wp->base.mode & (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON))); format_add(ft, "mouse_standard_flag", "%d", !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", !!(wp->base.mode & MODE_MOUSE_BUTTON)); - format_add(ft, "mouse_any_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_ANY)); - format_add(ft, "mouse_utf8_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_UTF8)); - format_window_pane_tabs(ft, wp); + format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } /* Set default format keys for paste buffer. */ void -format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) +format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { - char *pb_print = paste_print(pb, 50); + size_t bufsize; + char *s; - format_add(ft, "buffer_size", "%zu", pb->size); - format_add(ft, "buffer_sample", "%s", pb_print); + paste_buffer_data(pb, &bufsize); + format_add(ft, "buffer_size", "%zu", bufsize); + format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); - free(pb_print); + s = paste_make_sample(pb); + format_add(ft, "buffer_sample", "%s", s); + free(s); } diff --git a/grid-cell.c b/grid-cell.c deleted file mode 100644 index 09643a9c..00000000 --- a/grid-cell.c +++ /dev/null @@ -1,55 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2012 Nicholas Marriott - * - * 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 - -#include - -#include "tmux.h" - -/* Get cell width. */ -u_int -grid_cell_width(const struct grid_cell *gc) -{ - return (gc->xstate >> 4); -} - -/* Get cell data. */ -void -grid_cell_get(const struct grid_cell *gc, struct utf8_data *ud) -{ - ud->size = gc->xstate & 0xf; - ud->width = gc->xstate >> 4; - memcpy(ud->data, gc->xdata, ud->size); -} - -/* Set cell data. */ -void -grid_cell_set(struct grid_cell *gc, const struct utf8_data *ud) -{ - memcpy(gc->xdata, ud->data, ud->size); - gc->xstate = (ud->width << 4) | ud->size; -} - -/* Set a single character as cell data. */ -void -grid_cell_one(struct grid_cell *gc, u_char ch) -{ - *gc->xdata = ch; - gc->xstate = (1 << 4) | 1; -} diff --git a/grid-view.c b/grid-view.c index 7ef443a3..f6708c89 100644 --- a/grid-view.c +++ b/grid-view.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -30,24 +30,17 @@ #define grid_view_x(gd, x) (x) #define grid_view_y(gd, y) ((gd)->hsize + (y)) -/* Get cell for reading. */ -const struct grid_cell * -grid_view_peek_cell(struct grid *gd, u_int px, u_int py) +/* Get cell. */ +void +grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { - return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); -} - -/* Get cell for writing. */ -struct grid_cell * -grid_view_get_cell(struct grid *gd, u_int px, u_int py) -{ - return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); + grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } /* Set cell. */ void -grid_view_set_cell( - struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +grid_view_set_cell(struct grid *gd, u_int px, u_int py, + const struct grid_cell *gc) { grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } @@ -59,8 +52,6 @@ grid_view_clear_history(struct grid *gd) struct grid_line *gl; u_int yy, last; - GRID_DEBUG(gd, ""); - /* Find the last used line. */ last = 0; for (yy = 0; yy < gd->sy; yy++) { @@ -82,8 +73,6 @@ grid_view_clear_history(struct grid *gd) void grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -94,8 +83,6 @@ grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) void grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) { - GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); - if (gd->flags & GRID_HISTORY) { grid_collect_history(gd); if (rupper == 0 && rlower == gd->sy - 1) @@ -116,8 +103,6 @@ grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) void grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower) { - GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); - rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); @@ -130,8 +115,6 @@ grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); @@ -141,12 +124,11 @@ grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) /* Insert lines in region. */ void -grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) +grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, + u_int ny) { u_int ny2; - GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); - rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); @@ -162,8 +144,6 @@ grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); @@ -174,12 +154,11 @@ grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) /* Delete lines inside scroll region. */ void -grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) +grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, + u_int ny) { u_int ny2; - GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); - rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); @@ -195,8 +174,6 @@ grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -214,8 +191,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); @@ -229,8 +204,6 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) char * grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) { - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); - px = grid_view_x(gd, px); py = grid_view_y(gd, py); diff --git a/grid.c b/grid.c index 2955e8ba..579eb966 100644 --- a/grid.c +++ b/grid.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -36,29 +36,34 @@ */ /* Default grid cell data. */ -const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; -const struct grid_cell grid_marker_cell = { 0, 0, 8, 8, (1 << 4) | 1, "_" }; - -#define grid_put_cell(gd, px, py, gc) do { \ - memcpy(&gd->linedata[py].celldata[px], \ - gc, sizeof gd->linedata[py].celldata[px]); \ -} while (0) -#define grid_put_utf8(gd, px, py, gc) do { \ - memcpy(&gd->linedata[py].utf8data[px], \ - gc, sizeof gd->linedata[py].utf8data[px]); \ -} while (0) +const struct grid_cell grid_default_cell = { + 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } +}; +const struct grid_cell_entry grid_default_entry = { + 0, { .data = { 0, 8, 8, ' ' } } +}; int grid_check_y(struct grid *, u_int); -#ifdef DEBUG -int -grid_check_y(struct grid *gd, u_int py) +void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *l, + u_int, u_int); +void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); +void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, + u_int); +void grid_reflow_move(struct grid *, u_int *, struct grid_line *); +size_t grid_string_cells_fg(const struct grid_cell *, int *); +size_t grid_string_cells_bg(const struct grid_cell *, int *); +void grid_string_cells_code(const struct grid_cell *, + const struct grid_cell *, char *, size_t, int); + +/* Copy default into a cell. */ +static void +grid_clear_cell(struct grid *gd, u_int px, u_int py) { - if ((py) >= (gd)->hsize + (gd)->sy) - log_fatalx("y out of range: %u", py); - return (0); + gd->linedata[py].celldata[px] = grid_default_entry; } -#else + +/* Check grid y position. */ int grid_check_y(struct grid *gd, u_int py) { @@ -68,16 +73,6 @@ grid_check_y(struct grid *gd, u_int py) } return (0); } -#endif - -void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); -void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, - u_int); -void grid_reflow_move(struct grid *, u_int *, struct grid_line *); -size_t grid_string_cells_fg(const struct grid_cell *, int *); -size_t grid_string_cells_bg(const struct grid_cell *, int *); -void grid_string_cells_code(const struct grid_cell *, - const struct grid_cell *, char *, size_t, int); /* Create a new grid. */ struct grid * @@ -109,6 +104,7 @@ grid_destroy(struct grid *gd) for (yy = 0; yy < gd->hsize + gd->sy; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); + free(gl->extddata); } free(gd->linedata); @@ -121,10 +117,10 @@ int grid_compare(struct grid *ga, struct grid *gb) { struct grid_line *gla, *glb; - struct grid_cell *gca, *gcb; + struct grid_cell gca, gcb; u_int xx, yy; - if (ga->sx != gb->sx || ga->sy != ga->sy) + if (ga->sx != gb->sx || ga->sy != gb->sy) return (1); for (yy = 0; yy < ga->sy; yy++) { @@ -132,10 +128,10 @@ grid_compare(struct grid *ga, struct grid *gb) glb = &gb->linedata[yy]; if (gla->cellsize != glb->cellsize) return (1); - for (xx = 0; xx < ga->sx; xx++) { - gca = &gla->celldata[xx]; - gcb = &glb->celldata[xx]; - if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0) + for (xx = 0; xx < gla->cellsize; xx++) { + grid_get_cell(ga, xx, yy, &gca); + grid_get_cell(gb, xx, yy, &gcb); + if (memcmp(&gca, &gcb, sizeof (struct grid_cell)) != 0) return (1); } } @@ -152,8 +148,6 @@ grid_collect_history(struct grid *gd) { u_int yy; - GRID_DEBUG(gd, ""); - if (gd->hsize < gd->hlimit) return; @@ -174,15 +168,26 @@ grid_scroll_history(struct grid *gd) { u_int yy; - GRID_DEBUG(gd, ""); - yy = gd->hsize + gd->sy; - gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); + gd->linedata = xreallocarray(gd->linedata, yy + 1, + sizeof *gd->linedata); memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); gd->hsize++; } +/* Clear the history. */ +void +grid_clear_history(struct grid *gd) +{ + grid_clear_lines(gd, 0, gd->hsize); + grid_move_lines(gd, 0, gd->hsize, gd->sy); + + gd->hsize = 0; + gd->linedata = xreallocarray(gd->linedata, gd->sy, + sizeof *gd->linedata); +} + /* Scroll a region up, moving the top line into the history. */ void grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) @@ -190,11 +195,10 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) struct grid_line *gl_history, *gl_upper, *gl_lower; u_int yy; - GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower); - /* Create a space for a new line. */ yy = gd->hsize + gd->sy; - gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); + gd->linedata = xreallocarray(gd->linedata, yy + 1, + sizeof *gd->linedata); /* Move the entire screen down to free a space for this line. */ gl_history = &gd->linedata[gd->hsize]; @@ -228,9 +232,9 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx) if (sx <= gl->cellsize) return; - gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata); + gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); for (xx = gl->cellsize; xx < sx; xx++) - grid_put_cell(gd, xx, py, &grid_default_cell); + grid_clear_cell(gd, xx, py); gl->cellsize = sx; } @@ -244,38 +248,72 @@ grid_peek_line(struct grid *gd, u_int py) } /* Get cell for reading. */ -const struct grid_cell * -grid_peek_cell(struct grid *gd, u_int px, u_int py) +void +grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { - if (grid_check_y(gd, py) != 0) - return (&grid_default_cell); + struct grid_line *gl; + struct grid_cell_entry *gce; - if (px >= gd->linedata[py].cellsize) - return (&grid_default_cell); - return (&gd->linedata[py].celldata[px]); -} + if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) { + memcpy(gc, &grid_default_cell, sizeof *gc); + return; + } -/* Get cell at relative position (for writing). */ -struct grid_cell * -grid_get_cell(struct grid *gd, u_int px, u_int py) -{ - if (grid_check_y(gd, py) != 0) - return (NULL); + gl = &gd->linedata[py]; + gce = &gl->celldata[px]; - grid_expand_line(gd, py, px + 1); - return (&gd->linedata[py].celldata[px]); + if (gce->flags & GRID_FLAG_EXTENDED) { + if (gce->offset >= gl->extdsize) + memcpy(gc, &grid_default_cell, sizeof *gc); + else + memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); + return; + } + + gc->flags = gce->flags & ~GRID_FLAG_EXTENDED; + gc->attr = gce->data.attr; + gc->fg = gce->data.fg; + gc->bg = gce->data.bg; + utf8_set(&gc->data, gce->data.data); } /* Set cell at relative position. */ void -grid_set_cell( - struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { + struct grid_line *gl; + struct grid_cell_entry *gce; + struct grid_cell *gcp; + if (grid_check_y(gd, py) != 0) return; grid_expand_line(gd, py, px + 1); - grid_put_cell(gd, px, py, gc); + + gl = &gd->linedata[py]; + gce = &gl->celldata[px]; + + if ((gce->flags & GRID_FLAG_EXTENDED) || gc->data.size != 1 || + gc->data.width != 1) { + if (~gce->flags & GRID_FLAG_EXTENDED) { + gl->extddata = xreallocarray(gl->extddata, + gl->extdsize + 1, sizeof *gl->extddata); + gce->offset = gl->extdsize++; + gce->flags = gc->flags | GRID_FLAG_EXTENDED; + } + + if (gce->offset >= gl->extdsize) + fatalx("offset too big"); + gcp = &gl->extddata[gce->offset]; + memcpy(gcp, gc, sizeof *gcp); + return; + } + + gce->flags = gc->flags & ~GRID_FLAG_EXTENDED; + gce->data.attr = gc->attr; + gce->data.fg = gc->fg; + gce->data.bg = gc->bg; + gce->data.data = gc->data.data[0]; } /* Clear area. */ @@ -284,8 +322,6 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { u_int xx, yy; - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); - if (nx == 0 || ny == 0) return; @@ -309,7 +345,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) for (xx = px; xx < px + nx; xx++) { if (xx >= gd->linedata[yy].cellsize) break; - grid_put_cell(gd, xx, yy, &grid_default_cell); + grid_clear_cell(gd, xx, yy); } } } @@ -321,8 +357,6 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) struct grid_line *gl; u_int yy; - GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); - if (ny == 0) return; @@ -334,6 +368,7 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); + free(gl->extddata); memset(gl, 0, sizeof *gl); } } @@ -344,8 +379,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) { u_int yy; - GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); - if (ny == 0 || py == dy) return; @@ -365,8 +398,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) grid_clear_lines(gd, yy, 1); } - memmove( - &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); + memmove(&gd->linedata[dy], &gd->linedata[py], + ny * (sizeof *gd->linedata)); /* Wipe any lines that have been moved (without freeing them). */ for (yy = py; yy < py + ny; yy++) { @@ -383,8 +416,6 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) struct grid_line *gl; u_int xx; - GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); - if (nx == 0 || px == dx) return; @@ -394,14 +425,14 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) grid_expand_line(gd, py, px + nx); grid_expand_line(gd, py, dx + nx); - memmove( - &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); + memmove(&gl->celldata[dx], &gl->celldata[px], + nx * sizeof *gl->celldata); /* Wipe any cells that have been moved. */ for (xx = px; xx < px + nx; xx++) { if (xx >= dx && xx < dx + nx) continue; - grid_put_cell(gd, xx, py, &grid_default_cell); + grid_clear_cell(gd, xx, py); } } @@ -418,29 +449,29 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values) values[n++] = gc->fg; } else { switch (gc->fg) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - values[n++] = gc->fg + 30; - break; - case 8: - values[n++] = 39; - break; - case 90: - case 91: - case 92: - case 93: - case 94: - case 95: - case 96: - case 97: - values[n++] = gc->fg; - break; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + values[n++] = gc->fg + 30; + break; + case 8: + values[n++] = 39; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + values[n++] = gc->fg; + break; } } return (n); @@ -530,20 +561,18 @@ grid_string_cells_code(const struct grid_cell *lastgc, s[n++] = attrs[i].code; } - /* If the foreground c changed, append its parameters. */ + /* If the foreground colour changed, append its parameters. */ nnewc = grid_string_cells_fg(gc, newc); noldc = grid_string_cells_fg(lastgc, oldc); - if (nnewc != noldc || - memcmp(newc,oldc, nnewc * sizeof newc[0]) != 0) { + if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { for (i = 0; i < nnewc; i++) s[n++] = newc[i]; } - /* If the background c changed, append its parameters. */ + /* If the background colour changed, append its parameters. */ nnewc = grid_string_cells_bg(gc, newc); noldc = grid_string_cells_bg(lastgc, oldc); - if (nnewc != noldc || - memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { + if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { for (i = 0; i < nnewc; i++) s[n++] = newc[i]; } @@ -585,15 +614,13 @@ char * grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) { - const struct grid_cell *gc; + struct grid_cell gc; static struct grid_cell lastgc1; - struct utf8_data ud; - const char* data; + const char *data; char *buf, code[128]; size_t len, off, size, codelen; u_int xx; - - GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + const struct grid_line *gl; if (lastgc != NULL && *lastgc == NULL) { memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); @@ -604,29 +631,31 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, buf = xmalloc(len); off = 0; + gl = grid_peek_line(gd, py); for (xx = px; xx < px + nx; xx++) { - gc = grid_peek_cell(gd, xx, py); - if (gc->flags & GRID_FLAG_PADDING) + if (gl == NULL || xx >= gl->cellsize) + break; + grid_get_cell(gd, xx, py, &gc); + if (gc.flags & GRID_FLAG_PADDING) continue; - grid_cell_get(gc, &ud); if (with_codes) { - grid_string_cells_code(*lastgc, gc, code, sizeof code, + grid_string_cells_code(*lastgc, &gc, code, sizeof code, escape_c0); codelen = strlen(code); - memcpy(*lastgc, gc, sizeof *gc); + memcpy(*lastgc, &gc, sizeof **lastgc); } else codelen = 0; - data = ud.data; - size = ud.size; + data = gc.data.data; + size = gc.data.size; if (escape_c0 && size == 1 && *data == '\\') { data = "\\\\"; size = 2; } while (len < off + size + codelen + 1) { - buf = xrealloc(buf, 2, len); + buf = xreallocarray(buf, 2, len); len *= 2; } @@ -638,10 +667,10 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, off += size; } - if (trim) { + if (trim) { while (off > 0 && buf[off - 1] == ' ') off--; - } + } buf[off] = '\0'; return (buf); @@ -653,14 +682,12 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, * available. */ void -grid_duplicate_lines( - struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) +grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, + u_int ny) { struct grid_line *dstl, *srcl; u_int yy; - GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); - if (dy + ny > dst->hsize + dst->sy) ny = dst->hsize + dst->sy - dy; if (sy + ny > src->hsize + src->sy) @@ -673,10 +700,19 @@ grid_duplicate_lines( memcpy(dstl, srcl, sizeof *dstl); if (srcl->cellsize != 0) { - dstl->celldata = xcalloc( + dstl->celldata = xreallocarray(NULL, srcl->cellsize, sizeof *dstl->celldata); memcpy(dstl->celldata, srcl->celldata, srcl->cellsize * sizeof *dstl->celldata); + } else + dstl->celldata = NULL; + + if (srcl->extdsize != 0) { + dstl->extdsize = srcl->extdsize; + dstl->extddata = xreallocarray(NULL, dstl->extdsize, + sizeof *dstl->extddata); + memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * + sizeof *dstl->extddata); } sy++; @@ -684,6 +720,31 @@ grid_duplicate_lines( } } +/* Copy a section of a line. */ +void +grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl, + u_int from, u_int to_copy) +{ + struct grid_cell_entry *gce; + u_int i, was; + + memcpy(&dst_gl->celldata[to], &src_gl->celldata[from], + to_copy * sizeof *dst_gl->celldata); + + for (i = to; i < to + to_copy; i++) { + gce = &dst_gl->celldata[i]; + if (~gce->flags & GRID_FLAG_EXTENDED) + continue; + was = gce->offset; + + dst_gl->extddata = xreallocarray(dst_gl->extddata, + dst_gl->extdsize + 1, sizeof *dst_gl->extddata); + gce->offset = dst_gl->extdsize++; + memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was], + sizeof *dst_gl->extddata); + } +} + /* Join line data. */ void grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, @@ -703,13 +764,12 @@ grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, nx = ox + to_copy; /* Resize the destination line. */ - dst_gl->celldata = xrealloc(dst_gl->celldata, nx, + dst_gl->celldata = xreallocarray(dst_gl->celldata, nx, sizeof *dst_gl->celldata); dst_gl->cellsize = nx; /* Append as much as possible. */ - memcpy(&dst_gl->celldata[ox], &src_gl->celldata[0], - to_copy * sizeof src_gl->celldata[0]); + grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy); /* If there is any left in the source, split it. */ if (src_gl->cellsize > to_copy) { @@ -742,13 +802,13 @@ grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, to_copy = src_gl->cellsize; /* Expand destination line. */ - dst_gl->celldata = xmalloc(to_copy * sizeof *dst_gl->celldata); + dst_gl->celldata = xreallocarray(NULL, to_copy, + sizeof *dst_gl->celldata); dst_gl->cellsize = to_copy; dst_gl->flags |= GRID_LINE_WRAPPED; /* Copy the data. */ - memcpy (&dst_gl->celldata[0], &src_gl->celldata[offset], - to_copy * sizeof dst_gl->celldata[0]); + grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy); /* Move offset and reduce old line size. */ offset += to_copy; @@ -778,6 +838,7 @@ grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) /* Clear old line. */ src_gl->celldata = NULL; + src_gl->extddata = NULL; } /* @@ -807,7 +868,7 @@ grid_reflow(struct grid *dst, struct grid *src, u_int new_x) /* Previous was wrapped. Try to join. */ grid_reflow_join(dst, &py, src_gl, new_x); } - previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED; + previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED); } grid_destroy(src); diff --git a/hooks.c b/hooks.c new file mode 100644 index 00000000..c1a016ae --- /dev/null +++ b/hooks.c @@ -0,0 +1,226 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Thomas Adam + * + * 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 + +#include +#include + +#include "tmux.h" + +struct hooks { + RB_HEAD(hooks_tree, hook) tree; + struct hooks *parent; +}; + +static int hooks_cmp(struct hook *, struct hook *); +RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); +RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); + +static struct hook *hooks_find1(struct hooks *, const char *); +static void hooks_free1(struct hooks *, struct hook *); +static void hooks_emptyfn(struct cmd_q *); + +static int +hooks_cmp(struct hook *hook1, struct hook *hook2) +{ + return (strcmp(hook1->name, hook2->name)); +} + +struct hooks * +hooks_get(struct session *s) +{ + if (s != NULL) + return (s->hooks); + return (global_hooks); +} + +struct hooks * +hooks_create(struct hooks *parent) +{ + struct hooks *hooks; + + hooks = xcalloc(1, sizeof *hooks); + RB_INIT(&hooks->tree); + hooks->parent = parent; + return (hooks); +} + +static void +hooks_free1(struct hooks *hooks, struct hook *hook) +{ + RB_REMOVE(hooks_tree, &hooks->tree, hook); + cmd_list_free(hook->cmdlist); + free((char *)hook->name); + free(hook); +} + +void +hooks_free(struct hooks *hooks) +{ + struct hook *hook, *hook1; + + RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) + hooks_free1(hooks, hook); + free(hooks); +} + +struct hook * +hooks_first(struct hooks *hooks) +{ + return (RB_MIN(hooks_tree, &hooks->tree)); +} + +struct hook * +hooks_next(struct hook *hook) +{ + return (RB_NEXT(hooks_tree, &hooks->tree, hook)); +} + +void +hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) +{ + struct hook *hook; + + if ((hook = hooks_find1(hooks, name)) != NULL) + hooks_free1(hooks, hook); + + hook = xcalloc(1, sizeof *hook); + hook->name = xstrdup(name); + hook->cmdlist = cmdlist; + hook->cmdlist->references++; + RB_INSERT(hooks_tree, &hooks->tree, hook); +} + +void +hooks_remove(struct hooks *hooks, const char *name) +{ + struct hook *hook; + + if ((hook = hooks_find1(hooks, name)) != NULL) + hooks_free1(hooks, hook); +} + +static struct hook * +hooks_find1(struct hooks *hooks, const char *name) +{ + struct hook hook; + + hook.name = name; + return (RB_FIND(hooks_tree, &hooks->tree, &hook)); +} + +struct hook * +hooks_find(struct hooks *hooks, const char *name) +{ + struct hook hook0, *hook; + + hook0.name = name; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + while (hook == NULL) { + hooks = hooks->parent; + if (hooks == NULL) + break; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + } + return (hook); +} + +static void +hooks_emptyfn(struct cmd_q *hooks_cmdq) +{ + struct cmd_q *cmdq = hooks_cmdq->data; + + if (cmdq != NULL) { + if (hooks_cmdq->client_exit >= 0) + cmdq->client_exit = hooks_cmdq->client_exit; + if (!cmdq_free(cmdq)) + cmdq_continue(cmdq); + } + cmdq_free(hooks_cmdq); +} + +int +hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs, + const char *fmt, ...) +{ + struct hook *hook; + struct cmd_q *hooks_cmdq; + va_list ap; + char *name; + + va_start(ap, fmt); + xvasprintf(&name, fmt, ap); + va_end(ap); + + hook = hooks_find(hooks, name); + if (hook == NULL) { + free(name); + return (-1); + } + log_debug("running hook %s", name); + free(name); + + hooks_cmdq = cmdq_new(c); + hooks_cmdq->flags |= CMD_Q_NOHOOKS; + + if (fs != NULL) + cmd_find_copy_state(&hooks_cmdq->current, fs); + hooks_cmdq->parent = NULL; + + cmdq_run(hooks_cmdq, hook->cmdlist, NULL); + cmdq_free(hooks_cmdq); + return (0); +} + +int +hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, + const char *fmt, ...) +{ + struct hook *hook; + struct cmd_q *hooks_cmdq; + va_list ap; + char *name; + + va_start(ap, fmt); + xvasprintf(&name, fmt, ap); + va_end(ap); + + hook = hooks_find(hooks, name); + if (hook == NULL) { + free(name); + return (-1); + } + log_debug("running hook %s (parent %p)", name, cmdq); + free(name); + + hooks_cmdq = cmdq_new(cmdq->client); + hooks_cmdq->flags |= CMD_Q_NOHOOKS; + + if (fs != NULL) + cmd_find_copy_state(&hooks_cmdq->current, fs); + hooks_cmdq->parent = cmdq; + + hooks_cmdq->emptyfn = hooks_emptyfn; + hooks_cmdq->data = cmdq; + + if (cmdq != NULL) + cmdq->references++; + cmdq_run(hooks_cmdq, hook->cmdlist, NULL); + return (0); +} diff --git a/input-keys.c b/input-keys.c index faa7bd17..1572b7e7 100644 --- a/input-keys.c +++ b/input-keys.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -30,8 +30,10 @@ * direction with output). */ +void input_key_mouse(struct window_pane *, struct mouse_event *); + struct input_key_ent { - int key; + key_code key; const char *data; int flags; @@ -56,14 +58,14 @@ const struct input_key_ent input_keys[] = { { KEYC_F10, "\033[21~", 0 }, { KEYC_F11, "\033[23~", 0 }, { KEYC_F12, "\033[24~", 0 }, - { KEYC_F13, "\033[25~", 0 }, - { KEYC_F14, "\033[26~", 0 }, - { KEYC_F15, "\033[28~", 0 }, - { KEYC_F16, "\033[29~", 0 }, - { KEYC_F17, "\033[31~", 0 }, - { KEYC_F18, "\033[32~", 0 }, - { KEYC_F19, "\033[33~", 0 }, - { KEYC_F20, "\033[34~", 0 }, + { KEYC_F1|KEYC_SHIFT, "\033[25~", 0 }, + { KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, + { KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, + { KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, + { KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, + { KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, + { KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, + { KEYC_F8|KEYC_SHIFT, "\033[34~", 0 }, { KEYC_IC, "\033[2~", 0 }, { KEYC_DC, "\033[3~", 0 }, { KEYC_HOME, "\033[1~", 0 }, @@ -134,25 +136,43 @@ const struct input_key_ent input_keys[] = { /* Translate a key code into an output key sequence. */ void -input_key(struct window_pane *wp, int key) +input_key(struct window_pane *wp, key_code key, struct mouse_event *m) { - const struct input_key_ent *ike; - u_int i; - size_t dlen; - char *out; - u_char ch; + const struct input_key_ent *ike; + u_int i; + size_t dlen; + char *out; + key_code justkey; + struct utf8_data ud; - log_debug2("writing key 0x%x", key); + log_debug("writing key 0x%llx (%s) to %%%u", key, + key_string_lookup_key(key), wp->id); + + /* If this is a mouse key, pass off to mouse function. */ + if (KEYC_IS_MOUSE(key)) { + if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) + input_key_mouse(wp, m); + return; + } /* * If this is a normal 7-bit key, just send it, with a leading escape - * if necessary. + * if necessary. If it is a UTF-8 key, split it and send it. */ - if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) { + justkey = (key & ~KEYC_ESCAPE); + if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); - ch = key & ~KEYC_ESCAPE; - bufferevent_write(wp->event, &ch, 1); + ud.data[0] = justkey; + bufferevent_write(wp->event, &ud.data[0], 1); + return; + } + if (justkey > 0x7f && justkey < KEYC_BASE) { + if (utf8_split(justkey, &ud) != UTF8_DONE) + return; + if (key & KEYC_ESCAPE) + bufferevent_write(wp->event, "\033", 1); + bufferevent_write(wp->event, ud.data, ud.size); return; } @@ -160,7 +180,7 @@ input_key(struct window_pane *wp, int key) * Then try to look this up as an xterm key, if the flag to output them * is set. */ - if (options_get_number(&wp->window->options, "xterm-keys")) { + if (options_get_number(wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { bufferevent_write(wp->event, out, strlen(out)); free(out); @@ -185,11 +205,11 @@ input_key(struct window_pane *wp, int key) break; } if (i == nitems(input_keys)) { - log_debug2("key 0x%x missing", key); + log_debug("key 0x%llx missing", key); return; } dlen = strlen(ike->data); - log_debug2("found key 0x%x: \"%s\"", key, ike->data); + log_debug("found key 0x%llx: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) @@ -199,57 +219,48 @@ input_key(struct window_pane *wp, int key) /* Translate mouse and output. */ void -input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) +input_key_mouse(struct window_pane *wp, struct mouse_event *m) { - char buf[40]; - size_t len; - struct paste_buffer *pb; + char buf[40]; + size_t len; + u_int x, y; - if (wp->screen->mode & ALL_MOUSE_MODES) { - /* - * Use the SGR (1006) extension only if the application - * requested it and the underlying terminal also sent the event - * in this format (this is because an old style mouse release - * event cannot be converted into the new SGR format, since the - * released button is unknown). Otherwise pretend that tmux - * doesn't speak this extension, and fall back to the UTF-8 - * (1005) extension if the application requested, or to the - * legacy format. - */ - if (m->sgr && (wp->screen->mode & MODE_MOUSE_SGR)) { - len = xsnprintf(buf, sizeof buf, "\033[<%d;%d;%d%c", - m->sgr_xb, m->x + 1, m->y + 1, - m->sgr_rel ? 'm' : 'M'); - } else if (wp->screen->mode & MODE_MOUSE_UTF8) { - len = xsnprintf(buf, sizeof buf, "\033[M"); - len += utf8_split2(m->xb + 32, &buf[len]); - len += utf8_split2(m->x + 33, &buf[len]); - len += utf8_split2(m->y + 33, &buf[len]); - } else { - if (m->xb > 223 || m->x >= 222 || m->y > 222) - return; - len = xsnprintf(buf, sizeof buf, "\033[M"); - buf[len++] = m->xb + 32; - buf[len++] = m->x + 33; - buf[len++] = m->y + 33; - } - bufferevent_write(wp->event, buf, len); + if ((wp->screen->mode & ALL_MOUSE_MODES) == 0) + return; + if (!window_pane_visible(wp)) + return; + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; - } - if (m->button == 1 && (m->event & MOUSE_EVENT_CLICK) && - options_get_number(&wp->window->options, "mode-mouse") == 1) { - pb = paste_get_top(&global_buffers); - if (pb != NULL) { - paste_send_pane(pb, wp, "\r", - wp->screen->mode & MODE_BRACKETPASTE); - } - } else if ((m->xb & 3) != 1 && - options_get_number(&wp->window->options, "mode-mouse") == 1) { - if (window_pane_set_mode(wp, &window_copy_mode) == 0) { - window_copy_init_from_pane(wp); - if (wp->mode->mouse != NULL) - wp->mode->mouse(wp, s, m); - } + /* If this pane is not in button mode, discard motion events. */ + if (!(wp->screen->mode & MODE_MOUSE_BUTTON) && (m->b & MOUSE_MASK_DRAG)) + return; + + /* + * Use the SGR (1006) extension only if the application requested it + * and the underlying terminal also sent the event in this format (this + * is because an old style mouse release event cannot be converted into + * the new SGR format, since the released button is unknown). Otherwise + * pretend that tmux doesn't speak this extension, and fall back to the + * UTF-8 (1005) extension if the application requested, or to the + * legacy format. + */ + if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) { + len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", + m->sgr_b, x + 1, y + 1, m->sgr_type); + } else if (wp->screen->mode & MODE_MOUSE_UTF8) { + len = xsnprintf(buf, sizeof buf, "\033[M"); + len += utf8_split2(m->b + 32, &buf[len]); + len += utf8_split2(x + 33, &buf[len]); + len += utf8_split2(y + 33, &buf[len]); + } else { + if (m->b > 223) + return; + len = xsnprintf(buf, sizeof buf, "\033[M"); + buf[len++] = m->b + 32; + buf[len++] = x + 33; + buf[len++] = y + 33; } + log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); + bufferevent_write(wp->event, buf, len); } diff --git a/input.c b/input.c index 4aa02e90..ed1ebffc 100644 --- a/input.c +++ b/input.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" @@ -43,18 +44,69 @@ * pretty stupid but not supporting it is more trouble than it is worth. * * - Special handling for ESC inside a DCS to allow arbitrary byte sequences to - * be passed to the underlying teminal(s). + * be passed to the underlying terminals. */ +/* Input parser cell. */ +struct input_cell { + struct grid_cell cell; + int set; + int g0set; /* 1 if ACS */ + int g1set; /* 1 if ACS */ +}; + +/* Input parser context. */ +struct input_ctx { + struct window_pane *wp; + struct screen_write_ctx ctx; + + struct input_cell cell; + + struct input_cell old_cell; + u_int old_cx; + u_int old_cy; + + u_char interm_buf[4]; + size_t interm_len; + + u_char param_buf[64]; + size_t param_len; + +#define INPUT_BUF_START 32 +#define INPUT_BUF_LIMIT 1048576 + u_char *input_buf; + size_t input_len; + size_t input_space; + + int param_list[24]; /* -1 not present */ + u_int param_list_len; + + struct utf8_data utf8data; + + int ch; + int flags; +#define INPUT_DISCARD 0x1 + + const struct input_state *state; + + /* + * All input received since we were last in the ground state. Sent to + * control clients on connection. + */ + struct evbuffer *since_ground; +}; + /* Helper functions. */ struct input_transition; int input_split(struct input_ctx *); int input_get(struct input_ctx *, u_int, int, int); void input_reply(struct input_ctx *, const char *, ...); void input_set_state(struct window_pane *, const struct input_transition *); +void input_reset_cell(struct input_ctx *); /* Transition entry/exit handlers. */ void input_clear(struct input_ctx *); +void input_ground(struct input_ctx *); void input_enter_osc(struct input_ctx *); void input_exit_osc(struct input_ctx *); void input_enter_apc(struct input_ctx *); @@ -70,6 +122,13 @@ int input_input(struct input_ctx *); int input_c0_dispatch(struct input_ctx *); int input_esc_dispatch(struct input_ctx *); int input_csi_dispatch(struct input_ctx *); +void input_csi_dispatch_rm(struct input_ctx *); +void input_csi_dispatch_rm_private(struct input_ctx *); +void input_csi_dispatch_sm(struct input_ctx *); +void input_csi_dispatch_sm_private(struct input_ctx *); +void input_csi_dispatch_winops(struct input_ctx *); +void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *); +void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); void input_csi_dispatch_sgr(struct input_ctx *); int input_dcs_dispatch(struct input_ctx *); int input_utf8_open(struct input_ctx *); @@ -98,19 +157,23 @@ enum input_esc_type { INPUT_ESC_NEL, INPUT_ESC_RI, INPUT_ESC_RIS, - INPUT_ESC_SCSOFF_G0, - INPUT_ESC_SCSON_G0, + INPUT_ESC_SCSG0_OFF, + INPUT_ESC_SCSG0_ON, + INPUT_ESC_SCSG1_OFF, + INPUT_ESC_SCSG1_ON, }; /* Escape command table. */ const struct input_table_entry input_esc_table[] = { - { '0', "(", INPUT_ESC_SCSOFF_G0 }, + { '0', "(", INPUT_ESC_SCSG0_ON }, + { '0', ")", INPUT_ESC_SCSG1_ON }, { '7', "", INPUT_ESC_DECSC }, { '8', "", INPUT_ESC_DECRC }, { '8', "#", INPUT_ESC_DECALN }, { '=', "", INPUT_ESC_DECKPAM }, { '>', "", INPUT_ESC_DECKPNM }, - { 'B', "(", INPUT_ESC_SCSON_G0 }, + { 'B', "(", INPUT_ESC_SCSG0_OFF }, + { 'B', ")", INPUT_ESC_SCSG1_OFF }, { 'D', "", INPUT_ESC_IND }, { 'E', "", INPUT_ESC_NEL }, { 'H', "", INPUT_ESC_HTS }, @@ -150,6 +213,7 @@ enum input_csi_type { INPUT_CSI_SM_PRIVATE, INPUT_CSI_TBC, INPUT_CSI_VPA, + INPUT_CSI_WINOPS, }; /* Control (CSI) command table. */ @@ -184,6 +248,7 @@ const struct input_table_entry input_csi_table[] = { { 'q', " ", INPUT_CSI_DECSCUSR }, { 'r', "", INPUT_CSI_DECSTBM }, { 's', "", INPUT_CSI_SCP }, + { 't', "", INPUT_CSI_WINOPS }, { 'u', "", INPUT_CSI_RCP }, }; @@ -235,7 +300,7 @@ const struct input_transition input_state_utf8_one_table[]; /* ground state definition. */ const struct input_state input_state_ground = { "ground", - NULL, NULL, + input_ground, NULL, input_state_ground_table }; @@ -381,11 +446,11 @@ const struct input_transition input_state_ground_table[] = { { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x7e, input_print, NULL }, { 0x7f, 0x7f, NULL, NULL }, - { 0x80, 0xc1, input_print, NULL }, + { 0x80, 0xc1, NULL, NULL }, { 0xc2, 0xdf, input_utf8_open, &input_state_utf8_one }, { 0xe0, 0xef, input_utf8_open, &input_state_utf8_two }, { 0xf0, 0xf4, input_utf8_open, &input_state_utf8_three }, - { 0xf5, 0xff, input_print, NULL }, + { 0xf5, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; @@ -676,17 +741,64 @@ input_table_compare(const void *key, const void *value) return (strcmp(ictx->interm_buf, entry->interm)); } +/* Reset cell state to default. */ +void +input_reset_cell(struct input_ctx *ictx) +{ + memcpy(&ictx->cell.cell, &grid_default_cell, sizeof ictx->cell.cell); + ictx->cell.set = 0; + ictx->cell.g0set = ictx->cell.g1set = 0; + + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; +} + /* Initialise input parser. */ void input_init(struct window_pane *wp) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx; - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + ictx = wp->ictx = xcalloc(1, sizeof *ictx); - memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; + ictx->input_space = INPUT_BUF_START; + ictx->input_buf = xmalloc(INPUT_BUF_START); + + ictx->since_ground = evbuffer_new(); + + input_reset(wp, 0); +} + +/* Destroy input parser. */ +void +input_free(struct window_pane *wp) +{ + struct input_ctx *ictx = wp->ictx; + + free(ictx->input_buf); + evbuffer_free(ictx->since_ground); + + free (ictx); + wp->ictx = NULL; +} + +/* Reset input state and clear screen. */ +void +input_reset(struct window_pane *wp, int clear) +{ + struct input_ctx *ictx = wp->ictx; + + input_reset_cell(ictx); + + if (clear) { + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + screen_write_reset(&ictx->ctx); + screen_write_stop(&ictx->ctx); + } *ictx->interm_buf = '\0'; ictx->interm_len = 0; @@ -694,33 +806,28 @@ input_init(struct window_pane *wp) *ictx->param_buf = '\0'; ictx->param_len = 0; + *ictx->input_buf = '\0'; + ictx->input_len = 0; + ictx->state = &input_state_ground; ictx->flags = 0; - - ictx->since_ground = evbuffer_new(); } -/* Destroy input parser. */ -void -input_free(struct window_pane *wp) +/* Return pending data. */ +struct evbuffer * +input_pending(struct window_pane *wp) { - if (wp != NULL) - evbuffer_free(wp->ictx.since_ground); + return (wp->ictx->since_ground); } /* Change input state. */ void input_set_state(struct window_pane *wp, const struct input_transition *itr) { - struct input_ctx *ictx = &wp->ictx; - struct evbuffer *ground_evb = ictx->since_ground; + struct input_ctx *ictx = wp->ictx; if (ictx->state->exit != NULL) ictx->state->exit(ictx); - - if (itr->state == &input_state_ground) - evbuffer_drain(ground_evb, EVBUFFER_LENGTH(ground_evb)); - ictx->state = itr->state; if (ictx->state->enter != NULL) ictx->state->enter(ictx); @@ -730,7 +837,7 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr) void input_parse(struct window_pane *wp) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx = wp->ictx; const struct input_transition *itr; struct evbuffer *evb = wp->event->input; u_char *buf; @@ -739,8 +846,8 @@ input_parse(struct window_pane *wp) if (EVBUFFER_LENGTH(evb) == 0) return; - wp->window->flags |= WINDOW_ACTIVITY; - wp->window->flags &= ~WINDOW_SILENCE; + window_update_activity(wp->window); + wp->flags |= PANE_CHANGED; /* * Open the screen. Use NULL wp if there is a mode set as don't want to @@ -757,10 +864,12 @@ input_parse(struct window_pane *wp) notify_input(wp, evb); off = 0; + log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, + ictx->state->name, len, (int)len, buf); + /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; - log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); /* Find the transition. */ itr = ictx->state->transitions; @@ -771,7 +880,7 @@ input_parse(struct window_pane *wp) } if (itr->first == -1 || itr->last == -1) { /* No transition? Eh? */ - fatalx("No transition from state!"); + fatalx("no transition from state"); } /* @@ -875,12 +984,34 @@ input_clear(struct input_ctx *ictx) ictx->flags &= ~INPUT_DISCARD; } +/* Reset for ground state. */ +void +input_ground(struct input_ctx *ictx) +{ + evbuffer_drain(ictx->since_ground, EVBUFFER_LENGTH(ictx->since_ground)); + + if (ictx->input_space > INPUT_BUF_START) { + ictx->input_space = INPUT_BUF_START; + ictx->input_buf = xrealloc(ictx->input_buf, INPUT_BUF_START); + } +} + /* Output this character to the screen. */ int input_print(struct input_ctx *ictx) { - grid_cell_one(&ictx->cell, ictx->ch); - screen_write_cell(&ictx->ctx, &ictx->cell); + int set; + + set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set; + if (set == 1) + ictx->cell.cell.attr |= GRID_ATTR_CHARSET; + else + ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; + + utf8_set(&ictx->cell.cell.data, ictx->ch); + screen_write_cell(&ictx->ctx, &ictx->cell.cell); + + ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; return (0); } @@ -917,12 +1048,20 @@ input_parameter(struct input_ctx *ictx) int input_input(struct input_ctx *ictx) { - if (ictx->input_len == (sizeof ictx->input_buf) - 1) - ictx->flags |= INPUT_DISCARD; - else { - ictx->input_buf[ictx->input_len++] = ictx->ch; - ictx->input_buf[ictx->input_len] = '\0'; + size_t available; + + available = ictx->input_space; + while (ictx->input_len + 1 >= available) { + available *= 2; + if (available > INPUT_BUF_LIMIT) { + ictx->flags |= INPUT_DISCARD; + return (0); + } + ictx->input_buf = xrealloc(ictx->input_buf, available); + ictx->input_space = available; } + ictx->input_buf[ictx->input_len++] = ictx->ch; + ictx->input_buf[ictx->input_len] = '\0'; return (0); } @@ -934,19 +1073,18 @@ input_c0_dispatch(struct input_ctx *ictx) struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; - u_int trigger; - log_debug("%s: '%c", __func__, ictx->ch); + log_debug("%s: '%c'", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ break; case '\007': /* BEL */ - wp->window->flags |= WINDOW_BELL; + alerts_queue(wp->window, WINDOW_BELL); break; case '\010': /* BS */ screen_write_backspace(sctx); - goto count_c0; + break; case '\011': /* HT */ /* Don't tab beyond the end of the line. */ if (s->cx >= screen_size_x(s) - 1) @@ -963,30 +1101,21 @@ input_c0_dispatch(struct input_ctx *ictx) case '\013': /* VT */ case '\014': /* FF */ screen_write_linefeed(sctx, 0); - goto count_c0; + break; case '\015': /* CR */ screen_write_carriagereturn(sctx); - goto count_c0; + break; case '\016': /* SO */ - ictx->cell.attr |= GRID_ATTR_CHARSET; + ictx->cell.set = 1; break; case '\017': /* SI */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; + ictx->cell.set = 0; break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - return (0); - -count_c0: - trigger = options_get_number(&wp->window->options, "c0-change-trigger"); - if (++wp->changes == trigger) { - wp->flags |= PANE_DROP; - window_pane_timer_start(wp); - } - return (0); } @@ -1011,11 +1140,7 @@ input_esc_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_ESC_RIS: - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; - + input_reset_cell(ictx); screen_write_reset(sctx); break; case INPUT_ESC_IND: @@ -1050,16 +1175,17 @@ input_esc_dispatch(struct input_ctx *ictx) case INPUT_ESC_DECALN: screen_write_alignmenttest(sctx); break; - case INPUT_ESC_SCSON_G0: - /* - * Not really supported, but fake it up enough for those that - * use it to switch character sets (by redefining G0 to - * graphics set, rather than switching to G1). - */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; + case INPUT_ESC_SCSG0_ON: + ictx->cell.g0set = 1; break; - case INPUT_ESC_SCSOFF_G0: - ictx->cell.attr |= GRID_ATTR_CHARSET; + case INPUT_ESC_SCSG0_OFF: + ictx->cell.g0set = 0; + break; + case INPUT_ESC_SCSG1_ON: + ictx->cell.g1set = 1; + break; + case INPUT_ESC_SCSG1_OFF: + ictx->cell.g1set = 0; break; } @@ -1071,10 +1197,10 @@ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; - int n, m; + int n, m; + u_int cx; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1093,12 +1219,16 @@ input_csi_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_CSI_CBT: /* Find the previous tab point, n times. */ + cx = s->cx; + if (cx > screen_size_x(s) - 1) + cx = screen_size_x(s) - 1; n = input_get(ictx, 0, 1, 1); - while (s->cx > 0 && n-- > 0) { + while (cx > 0 && n-- > 0) { do - s->cx--; - while (s->cx > 0 && !bit_test(s->tabs, s->cx)); + cx--; + while (cx > 0 && !bit_test(s->tabs, cx)); } + s->cx = cx; break; case INPUT_CSI_CUB: screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); @@ -1114,6 +1244,9 @@ input_csi_dispatch(struct input_ctx *ictx) m = input_get(ictx, 1, 1, 1); screen_write_cursormove(sctx, m - 1, n - 1); break; + case INPUT_CSI_WINOPS: + input_csi_dispatch_winops(ictx); + break; case INPUT_CSI_CUU: screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; @@ -1138,7 +1271,7 @@ input_csi_dispatch(struct input_ctx *ictx) case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { case 0: - input_reply(ictx, "\033[>0;95;0c"); + input_reply(ictx, "\033[>84;0;0c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); @@ -1230,59 +1363,10 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_CSI_RM: - switch (input_get(ictx, 0, 0, -1)) { - case 4: /* IRM */ - screen_write_mode_clear(&ictx->ctx, MODE_INSERT); - break; - default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); - break; - } + input_csi_dispatch_rm(ictx); break; case INPUT_CSI_RM_PRIVATE: - switch (input_get(ictx, 0, 0, -1)) { - case 1: /* GATM */ - screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); - break; - case 3: /* DECCOLM */ - screen_write_cursormove(&ictx->ctx, 0, 0); - screen_write_clearscreen(&ictx->ctx); - break; - case 7: /* DECAWM */ - screen_write_mode_clear(&ictx->ctx, MODE_WRAP); - break; - case 25: /* TCEM */ - screen_write_mode_clear(&ictx->ctx, MODE_CURSOR); - break; - case 1000: - case 1001: - case 1002: - case 1003: - screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); - break; - case 1004: - screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); - break; - case 1005: - screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); - break; - case 1006: - screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); - break; - case 47: - case 1047: - window_pane_alternate_off(wp, &ictx->cell, 0); - break; - case 1049: - window_pane_alternate_off(wp, &ictx->cell, 1); - break; - case 2004: - screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); - break; - default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); - break; - } + input_csi_dispatch_rm_private(ictx); break; case INPUT_CSI_SCP: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); @@ -1293,68 +1377,10 @@ input_csi_dispatch(struct input_ctx *ictx) input_csi_dispatch_sgr(ictx); break; case INPUT_CSI_SM: - switch (input_get(ictx, 0, 0, -1)) { - case 4: /* IRM */ - screen_write_mode_set(&ictx->ctx, MODE_INSERT); - break; - default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); - break; - } + input_csi_dispatch_sm(ictx); break; case INPUT_CSI_SM_PRIVATE: - switch (input_get(ictx, 0, 0, -1)) { - case 1: /* GATM */ - screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); - break; - case 3: /* DECCOLM */ - screen_write_cursormove(&ictx->ctx, 0, 0); - screen_write_clearscreen(&ictx->ctx); - break; - case 7: /* DECAWM */ - screen_write_mode_set(&ictx->ctx, MODE_WRAP); - break; - case 25: /* TCEM */ - screen_write_mode_set(&ictx->ctx, MODE_CURSOR); - break; - case 1000: - screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_STANDARD); - break; - case 1002: - screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_BUTTON); - break; - case 1003: - screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY); - break; - case 1004: - if (s->mode & MODE_FOCUSON) - break; - screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); - wp->flags &= ~PANE_FOCUSED; /* force update if needed */ - break; - case 1005: - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); - break; - case 1006: - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); - break; - case 47: - case 1047: - window_pane_alternate_on(wp, &ictx->cell, 0); - break; - case 1049: - window_pane_alternate_on(wp, &ictx->cell, 1); - break; - case 2004: - screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); - break; - default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); - break; - } + input_csi_dispatch_sm_private(ictx); break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { @@ -1383,19 +1409,283 @@ input_csi_dispatch(struct input_ctx *ictx) return (0); } +/* Handle CSI RM. */ +void +input_csi_dispatch_rm(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 4: /* IRM */ + screen_write_mode_clear(&ictx->ctx, MODE_INSERT); + break; + case 34: + screen_write_mode_set(&ictx->ctx, MODE_BLINKING); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + } +} + +/* Handle CSI private RM. */ +void +input_csi_dispatch_rm_private(struct input_ctx *ictx) +{ + struct window_pane *wp = ictx->wp; + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 1: /* DECCKM */ + screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); + break; + case 3: /* DECCOLM */ + screen_write_cursormove(&ictx->ctx, 0, 0); + screen_write_clearscreen(&ictx->ctx); + break; + case 7: /* DECAWM */ + screen_write_mode_clear(&ictx->ctx, MODE_WRAP); + break; + case 12: + screen_write_mode_clear(&ictx->ctx, MODE_BLINKING); + break; + case 25: /* TCEM */ + screen_write_mode_clear(&ictx->ctx, MODE_CURSOR); + break; + case 1000: + case 1001: + case 1002: + screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); + break; + case 1004: + screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); + break; + case 1005: + screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); + break; + case 1006: + screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); + break; + case 47: + case 1047: + window_pane_alternate_off(wp, &ictx->cell.cell, 0); + break; + case 1049: + window_pane_alternate_off(wp, &ictx->cell.cell, 1); + break; + case 2004: + screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + } +} + +/* Handle CSI SM. */ +void +input_csi_dispatch_sm(struct input_ctx *ictx) +{ + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 4: /* IRM */ + screen_write_mode_set(&ictx->ctx, MODE_INSERT); + break; + case 34: + screen_write_mode_clear(&ictx->ctx, MODE_BLINKING); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + } +} + +/* Handle CSI private SM. */ +void +input_csi_dispatch_sm_private(struct input_ctx *ictx) +{ + struct window_pane *wp = ictx->wp; + u_int i; + + for (i = 0; i < ictx->param_list_len; i++) { + switch (input_get(ictx, i, 0, -1)) { + case 1: /* DECCKM */ + screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); + break; + case 3: /* DECCOLM */ + screen_write_cursormove(&ictx->ctx, 0, 0); + screen_write_clearscreen(&ictx->ctx); + break; + case 7: /* DECAWM */ + screen_write_mode_set(&ictx->ctx, MODE_WRAP); + break; + case 12: + screen_write_mode_set(&ictx->ctx, MODE_BLINKING); + break; + case 25: /* TCEM */ + screen_write_mode_set(&ictx->ctx, MODE_CURSOR); + break; + case 1000: + screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_STANDARD); + break; + case 1002: + screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_BUTTON); + break; + case 1004: + if (ictx->ctx.s->mode & MODE_FOCUSON) + break; + screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); + wp->flags |= PANE_FOCUSPUSH; /* force update */ + break; + case 1005: + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); + break; + case 1006: + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); + break; + case 47: + case 1047: + window_pane_alternate_on(wp, &ictx->cell.cell, 0); + break; + case 1049: + window_pane_alternate_on(wp, &ictx->cell.cell, 1); + break; + case 2004: + screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + } +} + +/* Handle CSI window operations. */ +void +input_csi_dispatch_winops(struct input_ctx *ictx) +{ + struct window_pane *wp = ictx->wp; + int n, m; + + m = 0; + while ((n = input_get(ictx, m, 0, -1)) != -1) { + switch (n) { + case 1: + case 2: + case 5: + case 6: + case 7: + case 11: + case 13: + case 14: + case 19: + case 20: + case 21: + case 24: + break; + case 3: + case 4: + case 8: + m++; + if (input_get(ictx, m, 0, -1) == -1) + return; + /* FALLTHROUGH */ + case 9: + case 10: + case 22: + case 23: + m++; + if (input_get(ictx, m, 0, -1) == -1) + return; + break; + case 18: + input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + m++; + } +} + +/* Handle CSI SGR for 256 colours. */ +void +input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) +{ + struct grid_cell *gc = &ictx->cell.cell; + int c; + + (*i)++; + c = input_get(ictx, *i, 0, -1); + if (c == -1) { + if (fgbg == 38) { + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = 8; + } else if (fgbg == 48) { + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = 8; + } + } else { + if (fgbg == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = c; + } else if (fgbg == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = c; + } + } +} + +/* Handle CSI SGR for RGB colours. */ +void +input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) +{ + struct grid_cell *gc = &ictx->cell.cell; + int c, r, g, b; + + (*i)++; + r = input_get(ictx, *i, 0, -1); + if (r == -1 || r > 255) + return; + (*i)++; + g = input_get(ictx, *i, 0, -1); + if (g == -1 || g > 255) + return; + (*i)++; + b = input_get(ictx, *i, 0, -1); + if (b == -1 || b > 255) + return; + + c = colour_find_rgb(r, g, b); + if (fgbg == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = c; + } else if (fgbg == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = c; + } +} + /* Handle CSI SGR. */ void input_csi_dispatch_sgr(struct input_ctx *ictx) { - struct grid_cell *gc = &ictx->cell; + struct grid_cell *gc = &ictx->cell.cell; u_int i; - int n, m; - u_char attr; + int n; if (ictx->param_list_len == 0) { - attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); - gc->attr |= (attr & GRID_ATTR_CHARSET); return; } @@ -1404,28 +1694,13 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) if (n == 38 || n == 48) { i++; - if (input_get(ictx, i, 0, -1) != 5) - continue; - - i++; - m = input_get(ictx, i, 0, -1); - if (m == -1) { - if (n == 38) { - gc->flags &= ~GRID_FLAG_FG256; - gc->fg = 8; - } else if (n == 48) { - gc->flags &= ~GRID_FLAG_BG256; - gc->bg = 8; - } - - } else { - if (n == 38) { - gc->flags |= GRID_FLAG_FG256; - gc->fg = m; - } else if (n == 48) { - gc->flags |= GRID_FLAG_BG256; - gc->bg = m; - } + switch (input_get(ictx, i, 0, -1)) { + case 2: + input_csi_dispatch_sgr_rgb(ictx, n, &i); + break; + case 5: + input_csi_dispatch_sgr_256(ictx, n, &i); + break; } continue; } @@ -1433,9 +1708,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) switch (n) { case 0: case 10: - attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); - gc->attr |= (attr & GRID_ATTR_CHARSET); break; case 1: gc->attr |= GRID_ATTR_BRIGHT; @@ -1564,8 +1837,8 @@ input_enter_osc(struct input_ctx *ictx) void input_exit_osc(struct input_ctx *ictx) { - u_char *p = ictx->input_buf; - int option; + u_char *p = ictx->input_buf; + u_int option; if (ictx->flags & INPUT_DISCARD) return; @@ -1636,12 +1909,12 @@ input_exit_rename(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; - if (!options_get_number(&ictx->wp->window->options, "allow-rename")) + if (!options_get_number(ictx->wp->window->options, "allow-rename")) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); window_set_name(ictx->wp->window, ictx->input_buf); - options_set_number(&ictx->wp->window->options, "automatic-rename", 0); + options_set_number(ictx->wp->window->options, "automatic-rename", 0); server_status_window(ictx->wp->window); } @@ -1650,14 +1923,13 @@ input_exit_rename(struct input_ctx *ictx) int input_utf8_open(struct input_ctx *ictx) { - if (!options_get_number(&ictx->wp->window->options, "utf8")) { - /* Print, and do not switch state. */ - input_print(ictx); - return (-1); - } - log_debug("%s", __func__); + struct utf8_data *ud = &ictx->utf8data; + + if (utf8_open(ud, ictx->ch) != UTF8_MORE) + fatalx("UTF-8 open invalid %#x", ictx->ch); + + log_debug("%s %hhu", __func__, ud->size); - utf8_open(&ictx->utf8data, ictx->ch); return (0); } @@ -1665,9 +1937,13 @@ input_utf8_open(struct input_ctx *ictx) int input_utf8_add(struct input_ctx *ictx) { + struct utf8_data *ud = &ictx->utf8data; + + if (utf8_append(ud, ictx->ch) != UTF8_MORE) + fatalx("UTF-8 add invalid %#x", ictx->ch); + log_debug("%s", __func__); - utf8_append(&ictx->utf8data, ictx->ch); return (0); } @@ -1675,12 +1951,16 @@ input_utf8_add(struct input_ctx *ictx) int input_utf8_close(struct input_ctx *ictx) { - log_debug("%s", __func__); + struct utf8_data *ud = &ictx->utf8data; - utf8_append(&ictx->utf8data, ictx->ch); + if (utf8_append(ud, ictx->ch) != UTF8_DONE) + fatalx("UTF-8 close invalid %#x", ictx->ch); - grid_cell_set(&ictx->cell, &ictx->utf8data); - screen_write_cell(&ictx->ctx, &ictx->cell); + log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, + (int)ud->size, ud->data, ud->width); + + utf8_copy(&ictx->cell.cell.data, ud); + screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); } diff --git a/job.c b/job.c index 6a7286ae..902fc3e6 100644 --- a/job.c +++ b/job.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -39,32 +40,40 @@ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * -job_run(const char *cmd, struct session *s, +job_run(const char *cmd, struct session *s, const char *cwd, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; - struct environ env; + struct environ *env; pid_t pid; int nullfd, out[2]; + const char *home; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); - environ_init(&env); - environ_copy(&global_environ, &env); + env = environ_create(); + environ_copy(global_environ, env); if (s != NULL) - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + environ_copy(s->environ, env); + server_fill_environ(s, env); switch (pid = fork()) { case -1: - environ_free(&env); + environ_free(env); + close(out[0]); + close(out[1]); return (NULL); case 0: /* child */ clear_signals(1); - environ_push(&env); - environ_free(&env); + if (cwd == NULL || chdir(cwd) != 0) { + if ((home = find_home()) == NULL || chdir(home) != 0) + chdir("/"); + } + + environ_push(env); + environ_free(env); if (dup2(out[1], STDIN_FILENO) == -1) fatal("dup2 failed"); @@ -89,10 +98,12 @@ job_run(const char *cmd, struct session *s, } /* parent */ - environ_free(&env); + environ_free(env); close(out[1]); job = xmalloc(sizeof *job); + job->state = JOB_RUNNING; + job->cmd = xstrdup(cmd); job->pid = pid; job->status = 0; @@ -108,7 +119,7 @@ job_run(const char *cmd, struct session *s, job->event = bufferevent_new(job->fd, NULL, job_write_callback, job_callback, job); - bufferevent_enable(job->event, EV_READ); + bufferevent_enable(job->event, EV_READ|EV_WRITE); log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); return (job); @@ -138,13 +149,13 @@ job_free(struct job *job) /* Called when output buffer falls below low watermark (default is 0). */ void -job_write_callback(unused struct bufferevent *bufev, void *data) +job_write_callback(__unused struct bufferevent *bufev, void *data) { struct job *job = data; size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); - log_debug("job write %p: %s, pid %ld, output left %lu", job, job->cmd, - (long) job->pid, (unsigned long) len); + log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, + (long) job->pid, len); if (len == 0) { shutdown(job->fd, SHUT_WR); @@ -154,20 +165,20 @@ job_write_callback(unused struct bufferevent *bufev, void *data) /* Job buffer error callback. */ void -job_callback(unused struct bufferevent *bufev, unused short events, void *data) +job_callback(__unused struct bufferevent *bufev, __unused short events, + void *data) { struct job *job = data; log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); - if (job->pid == -1) { + if (job->state == JOB_DEAD) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else { bufferevent_disable(job->event, EV_READ); - close(job->fd); - job->fd = -1; + job->state = JOB_CLOSED; } } @@ -179,10 +190,12 @@ job_died(struct job *job, int status) job->status = status; - if (job->fd == -1) { + if (job->state == JOB_CLOSED) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); - } else + } else { job->pid = -1; + job->state = JOB_DEAD; + } } diff --git a/key-bindings.c b/key-bindings.c index 86048ea6..47a7d867 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -25,183 +25,235 @@ #include "tmux.h" RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); +RB_GENERATE(key_tables, key_table, entry, key_table_cmp); +struct key_tables key_tables = RB_INITIALIZER(&key_tables); -struct key_bindings key_bindings; -struct key_bindings dead_key_bindings; +int +key_table_cmp(struct key_table *e1, struct key_table *e2) +{ + return (strcmp(e1->name, e2->name)); +} int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { - int key1, key2; - - key1 = bd1->key & ~KEYC_PREFIX; - key2 = bd2->key & ~KEYC_PREFIX; - if (key1 != key2) - return (key1 - key2); - - if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) + if (bd1->key < bd2->key) return (-1); - if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) + if (bd1->key > bd2->key) return (1); return (0); } -struct key_binding * -key_bindings_lookup(int key) +struct key_table * +key_bindings_get_table(const char *name, int create) { - struct key_binding bd; + struct key_table table_find, *table; - bd.key = key; - return (RB_FIND(key_bindings, &key_bindings, &bd)); + table_find.name = name; + table = RB_FIND(key_tables, &key_tables, &table_find); + if (table != NULL || !create) + return (table); + + table = xmalloc(sizeof *table); + table->name = xstrdup(name); + RB_INIT(&table->key_bindings); + + table->references = 1; /* one reference in key_tables */ + RB_INSERT(key_tables, &key_tables, table); + + return (table); } void -key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) +key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; - key_bindings_remove(key); + if (--table->references != 0) + return; + + while (!RB_EMPTY(&table->key_bindings)) { + bd = RB_ROOT(&table->key_bindings); + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); + } + + free((void *)table->name); + free(table); +} + +void +key_bindings_add(const char *name, key_code key, int can_repeat, + struct cmd_list *cmdlist) +{ + struct key_table *table; + struct key_binding bd_find, *bd; + + table = key_bindings_get_table(name, 1); + + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd != NULL) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); + } bd = xmalloc(sizeof *bd); bd->key = key; - RB_INSERT(key_bindings, &key_bindings, bd); + RB_INSERT(key_bindings, &table->key_bindings, bd); bd->can_repeat = can_repeat; bd->cmdlist = cmdlist; } void -key_bindings_remove(int key) +key_bindings_remove(const char *name, key_code key) { - struct key_binding *bd; + struct key_table *table; + struct key_binding bd_find, *bd; - if ((bd = key_bindings_lookup(key)) == NULL) + table = key_bindings_get_table(name, 0); + if (table == NULL) return; - RB_REMOVE(key_bindings, &key_bindings, bd); - RB_INSERT(key_bindings, &dead_key_bindings, bd); + + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd == NULL) + return; + + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); + + if (RB_EMPTY(&table->key_bindings)) { + RB_REMOVE(key_tables, &key_tables, table); + key_bindings_unref_table(table); + } } void -key_bindings_clean(void) +key_bindings_remove_table(const char *name) { - struct key_binding *bd; + struct key_table *table; - while (!RB_EMPTY(&dead_key_bindings)) { - bd = RB_ROOT(&dead_key_bindings); - RB_REMOVE(key_bindings, &dead_key_bindings, bd); - cmd_list_free(bd->cmdlist); - free(bd); + table = key_bindings_get_table(name, 0); + if (table != NULL) { + RB_REMOVE(key_tables, &key_tables, table); + key_bindings_unref_table(table); } } void key_bindings_init(void) { - static const struct { - int key; - int can_repeat; - const struct cmd_entry *entry; - } table[] = { - { ' ', 0, &cmd_next_layout_entry }, - { '!', 0, &cmd_break_pane_entry }, - { '"', 0, &cmd_split_window_entry }, - { '#', 0, &cmd_list_buffers_entry }, - { '$', 0, &cmd_command_prompt_entry }, - { '%', 0, &cmd_split_window_entry }, - { '&', 0, &cmd_confirm_before_entry }, - { '(', 0, &cmd_switch_client_entry }, - { ')', 0, &cmd_switch_client_entry }, - { ',', 0, &cmd_command_prompt_entry }, - { '-', 0, &cmd_delete_buffer_entry }, - { '.', 0, &cmd_command_prompt_entry }, - { '0', 0, &cmd_select_window_entry }, - { '1', 0, &cmd_select_window_entry }, - { '2', 0, &cmd_select_window_entry }, - { '3', 0, &cmd_select_window_entry }, - { '4', 0, &cmd_select_window_entry }, - { '5', 0, &cmd_select_window_entry }, - { '6', 0, &cmd_select_window_entry }, - { '7', 0, &cmd_select_window_entry }, - { '8', 0, &cmd_select_window_entry }, - { '9', 0, &cmd_select_window_entry }, - { ':', 0, &cmd_command_prompt_entry }, - { ';', 0, &cmd_last_pane_entry }, - { '=', 0, &cmd_choose_buffer_entry }, - { '?', 0, &cmd_list_keys_entry }, - { 'D', 0, &cmd_choose_client_entry }, - { 'L', 0, &cmd_switch_client_entry }, - { '[', 0, &cmd_copy_mode_entry }, - { '\'', 0, &cmd_command_prompt_entry }, - { '\002', /* C-b */ 0, &cmd_send_prefix_entry }, - { '\017', /* C-o */ 0, &cmd_rotate_window_entry }, - { '\032', /* C-z */ 0, &cmd_suspend_client_entry }, - { ']', 0, &cmd_paste_buffer_entry }, - { 'c', 0, &cmd_new_window_entry }, - { 'd', 0, &cmd_detach_client_entry }, - { 'f', 0, &cmd_command_prompt_entry }, - { 'i', 0, &cmd_display_message_entry }, - { 'l', 0, &cmd_last_window_entry }, - { 'n', 0, &cmd_next_window_entry }, - { 'o', 0, &cmd_select_pane_entry }, - { 'p', 0, &cmd_previous_window_entry }, - { 'q', 0, &cmd_display_panes_entry }, - { 'r', 0, &cmd_refresh_client_entry }, - { 's', 0, &cmd_choose_tree_entry }, - { 't', 0, &cmd_clock_mode_entry }, - { 'w', 0, &cmd_choose_window_entry }, - { 'x', 0, &cmd_confirm_before_entry }, - { 'z', 0, &cmd_resize_pane_entry }, - { '{', 0, &cmd_swap_pane_entry }, - { '}', 0, &cmd_swap_pane_entry }, - { '~', 0, &cmd_show_messages_entry }, - { '1' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, - { '2' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, - { '3' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, - { '4' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, - { '5' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, - { KEYC_PPAGE, 0, &cmd_copy_mode_entry }, - { 'n' | KEYC_ESCAPE, 0, &cmd_next_window_entry }, - { 'o' | KEYC_ESCAPE, 0, &cmd_rotate_window_entry }, - { 'p' | KEYC_ESCAPE, 0, &cmd_previous_window_entry }, - { KEYC_UP, 1, &cmd_select_pane_entry }, - { KEYC_DOWN, 1, &cmd_select_pane_entry }, - { KEYC_LEFT, 1, &cmd_select_pane_entry }, - { KEYC_RIGHT, 1, &cmd_select_pane_entry }, - { KEYC_UP | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, - { KEYC_DOWN | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, - { KEYC_LEFT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, - { KEYC_RIGHT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, - { KEYC_UP | KEYC_CTRL, 1, &cmd_resize_pane_entry }, - { KEYC_DOWN | KEYC_CTRL, 1, &cmd_resize_pane_entry }, - { KEYC_LEFT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, - { KEYC_RIGHT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, + static const char *defaults[] = { + "bind C-b send-prefix", + "bind C-o rotate-window", + "bind C-z suspend-client", + "bind Space next-layout", + "bind ! break-pane", + "bind '\"' split-window", + "bind '#' list-buffers", + "bind '$' command-prompt -I'#S' \"rename-session '%%'\"", + "bind % split-window -h", + "bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window", + "bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"", + "bind ( switch-client -p", + "bind ) switch-client -n", + "bind , command-prompt -I'#W' \"rename-window '%%'\"", + "bind - delete-buffer", + "bind . command-prompt \"move-window -t '%%'\"", + "bind 0 select-window -t:=0", + "bind 1 select-window -t:=1", + "bind 2 select-window -t:=2", + "bind 3 select-window -t:=3", + "bind 4 select-window -t:=4", + "bind 5 select-window -t:=5", + "bind 6 select-window -t:=6", + "bind 7 select-window -t:=7", + "bind 8 select-window -t:=8", + "bind 9 select-window -t:=9", + "bind : command-prompt", + "bind \\; last-pane", + "bind = choose-buffer", + "bind ? list-keys", + "bind D choose-client", + "bind L switch-client -l", + "bind M select-pane -M", + "bind [ copy-mode", + "bind ] paste-buffer", + "bind c new-window", + "bind d detach-client", + "bind f command-prompt \"find-window '%%'\"", + "bind i display-message", + "bind l last-window", + "bind m select-pane -m", + "bind n next-window", + "bind o select-pane -t:.+", + "bind p previous-window", + "bind q display-panes", + "bind r refresh-client", + "bind s choose-tree", + "bind t clock-mode", + "bind w choose-window", + "bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", + "bind z resize-pane -Z", + "bind { swap-pane -U", + "bind } swap-pane -D", + "bind '~' show-messages", + "bind PPage copy-mode -u", + "bind -r Up select-pane -U", + "bind -r Down select-pane -D", + "bind -r Left select-pane -L", + "bind -r Right select-pane -R", + "bind M-1 select-layout even-horizontal", + "bind M-2 select-layout even-vertical", + "bind M-3 select-layout main-horizontal", + "bind M-4 select-layout main-vertical", + "bind M-5 select-layout tiled", + "bind M-n next-window -a", + "bind M-o rotate-window -D", + "bind M-p previous-window -a", + "bind -r M-Up resize-pane -U 5", + "bind -r M-Down resize-pane -D 5", + "bind -r M-Left resize-pane -L 5", + "bind -r M-Right resize-pane -R 5", + "bind -r C-Up resize-pane -U", + "bind -r C-Down resize-pane -D", + "bind -r C-Left resize-pane -L", + "bind -r C-Right resize-pane -R", + "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", + "bind -n MouseDrag1Border resize-pane -M", + "bind -n MouseDown1Status select-window -t=", + "bind -n WheelDownStatus next-window", + "bind -n WheelUpStatus previous-window", + "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", + "bind -n MouseDown3Pane if-shell -Ft= '#{mouse_any_flag}' 'select-pane -t=; send-keys -M' 'select-pane -mt='", + "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", }; u_int i; - struct cmd *cmd; struct cmd_list *cmdlist; + char *cause; + int error; + struct cmd_q *cmdq; - RB_INIT(&key_bindings); - - for (i = 0; i < nitems(table); i++) { - cmdlist = xcalloc(1, sizeof *cmdlist); - cmdlist->references = 1; - TAILQ_INIT(&cmdlist->list); - - cmd = xcalloc(1, sizeof *cmd); - cmd->entry = table[i].entry; - if (cmd->entry->key_binding != NULL) - cmd->entry->key_binding(cmd, table[i].key); - else - cmd->args = args_create(0); - TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry); - - key_bindings_add( - table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist); + cmdq = cmdq_new(NULL); + for (i = 0; i < nitems(defaults); i++) { + error = cmd_string_parse(defaults[i], &cmdlist, + "", i, &cause); + if (error != 0) + fatalx("bad default key"); + cmdq_run(cmdq, cmdlist, NULL); + cmd_list_free(cmdlist); } + cmdq_free(cmdq); } void -key_bindings_dispatch(struct key_binding *bd, struct client *c) +key_bindings_dispatch(struct key_binding *bd, struct client *c, + struct mouse_event *m) { struct cmd *cmd; int readonly; @@ -212,9 +264,9 @@ key_bindings_dispatch(struct key_binding *bd, struct client *c) readonly = 0; } if (!readonly && (c->flags & CLIENT_READONLY)) { - cmdq_info(c->cmdq, "client is read-only"); + cmdq_error(c->cmdq, "client is read-only"); return; } - cmdq_run(c->cmdq, bd->cmdlist); + cmdq_run(c->cmdq, bd->cmdlist, m); } diff --git a/key-string.c b/key-string.c index 797eedd5..1ff3ca30 100644 --- a/key-string.c +++ b/key-string.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -22,12 +22,12 @@ #include "tmux.h" -int key_string_search_table(const char *); -int key_string_get_modifiers(const char **); +static key_code key_string_search_table(const char *); +static key_code key_string_get_modifiers(const char **); const struct { const char *string; - int key; + key_code key; } key_string_table[] = { /* Function keys. */ { "F1", KEYC_F1 }, @@ -42,14 +42,6 @@ const struct { { "F10", KEYC_F10 }, { "F11", KEYC_F11 }, { "F12", KEYC_F12 }, - { "F13", KEYC_F13 }, - { "F14", KEYC_F14 }, - { "F15", KEYC_F15 }, - { "F16", KEYC_F16 }, - { "F17", KEYC_F17 }, - { "F18", KEYC_F18 }, - { "F19", KEYC_F19 }, - { "F20", KEYC_F20 }, { "IC", KEYC_IC }, { "DC", KEYC_DC }, { "Home", KEYC_HOME }, @@ -90,10 +82,23 @@ const struct { { "KPEnter", KEYC_KP_ENTER }, { "KP0", KEYC_KP_ZERO }, { "KP.", KEYC_KP_PERIOD }, + + /* Mouse keys. */ + KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), + KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2), + KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3), + KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1), + KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2), + KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3), + KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), + KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), + KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), + KEYC_MOUSE_STRING(WHEELUP, WheelUp), + KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), }; /* Find key string in table. */ -int +static key_code key_string_search_table(const char *string) { u_int i; @@ -102,14 +107,14 @@ key_string_search_table(const char *string) if (strcasecmp(string, key_string_table[i].string) == 0) return (key_string_table[i].key); } - return (KEYC_NONE); + return (KEYC_UNKNOWN); } /* Find modifiers. */ -int +static key_code key_string_get_modifiers(const char **string) { - int modifiers; + key_code modifiers; modifiers = 0; while (((*string)[0] != '\0') && (*string)[1] == '-') { @@ -133,18 +138,26 @@ key_string_get_modifiers(const char **string) } /* Lookup a string and convert to a key value. */ -int +key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; - int key, modifiers; + key_code key; u_short u; int size; + key_code modifiers; + struct utf8_data ud; + u_int i; + enum utf8_state more; + + /* Is this no key? */ + if (strcasecmp(string, "None") == 0) + return (KEYC_NONE); /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%hx%n", &u, &size) != 1 || size > 4) - return (KEYC_NONE); + return (KEYC_UNKNOWN); return (u); } @@ -156,18 +169,30 @@ key_string_lookup_string(const char *string) } modifiers |= key_string_get_modifiers(&string); if (string[0] == '\0') - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Is this a standard ASCII key? */ - if (string[1] == '\0') { - key = (u_char) string[0]; - if (key < 32 || key == 127 || key > 255) - return (KEYC_NONE); + if (string[1] == '\0' && (u_char)string[0] <= 127) { + key = (u_char)string[0]; + if (key < 32 || key == 127) + return (KEYC_UNKNOWN); } else { + /* Try as a UTF-8 key. */ + if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { + if (strlen(string) != ud.size) + return (KEYC_UNKNOWN); + for (i = 1; i < ud.size; i++) + more = utf8_append(&ud, (u_char)string[i]); + if (more != UTF8_DONE) + return (KEYC_UNKNOWN); + key = utf8_combine(&ud); + return (key | modifiers); + } + /* Otherwise look the key up in the table. */ key = key_string_search_table(string); - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); } /* Convert the standard control keys. */ @@ -181,7 +206,7 @@ key_string_lookup_string(const char *string) else if (key == 63) key = KEYC_BSPACE; else - return (KEYC_NONE); + return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; } @@ -190,17 +215,24 @@ key_string_lookup_string(const char *string) /* Convert a key code into string format, with prefix if necessary. */ const char * -key_string_lookup_key(int key) +key_string_lookup_key(key_code key) { - static char out[24]; - char tmp[8]; - u_int i; + static char out[24]; + char tmp[8]; + u_int i; + struct utf8_data ud; *out = '\0'; /* Handle no key. */ if (key == KEYC_NONE) - return ("none"); + return ("None"); + + /* Handle special keys. */ + if (key == KEYC_UNKNOWN) + return ("Unknown"); + if (key == KEYC_MOUSE) + return ("Mouse"); /* * Special case: display C-@ as C-Space. Could do this below in @@ -230,21 +262,32 @@ key_string_lookup_key(int key) return (out); } + /* Is this a UTF-8 key? */ + if (key > 127 && key < KEYC_BASE) { + if (utf8_split(key, &ud) == UTF8_DONE) { + memcpy(out, ud.data, ud.size); + out[ud.size] = '\0'; + return (out); + } + } + /* Invalid keys are errors. */ - if (key == 127 || key > 255) - return (NULL); + if (key == 127 || key > 255) { + snprintf(out, sizeof out, "Invalid#%llx", key); + return (out); + } /* Check for standard or control key. */ - if (key >= 0 && key <= 32) { + if (key <= 32) { if (key == 0 || key > 26) - xsnprintf(tmp, sizeof tmp, "C-%c", 64 + key); + xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key)); else - xsnprintf(tmp, sizeof tmp, "C-%c", 96 + key); + xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key)); } else if (key >= 32 && key <= 126) { tmp[0] = key; tmp[1] = '\0'; } else if (key >= 128) - xsnprintf(tmp, sizeof tmp, "\\%o", key); + xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); return (out); diff --git a/layout-custom.c b/layout-custom.c index e32d9d9d..57503518 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott @@ -55,12 +55,12 @@ layout_checksum(const char *layout) /* Dump layout as a string. */ char * -layout_dump(struct window *w) +layout_dump(struct layout_cell *root) { char layout[BUFSIZ], *out; *layout = '\0'; - if (layout_append(w->layout_root, layout, sizeof layout) != 0) + if (layout_append(root, layout, sizeof layout) != 0) return (NULL); xasprintf(&out, "%04x,%s", layout_checksum(layout), layout); diff --git a/layout-set.c b/layout-set.c index 646528ba..852ec0f6 100644 --- a/layout-set.c +++ b/layout-set.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -23,8 +23,8 @@ #include "tmux.h" /* - * Set window layouts - predefined methods to arrange windows. These are one-off - * and generate a layout tree. + * Set window layouts - predefined methods to arrange windows. These are + * one-off and generate a layout tree. */ void layout_set_even_h(struct window *); @@ -44,12 +44,6 @@ const struct { { "tiled", layout_set_tiled }, }; -const char * -layout_set_name(u_int layout) -{ - return (layout_sets[layout].name); -} - int layout_set_lookup(const char *name) { @@ -251,10 +245,10 @@ layout_set_main_h(struct window *w) width = (w->sx - (n - 1)) / columns; /* 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; + otherheight = options_get_number(w->options, "other-pane-height") + 1; /* * If an other pane height was specified, honour it so long as it @@ -372,10 +366,10 @@ layout_set_main_v(struct window *w) height = (w->sy - (n - 1)) / rows; /* 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; + otherwidth = options_get_number(w->options, "other-pane-width") + 1; /* * If an other pane width was specified, honour it so long as it diff --git a/layout.c b/layout.c index b74bd789..c448814a 100644 --- a/layout.c +++ b/layout.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -85,9 +85,9 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) { struct layout_cell *lcchild; - log_debug( - "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc, - lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy); + log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, + " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, + lc->sy); switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: @@ -100,8 +100,8 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) } void -layout_set_size( - struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff) +layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, + u_int yoff) { lc->sx = sx; lc->sy = sy; @@ -519,59 +519,10 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) notify_window_layout_changed(wp->window); } -/* Resize pane based on mouse events. */ -void -layout_resize_pane_mouse(struct client *c) -{ - struct window *w; - struct window_pane *wp; - struct mouse_event *m = &c->tty.mouse; - int pane_border; - - w = c->session->curw->window; - - pane_border = 0; - if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->xoff + wp->sx == m->lx && - wp->yoff <= 1 + m->ly && - wp->yoff + wp->sy >= m->ly) { - layout_resize_pane(wp, LAYOUT_LEFTRIGHT, - m->x - m->lx); - pane_border = 1; - } - if (wp->yoff + wp->sy == m->ly && - wp->xoff <= 1 + m->lx && - wp->xoff + wp->sx >= m->lx) { - layout_resize_pane(wp, LAYOUT_TOPBOTTOM, - m->y - m->ly); - pane_border = 1; - } - } - if (pane_border) - server_redraw_window(w); - } else if (~m->event & MOUSE_EVENT_UP) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if ((wp->xoff + wp->sx == m->x && - wp->yoff <= 1 + m->y && - wp->yoff + wp->sy >= m->y) || - (wp->yoff + wp->sy == m->y && - wp->xoff <= 1 + m->x && - wp->xoff + wp->sx >= m->x)) { - pane_border = 1; - } - } - } - if (pane_border) - m->flags |= MOUSE_RESIZE_PANE; - else - m->flags &= ~MOUSE_RESIZE_PANE; -} - /* Helper function to grow pane. */ int -layout_resize_pane_grow( - struct layout_cell *lc, enum layout_type type, int needed) +layout_resize_pane_grow(struct layout_cell *lc, enum layout_type type, + int needed) { struct layout_cell *lcadd, *lcremove; u_int size; @@ -611,8 +562,8 @@ layout_resize_pane_grow( /* Helper function to shrink pane. */ int -layout_resize_pane_shrink( - struct layout_cell *lc, enum layout_type type, int needed) +layout_resize_pane_shrink(struct layout_cell *lc, enum layout_type type, + int needed) { struct layout_cell *lcadd, *lcremove; u_int size; @@ -654,8 +605,8 @@ layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) * split. This must be followed by layout_assign_pane before much else happens! **/ struct layout_cell * -layout_split_pane( - struct window_pane *wp, enum layout_type type, int size, int insert_before) +layout_split_pane(struct window_pane *wp, enum layout_type type, int size, + int insert_before) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; u_int sx, sy, xoff, yoff, size1, size2; @@ -735,6 +686,8 @@ layout_split_pane( case LAYOUT_LEFTRIGHT: if (size < 0) size2 = ((sx + 1) / 2) - 1; + else if (insert_before) + size2 = sx - size - 1; else size2 = size; if (size2 < PANE_MINIMUM) @@ -748,6 +701,8 @@ layout_split_pane( case LAYOUT_TOPBOTTOM: if (size < 0) size2 = ((sy + 1) / 2) - 1; + else if (insert_before) + size2 = sy - size - 1; else size2 = size; if (size2 < PANE_MINIMUM) diff --git a/log.c b/log.c index dbf9ee15..d7f6fe4b 100644 --- a/log.c +++ b/log.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -22,41 +22,57 @@ #include #include #include -#include -#include +#include #include "tmux.h" -/* Log file, if needed. */ -FILE *log_file; +static FILE *log_file; +static int log_level; -/* Debug level. */ -int log_level = 0; - -void log_event_cb(int, const char *); -void log_vwrite(const char *, va_list); -__dead void log_vfatal(const char *, va_list); +static void log_event_cb(int, const char *); +static void log_vwrite(const char *, va_list); /* Log callback for libevent. */ -void -log_event_cb(unused int severity, const char *msg) +static void +log_event_cb(__unused int severity, const char *msg) { - log_warnx("%s", msg); + log_debug("%s", msg); +} + +/* Increment log level. */ +void +log_add_level(void) +{ + log_level++; +} + +/* Get log level. */ +int +log_get_level(void) +{ + return (log_level); } /* Open logging to file. */ void -log_open(int level, const char *path) +log_open(const char *name) { + char *path; + + if (log_level == 0) + return; + + if (log_file != NULL) + fclose(log_file); + + xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid()); log_file = fopen(path, "w"); + free(path); if (log_file == NULL) return; - log_level = level; - setlinebuf(log_file); + setvbuf(log_file, NULL, _IOLBF, 0); event_set_log_callback(log_event_cb); - - tzset(); } /* Close logging. */ @@ -65,45 +81,39 @@ log_close(void) { if (log_file != NULL) fclose(log_file); + log_file = NULL; event_set_log_callback(NULL); } /* Write a log message. */ -void +static void log_vwrite(const char *msg, va_list ap) { - char *fmt; + char *fmt, *out; + struct timeval tv; if (log_file == NULL) return; - if (asprintf(&fmt, "%s\n", msg) == -1) + if (vasprintf(&fmt, msg, ap) == -1) exit(1); - if (vfprintf(log_file, fmt, ap) == -1) + if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) + exit(1); + + gettimeofday(&tv, NULL); + if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, + (int)tv.tv_usec, out) == -1) exit(1); fflush(log_file); + + free(out); free(fmt); } -/* Log a warning with error string. */ -void printflike1 -log_warn(const char *msg, ...) -{ - va_list ap; - char *fmt; - - va_start(ap, msg); - if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1) - exit(1); - log_vwrite(fmt, ap); - free(fmt); - va_end(ap); -} - -/* Log a warning. */ -void printflike1 -log_warnx(const char *msg, ...) +/* Log a debug message. */ +void +log_debug(const char *msg, ...) { va_list ap; @@ -112,82 +122,30 @@ log_warnx(const char *msg, ...) va_end(ap); } -/* Log an informational message. */ -void printflike1 -log_info(const char *msg, ...) -{ - va_list ap; - - if (log_level > -1) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } -} - -/* Log a debug message. */ -void printflike1 -log_debug(const char *msg, ...) -{ - va_list ap; - - if (log_level > 0) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } -} - -/* Log a debug message at level 2. */ -void printflike1 -log_debug2(const char *msg, ...) -{ - va_list ap; - - if (log_level > 1) { - va_start(ap, msg); - log_vwrite(msg, ap); - va_end(ap); - } -} - -/* Log a critical error, with error string if necessary, and die. */ +/* Log a critical error with error string and die. */ __dead void -log_vfatal(const char *msg, va_list ap) +fatal(const char *msg, ...) { char *fmt; + va_list ap; - if (errno != 0) { - if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) - exit(1); - log_vwrite(fmt, ap); - } else { - if (asprintf(&fmt, "fatal: %s", msg) == -1) - exit(1); - log_vwrite(fmt, ap); - } - free(fmt); - + va_start(ap, msg); + if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) + exit(1); + log_vwrite(fmt, ap); exit(1); } -/* Log a critical error, with error string, and die. */ -__dead void printflike1 -log_fatal(const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - log_vfatal(msg, ap); -} - /* Log a critical error and die. */ -__dead void printflike1 -log_fatalx(const char *msg, ...) +__dead void +fatalx(const char *msg, ...) { - va_list ap; + char *fmt; + va_list ap; - errno = 0; va_start(ap, msg); - log_vfatal(msg, ap); + if (asprintf(&fmt, "fatal: %s", msg) == -1) + exit(1); + log_vwrite(fmt, ap); + exit(1); } diff --git a/logo/LICENSE b/logo/LICENSE new file mode 100644 index 00000000..3f44eb59 --- /dev/null +++ b/logo/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2015, Jason Long + +Permission to use, copy, modify, and/or 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. diff --git a/logo/favicon.ico b/logo/favicon.ico new file mode 100644 index 00000000..6e5398a7 Binary files /dev/null and b/logo/favicon.ico differ diff --git a/logo/tmux-logo-1-color.eps b/logo/tmux-logo-1-color.eps new file mode 100644 index 00000000..20018e67 --- /dev/null +++ b/logo/tmux-logo-1-color.eps @@ -0,0 +1,922 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%APL_DSC_Encoding: UTF8 +%APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) +%%Title: (Unknown) +%%Creator: (Unknown) +%%CreationDate: (Unknown) +%%For: (Unknown) +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 0 0 608 160 +%%EndComments +%%BeginProlog +%%BeginFile: cg-pdf.ps +%%Copyright: Copyright 2000-2004 Apple Computer Incorporated. +%%Copyright: All Rights Reserved. +currentpacking true setpacking +/cg_md 141 dict def +cg_md begin +/L3? languagelevel 3 ge def +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/xd{exch def}bd +/cmmtx matrix def +mark +/sc/setcolor +/scs/setcolorspace +/dr/defineresource +/fr/findresource +/T/true +/F/false +/d/setdash +/w/setlinewidth +/J/setlinecap +/j/setlinejoin +/M/setmiterlimit +/i/setflat +/rc/rectclip +/rf/rectfill +/rs/rectstroke +/f/fill +/f*/eofill +/sf/selectfont +/s/show +/xS/xshow +/yS/yshow +/xyS/xyshow +/S/stroke +/m/moveto +/l/lineto +/c/curveto +/h/closepath +/n/newpath +/q/gsave +/Q/grestore +counttomark 2 idiv +{ld}repeat pop +/SC{ + /ColorSpace fr scs +}bd +/sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld +/soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld +/cgmtx matrix def +/sdmtx{cgmtx currentmatrix pop}bd +/CM {cgmtx setmatrix}bd +/cm {cmmtx astore CM concat}bd +/W{clip newpath}bd +/W*{eoclip newpath}bd +statusdict begin product end dup (HP) anchorsearch{ + pop pop pop + true +}{ + pop + (hp) anchorsearch{ + pop pop true + }{ + pop false + }ifelse +}ifelse +{ + { + { + pop pop + (0)dup 0 4 -1 roll put + F charpath + }cshow + } +}{ + {F charpath} +}ifelse +/cply exch bd +/cps {cply stroke}bd +/pgsave 0 def +/bp{/pgsave save store}bd +/ep{pgsave restore showpage}def +/re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd +/scrdict 10 dict def +/scrmtx matrix def +/patarray 0 def +/createpat{patarray 3 1 roll put}bd +/makepat{ +scrmtx astore pop +gsave +initgraphics +CM +patarray exch get +scrmtx +makepattern +grestore +setpattern +}bd +/cg_BeginEPSF{ + userdict save/cg_b4_Inc_state exch put + userdict/cg_endepsf/cg_EndEPSF load put + count userdict/cg_op_count 3 -1 roll put + countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put + 3 sub{end}repeat + /showpage {} def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash newpath + false setstrokeadjust false setoverprint +}bd +/cg_EndEPSF{ + countdictstack 3 sub { end } repeat + cg_dict_array 3 1 index length 3 sub getinterval + {begin}forall + count userdict/cg_op_count get sub{pop}repeat + userdict/cg_b4_Inc_state get restore + F setpacking +}bd +/cg_biproc{currentfile/RunLengthDecode filter}bd +/cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd +/ImageDataSource 0 def +L3?{ + /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd + /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd +}{ + /ImageBandMask 0 def + /ImageBandData 0 def + /cg_mibiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd + /cg_miaiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter + dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd +}ifelse +/imsave 0 def +/BI{save/imsave xd mark}bd +/EI{imsave restore}bd +/ID{ +counttomark 2 idiv +dup 2 add +dict begin +{def} repeat +pop +/ImageType 1 def +/ImageMatrix[Width 0 0 Height neg 0 Height]def +currentdict dup/ImageMask known{ImageMask}{F}ifelse exch +L3?{ + dup/MaskedImage known + { + pop + << + /ImageType 3 + /InterleaveType 2 + /DataDict currentdict + /MaskDict + << /ImageType 1 + /Width Width + /Height Height + /ImageMatrix ImageMatrix + /BitsPerComponent 1 + /Decode [0 1] + currentdict/Interpolate known + {/Interpolate Interpolate}if + >> + >> + }if +}if +exch +{imagemask}{image}ifelse +end +}bd +/cguidfix{statusdict begin mark version end +{cvr}stopped{cleartomark 0}{exch pop}ifelse +2012 lt{dup findfont dup length dict begin +{1 index/FID ne 2 index/UniqueID ne and +{def} {pop pop} ifelse}forall +currentdict end definefont pop +}{pop}ifelse +}bd +/t_array 0 def +/t_i 0 def +/t_c 1 string def +/x_proc{ + exch t_array t_i get add exch moveto + /t_i t_i 1 add store +}bd +/y_proc{ + t_array t_i get add moveto + /t_i t_i 1 add store +}bd +/xy_proc{ + + t_array t_i 2 copy 1 add get 3 1 roll get + 4 -1 roll add 3 1 roll add moveto + /t_i t_i 2 add store +}bd +/sop 0 def +/cp_proc/x_proc ld +/base_charpath +{ + /t_array xs + /t_i 0 def + { + t_c 0 3 -1 roll put + currentpoint + t_c cply sop + cp_proc + }forall + /t_array 0 def +}bd +/sop/stroke ld +/nop{}def +/xsp/base_charpath ld +/ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd +/xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd +/xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd +/ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd +/xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd +/refnt{ +findfont dup length dict copy dup +/Encoding 4 -1 roll put +definefont pop +}bd +/renmfont{ +findfont dup length dict copy definefont pop +}bd +L3? dup dup{save exch}if +/Range 0 def +/DataSource 0 def +/val 0 def +/nRange 0 def +/mulRange 0 def +/d0 0 def +/r0 0 def +/di 0 def +/ri 0 def +/a0 0 def +/a1 0 def +/r1 0 def +/r2 0 def +/dx 0 def +/Nsteps 0 def +/sh3tp 0 def +/ymax 0 def +/ymin 0 def +/xmax 0 def +/xmin 0 def +/setupFunEval +{ + begin + /nRange Range length 2 idiv store + /mulRange + + [ + 0 1 nRange 1 sub + { + 2 mul/nDim2 xd + Range nDim2 get + Range nDim2 1 add get + 1 index sub + + 255 div + exch + }for + ]store + end +}bd +/FunEval +{ + begin + + nRange mul /val xd + + 0 1 nRange 1 sub + { + dup 2 mul/nDim2 xd + val + add DataSource exch get + mulRange nDim2 get mul + mulRange nDim2 1 add get + add + }for + end +}bd +/max +{ + 2 copy lt + {exch pop}{pop}ifelse +}bd +/sh2 +{ + /Coords load aload pop + 3 index 3 index translate + + 3 -1 roll sub + 3 1 roll exch + sub + 2 copy + dup mul exch dup mul add sqrt + dup + scale + atan + + rotate + + /Function load setupFunEval + + + clippath {pathbbox}stopped {0 0 0 0}if newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + currentdict/Extend known + { + /Extend load 0 get + { + 0/Function load FunEval sc + xmin ymin xmin abs ymax ymin sub rectfill + }if + }if + + /Nsteps/Function load/Size get 0 get 1 sub store + /dx 1 Nsteps div store + gsave + /di ymax ymin sub store + /Function load + + 0 1 Nsteps + { + 1 index FunEval sc + 0 ymin dx di rectfill + dx 0 translate + }for + pop + grestore + currentdict/Extend known + { + /Extend load 1 get + { + Nsteps/Function load FunEval sc + 1 ymin xmax 1 sub abs ymax ymin sub rectfill + }if + }if +}bd +/shp +{ + 4 copy + + dup 0 gt{ + 0 exch a1 a0 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a0 a1 arcn + }{ + pop 0 lineto + }ifelse + + fill + + dup 0 gt{ + 0 exch a0 a1 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a1 a0 arcn + }{ + pop 0 lineto + }ifelse + + fill +}bd +/calcmaxs +{ + + xmin dup mul ymin dup mul add sqrt + xmax dup mul ymin dup mul add sqrt + xmin dup mul ymax dup mul add sqrt + xmax dup mul ymax dup mul add sqrt + max max max +}bd +/sh3 +{ + /Coords load aload pop + 5 index 5 index translate + 3 -1 roll 6 -1 roll sub + 3 -1 roll 5 -1 roll sub + 2 copy dup mul exch dup mul add sqrt + /dx xs + 2 copy 0 ne exch 0 ne or + { + + exch atan rotate + }{ + pop pop + }ifelse + + /r2 xs + /r1 xs + /Function load + dup/Size get 0 get 1 sub + /Nsteps xs + setupFunEval + + + + + + dx r2 add r1 lt{ + + 0 + }{ + dx r1 add r2 le + { + 1 + }{ + r1 r2 eq + { + 2 + }{ + 3 + }ifelse + }ifelse + }ifelse + /sh3tp xs + clippath {pathbbox}stopped {0 0 0 0}if + newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + + dx dup mul r2 r1 sub dup mul sub dup 0 gt + { + sqrt r2 r1 sub atan + /a0 exch 180 exch sub store + /a1 a0 neg store + }{ + pop + /a0 0 store + /a1 360 store + }ifelse + currentdict/Extend known + { + /Extend load 0 get r1 0 gt and + { + 0/Function load FunEval sc + + + + + { + { + dx 0 r1 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + r1 0 gt{0 0 r1 0 360 arc fill}if + } + { + + + + + 0 r1 xmin abs r1 add neg r1 shp + } + { + + + r2 r1 gt{ + + 0 r1 + r1 neg r2 r1 sub div dx mul + 0 + shp + }{ + + + + 0 r1 calcmaxs + dup + + r2 add dx mul dx r1 r2 sub sub div + neg + exch 1 index + abs exch sub + shp + }ifelse + } + }sh3tp get exec + }if + }if + + /d0 0 store + /r0 r1 store + /di dx Nsteps div store + /ri r2 r1 sub Nsteps div store + /Function load + 0 1 Nsteps + { + 1 index FunEval sc + d0 di add r0 ri add d0 r0 shp + { + + d0 0 r0 a1 a0 arc + d0 di add 0 r0 ri add a0 a1 arcn + fill + + + d0 0 r0 a0 a1 arc + d0 di add 0 r0 ri add a1 a0 arcn + fill + }pop + + + /d0 d0 di add store + /r0 r0 ri add store + }for + pop + + currentdict/Extend known + { + /Extend load 1 get r2 0 gt and + { + Nsteps/Function load FunEval sc + + + + + { + { + dx 0 r2 0 360 arc fill + } + { + dx 0 r2 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + + + xmax abs r1 add r1 dx r1 shp + } + { + + r2 r1 gt{ + + + + calcmaxs dup + + r1 add dx mul dx r2 r1 sub sub div + exch 1 index + exch sub + dx r2 + shp + }{ + + r1 neg r2 r1 sub div dx mul + 0 + dx + r2 + shp + }ifelse + } + } + sh3tp get exec + }if + }if +}bd +/sh +{ + begin + /ShadingType load dup dup 2 eq exch 3 eq or + { + gsave + newpath + /ColorSpace load scs + currentdict/BBox known + { + /BBox load aload pop + 2 index sub + 3 index + 3 -1 roll exch sub + exch rectclip + }if + 2 eq + {sh2}{sh3}ifelse + grestore + }{ + + pop + (DEBUG: shading type unimplemented\n)print flush + }ifelse + end +}bd +{restore}if not dup{save exch}if + L3?{ + /sh/shfill ld + /csq/clipsave ld + /csQ/cliprestore ld + }if +{restore}if +end +setpacking +%%EndFile +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%PageBoundingBox: 0 0 608 160 +%%BeginPageSetup +cg_md begin +bp +sdmtx +[ /CIEBasedABC 4 dict dup begin +/WhitePoint [ 0.9505 1.0000 1.0891 ] def +/DecodeABC [ +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind +] def +/MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def +/RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def +end ] /Cs1 exch/ColorSpace dr pop +%%EndPageSetup +0.60000002 i +/Cs1 SC +0.23529412 0.23529412 0.23529412 sc +q +2 15.003872 m +2 7.8149743 7.8157911 2 14.998466 2 c +145.00154 2 l +152.17584 2 158 7.8244047 158 15.003872 c +160 15.003872 m +160 6.7174625 153.27803 0 145.00154 0 c +14.998466 0 l +6.7150416 0 0 6.7065849 0 15.003872 c +2 14 m +0 16 l +160 16 l +158 14 l +160 14 m +0 14 l +W +0 0 608 160 rc +-5 21 m +165 21 l +165 -5 l +-5 -5 l +h +f +Q +q +83 90 m +83 160 l +77 160 l +77 14 l +83 14 l +83 84 l +160 84 l +160 90 l +83 90 l +h +0 144.99352 m +0 153.28137 6.7219648 160 14.998466 160 c +145.00154 160 l +153.28496 160 160 153.27509 160 144.99352 c +160 14 l +0 14 l +0 144.99352 l +h +0 144.99352 m +W* +0 0 608 160 rc +-5 165 m +165 165 l +165 9 l +-5 9 l +h +f +Q +q +242 12 m +230.85426 12.165432 222.63789 15.032893 217.12346 20.7679 c +211.60902 26.502909 208.85185 34.995007 209 46.244446 c +209 98 l +189 98 l +189 113 l +209 113 l +209 146 l +225 146 l +225 113 l +262 112.91358 l +262 98.024689 l +225 98 l +225 46.079014 l +225.39507 39.571983 226.99422 34.802074 230.1926 31.769136 c +233.39096 28.736198 237.58186 27.219753 242.76543 27.219753 c +245.19179 27.219753 247.89381 27.49547 250.87161 28.046913 c +253.8494 28.598356 256.88229 29.425508 259.97037 30.528395 c +263.27902 15.97037 l +259.52921 14.646907 255.86215 13.681896 252.27777 13.075309 c +248.69341 12.468721 245.19179 12.165432 241.77284 12.165432 c +242 12 l +h +295 14 m +278.15918 14 l +278 112.91358 l +295 112.91358 l +295 96.370369 l +295 96.370369 304.76617 106.29628 309.67401 109.60493 c +314.58185 112.9136 320.34436 114.5679 326.96167 114.5679 c +332.80695 114.5679 337.79745 112.94117 341.93326 109.68765 c +346.06909 106.43414 349.01926 101.99509 350.78387 96.370369 c +350.78387 96.370369 361.92294 106.29628 366.99622 109.60493 c +372.06952 112.9136 378.08014 114.5679 385.02832 114.5679 c +392.74854 114.5679 398.92459 111.72801 403.55673 106.04815 c +408.18884 100.36829 410.50488 92.730911 410 83.139999 c +410 14 l +394 14 l +393.96167 80.488892 l +393.96167 87.106209 392.69336 91.820976 390.15674 94.633331 c +387.62009 97.445694 383.98062 98.851852 379.23822 98.851852 c +374.49579 98.851852 369.78104 97.418121 365.09375 94.550621 c +360.40649 91.683113 356.24316 87.878212 353 83.135803 c +353 14 l +336.06042 14 l +336.06042 80.488892 l +336.06042 87.106209 334.79211 91.820976 332.25549 94.633331 c +329.71884 97.445694 326.07938 98.851852 321.33698 98.851852 c +316.59457 98.851852 311.87979 97.418121 307.19254 94.550621 c +302.50525 91.683113 298.34192 87.878212 295 83.135803 c +295 14 l +h +438.9505 21.264198 m +433.10519 27.440361 430.18259 35.656738 430 46 c +430 113 l +447 113 l +447 49.220001 l +446.7258 42.16375 448.3801 36.814835 451.68875 33.175308 c +454.99741 29.535784 459.85004 27.716049 466.2468 27.716049 c +471.32007 27.716049 476.36569 29.177351 481.38382 32.099998 c +486.40195 35.022648 491.39243 39.35141 496 45.086418 c +496 112.91358 l +513 113 l +512.89862 14 l +496 14 l +496 30.197531 l +496 30.197531 485.13364 20.271622 479.89493 16.962963 c +474.65622 13.654305 468.78345 12 462.2764 12 c +452.57101 12 444.79578 15.088035 438.9505 21.264198 c +h +588.65784 14 m +564.50476 51.041977 l +541.84052 14 l +523.4776 14 l +556.56403 63.449383 l +524.63562 113 l +543.164 113 l +566.15906 77.180244 l +589.48499 113 l +607.84796 113 l +574.43066 64.441978 l +607.18622 14 l +588.65784 14 l +h +588.65784 14 m +W* +0 0 608 160 rc +184 151 m +612.84796 151 l +612.84796 7 l +184 7 l +h +f +ep +end +%%Trailer +%%EOF diff --git a/logo/tmux-logo-1-color.svg b/logo/tmux-logo-1-color.svg new file mode 100644 index 00000000..2e6dda44 --- /dev/null +++ b/logo/tmux-logo-1-color.svg @@ -0,0 +1,18 @@ + + + + logomark + wordmark copy 3 + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/logo/tmux-logo-huge.png b/logo/tmux-logo-huge.png new file mode 100644 index 00000000..141796d4 Binary files /dev/null and b/logo/tmux-logo-huge.png differ diff --git a/logo/tmux-logo-large.png b/logo/tmux-logo-large.png new file mode 100644 index 00000000..44dce469 Binary files /dev/null and b/logo/tmux-logo-large.png differ diff --git a/logo/tmux-logo-medium.png b/logo/tmux-logo-medium.png new file mode 100644 index 00000000..be2fa5c7 Binary files /dev/null and b/logo/tmux-logo-medium.png differ diff --git a/logo/tmux-logo-small.png b/logo/tmux-logo-small.png new file mode 100644 index 00000000..a13e3d7f Binary files /dev/null and b/logo/tmux-logo-small.png differ diff --git a/logo/tmux-logo.eps b/logo/tmux-logo.eps new file mode 100644 index 00000000..23db6a09 --- /dev/null +++ b/logo/tmux-logo.eps @@ -0,0 +1,925 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%APL_DSC_Encoding: UTF8 +%APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) +%%Title: (Unknown) +%%Creator: (Unknown) +%%CreationDate: (Unknown) +%%For: (Unknown) +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 0 0 608 160 +%%EndComments +%%BeginProlog +%%BeginFile: cg-pdf.ps +%%Copyright: Copyright 2000-2004 Apple Computer Incorporated. +%%Copyright: All Rights Reserved. +currentpacking true setpacking +/cg_md 141 dict def +cg_md begin +/L3? languagelevel 3 ge def +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/xd{exch def}bd +/cmmtx matrix def +mark +/sc/setcolor +/scs/setcolorspace +/dr/defineresource +/fr/findresource +/T/true +/F/false +/d/setdash +/w/setlinewidth +/J/setlinecap +/j/setlinejoin +/M/setmiterlimit +/i/setflat +/rc/rectclip +/rf/rectfill +/rs/rectstroke +/f/fill +/f*/eofill +/sf/selectfont +/s/show +/xS/xshow +/yS/yshow +/xyS/xyshow +/S/stroke +/m/moveto +/l/lineto +/c/curveto +/h/closepath +/n/newpath +/q/gsave +/Q/grestore +counttomark 2 idiv +{ld}repeat pop +/SC{ + /ColorSpace fr scs +}bd +/sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld +/soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld +/cgmtx matrix def +/sdmtx{cgmtx currentmatrix pop}bd +/CM {cgmtx setmatrix}bd +/cm {cmmtx astore CM concat}bd +/W{clip newpath}bd +/W*{eoclip newpath}bd +statusdict begin product end dup (HP) anchorsearch{ + pop pop pop + true +}{ + pop + (hp) anchorsearch{ + pop pop true + }{ + pop false + }ifelse +}ifelse +{ + { + { + pop pop + (0)dup 0 4 -1 roll put + F charpath + }cshow + } +}{ + {F charpath} +}ifelse +/cply exch bd +/cps {cply stroke}bd +/pgsave 0 def +/bp{/pgsave save store}bd +/ep{pgsave restore showpage}def +/re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd +/scrdict 10 dict def +/scrmtx matrix def +/patarray 0 def +/createpat{patarray 3 1 roll put}bd +/makepat{ +scrmtx astore pop +gsave +initgraphics +CM +patarray exch get +scrmtx +makepattern +grestore +setpattern +}bd +/cg_BeginEPSF{ + userdict save/cg_b4_Inc_state exch put + userdict/cg_endepsf/cg_EndEPSF load put + count userdict/cg_op_count 3 -1 roll put + countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put + 3 sub{end}repeat + /showpage {} def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash newpath + false setstrokeadjust false setoverprint +}bd +/cg_EndEPSF{ + countdictstack 3 sub { end } repeat + cg_dict_array 3 1 index length 3 sub getinterval + {begin}forall + count userdict/cg_op_count get sub{pop}repeat + userdict/cg_b4_Inc_state get restore + F setpacking +}bd +/cg_biproc{currentfile/RunLengthDecode filter}bd +/cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd +/ImageDataSource 0 def +L3?{ + /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd + /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd +}{ + /ImageBandMask 0 def + /ImageBandData 0 def + /cg_mibiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd + /cg_miaiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter + dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd +}ifelse +/imsave 0 def +/BI{save/imsave xd mark}bd +/EI{imsave restore}bd +/ID{ +counttomark 2 idiv +dup 2 add +dict begin +{def} repeat +pop +/ImageType 1 def +/ImageMatrix[Width 0 0 Height neg 0 Height]def +currentdict dup/ImageMask known{ImageMask}{F}ifelse exch +L3?{ + dup/MaskedImage known + { + pop + << + /ImageType 3 + /InterleaveType 2 + /DataDict currentdict + /MaskDict + << /ImageType 1 + /Width Width + /Height Height + /ImageMatrix ImageMatrix + /BitsPerComponent 1 + /Decode [0 1] + currentdict/Interpolate known + {/Interpolate Interpolate}if + >> + >> + }if +}if +exch +{imagemask}{image}ifelse +end +}bd +/cguidfix{statusdict begin mark version end +{cvr}stopped{cleartomark 0}{exch pop}ifelse +2012 lt{dup findfont dup length dict begin +{1 index/FID ne 2 index/UniqueID ne and +{def} {pop pop} ifelse}forall +currentdict end definefont pop +}{pop}ifelse +}bd +/t_array 0 def +/t_i 0 def +/t_c 1 string def +/x_proc{ + exch t_array t_i get add exch moveto + /t_i t_i 1 add store +}bd +/y_proc{ + t_array t_i get add moveto + /t_i t_i 1 add store +}bd +/xy_proc{ + + t_array t_i 2 copy 1 add get 3 1 roll get + 4 -1 roll add 3 1 roll add moveto + /t_i t_i 2 add store +}bd +/sop 0 def +/cp_proc/x_proc ld +/base_charpath +{ + /t_array xs + /t_i 0 def + { + t_c 0 3 -1 roll put + currentpoint + t_c cply sop + cp_proc + }forall + /t_array 0 def +}bd +/sop/stroke ld +/nop{}def +/xsp/base_charpath ld +/ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd +/xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd +/xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd +/ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd +/xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd +/refnt{ +findfont dup length dict copy dup +/Encoding 4 -1 roll put +definefont pop +}bd +/renmfont{ +findfont dup length dict copy definefont pop +}bd +L3? dup dup{save exch}if +/Range 0 def +/DataSource 0 def +/val 0 def +/nRange 0 def +/mulRange 0 def +/d0 0 def +/r0 0 def +/di 0 def +/ri 0 def +/a0 0 def +/a1 0 def +/r1 0 def +/r2 0 def +/dx 0 def +/Nsteps 0 def +/sh3tp 0 def +/ymax 0 def +/ymin 0 def +/xmax 0 def +/xmin 0 def +/setupFunEval +{ + begin + /nRange Range length 2 idiv store + /mulRange + + [ + 0 1 nRange 1 sub + { + 2 mul/nDim2 xd + Range nDim2 get + Range nDim2 1 add get + 1 index sub + + 255 div + exch + }for + ]store + end +}bd +/FunEval +{ + begin + + nRange mul /val xd + + 0 1 nRange 1 sub + { + dup 2 mul/nDim2 xd + val + add DataSource exch get + mulRange nDim2 get mul + mulRange nDim2 1 add get + add + }for + end +}bd +/max +{ + 2 copy lt + {exch pop}{pop}ifelse +}bd +/sh2 +{ + /Coords load aload pop + 3 index 3 index translate + + 3 -1 roll sub + 3 1 roll exch + sub + 2 copy + dup mul exch dup mul add sqrt + dup + scale + atan + + rotate + + /Function load setupFunEval + + + clippath {pathbbox}stopped {0 0 0 0}if newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + currentdict/Extend known + { + /Extend load 0 get + { + 0/Function load FunEval sc + xmin ymin xmin abs ymax ymin sub rectfill + }if + }if + + /Nsteps/Function load/Size get 0 get 1 sub store + /dx 1 Nsteps div store + gsave + /di ymax ymin sub store + /Function load + + 0 1 Nsteps + { + 1 index FunEval sc + 0 ymin dx di rectfill + dx 0 translate + }for + pop + grestore + currentdict/Extend known + { + /Extend load 1 get + { + Nsteps/Function load FunEval sc + 1 ymin xmax 1 sub abs ymax ymin sub rectfill + }if + }if +}bd +/shp +{ + 4 copy + + dup 0 gt{ + 0 exch a1 a0 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a0 a1 arcn + }{ + pop 0 lineto + }ifelse + + fill + + dup 0 gt{ + 0 exch a0 a1 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a1 a0 arcn + }{ + pop 0 lineto + }ifelse + + fill +}bd +/calcmaxs +{ + + xmin dup mul ymin dup mul add sqrt + xmax dup mul ymin dup mul add sqrt + xmin dup mul ymax dup mul add sqrt + xmax dup mul ymax dup mul add sqrt + max max max +}bd +/sh3 +{ + /Coords load aload pop + 5 index 5 index translate + 3 -1 roll 6 -1 roll sub + 3 -1 roll 5 -1 roll sub + 2 copy dup mul exch dup mul add sqrt + /dx xs + 2 copy 0 ne exch 0 ne or + { + + exch atan rotate + }{ + pop pop + }ifelse + + /r2 xs + /r1 xs + /Function load + dup/Size get 0 get 1 sub + /Nsteps xs + setupFunEval + + + + + + dx r2 add r1 lt{ + + 0 + }{ + dx r1 add r2 le + { + 1 + }{ + r1 r2 eq + { + 2 + }{ + 3 + }ifelse + }ifelse + }ifelse + /sh3tp xs + clippath {pathbbox}stopped {0 0 0 0}if + newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + + dx dup mul r2 r1 sub dup mul sub dup 0 gt + { + sqrt r2 r1 sub atan + /a0 exch 180 exch sub store + /a1 a0 neg store + }{ + pop + /a0 0 store + /a1 360 store + }ifelse + currentdict/Extend known + { + /Extend load 0 get r1 0 gt and + { + 0/Function load FunEval sc + + + + + { + { + dx 0 r1 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + r1 0 gt{0 0 r1 0 360 arc fill}if + } + { + + + + + 0 r1 xmin abs r1 add neg r1 shp + } + { + + + r2 r1 gt{ + + 0 r1 + r1 neg r2 r1 sub div dx mul + 0 + shp + }{ + + + + 0 r1 calcmaxs + dup + + r2 add dx mul dx r1 r2 sub sub div + neg + exch 1 index + abs exch sub + shp + }ifelse + } + }sh3tp get exec + }if + }if + + /d0 0 store + /r0 r1 store + /di dx Nsteps div store + /ri r2 r1 sub Nsteps div store + /Function load + 0 1 Nsteps + { + 1 index FunEval sc + d0 di add r0 ri add d0 r0 shp + { + + d0 0 r0 a1 a0 arc + d0 di add 0 r0 ri add a0 a1 arcn + fill + + + d0 0 r0 a0 a1 arc + d0 di add 0 r0 ri add a1 a0 arcn + fill + }pop + + + /d0 d0 di add store + /r0 r0 ri add store + }for + pop + + currentdict/Extend known + { + /Extend load 1 get r2 0 gt and + { + Nsteps/Function load FunEval sc + + + + + { + { + dx 0 r2 0 360 arc fill + } + { + dx 0 r2 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + + + xmax abs r1 add r1 dx r1 shp + } + { + + r2 r1 gt{ + + + + calcmaxs dup + + r1 add dx mul dx r2 r1 sub sub div + exch 1 index + exch sub + dx r2 + shp + }{ + + r1 neg r2 r1 sub div dx mul + 0 + dx + r2 + shp + }ifelse + } + } + sh3tp get exec + }if + }if +}bd +/sh +{ + begin + /ShadingType load dup dup 2 eq exch 3 eq or + { + gsave + newpath + /ColorSpace load scs + currentdict/BBox known + { + /BBox load aload pop + 2 index sub + 3 index + 3 -1 roll exch sub + exch rectclip + }if + 2 eq + {sh2}{sh3}ifelse + grestore + }{ + + pop + (DEBUG: shading type unimplemented\n)print flush + }ifelse + end +}bd +{restore}if not dup{save exch}if + L3?{ + /sh/shfill ld + /csq/clipsave ld + /csQ/cliprestore ld + }if +{restore}if +end +setpacking +%%EndFile +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%PageBoundingBox: 0 0 608 160 +%%BeginPageSetup +cg_md begin +bp +sdmtx +[ /CIEBasedABC 4 dict dup begin +/WhitePoint [ 0.9505 1.0000 1.0891 ] def +/DecodeABC [ +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind +] def +/MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def +/RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def +end ] /Cs1 exch/ColorSpace dr pop +%%EndPageSetup +0.60000002 i +/Cs1 SC +0.10588235 0.72549021 0.12156863 sc +q +0 44 m +160 44 l +160 15.003872 l +160 6.7174625 153.27803 0 145.00154 0 c +14.998466 0 l +6.7150416 0 0 6.7065849 0 15.003872 c +0 44 l +h +0 44 m +160 44 l +160 14 l +0 14 l +0 44 l +h +0 44 m +W* +0 0 608 160 rc +-5 49 m +165 49 l +165 -5 l +-5 -5 l +h +f +Q +0.23529412 0.23529412 0.23529412 sc +q +83 90 m +83 160 l +77 160 l +77 14 l +83 14 l +83 84 l +160 84 l +160 90 l +83 90 l +h +0 144.99352 m +0 153.28137 6.7219648 160 14.998466 160 c +145.00154 160 l +153.28496 160 160 153.27509 160 144.99352 c +160 14 l +0 14 l +0 144.99352 l +h +0 144.99352 m +W* +0 0 608 160 rc +-5 165 m +165 165 l +165 9 l +-5 9 l +h +f +Q +0.10588235 0.72549021 0.12156863 sc +q +241.77284 12.165432 m +230.85426 12.165432 222.63789 15.032893 217.12346 20.7679 c +211.60902 26.502909 208.85185 34.995007 208.85185 46.244446 c +208.85185 98.024689 l +189 98.024689 l +189 112.91358 l +208.85185 112.91358 l +208.85185 146 l +225.39507 146 l +225.39507 112.91358 l +261.79013 112.91358 l +261.79013 98.024689 l +225.39507 98.024689 l +225.39507 46.079014 l +225.39507 39.571983 226.99422 34.802074 230.1926 31.769136 c +233.39096 28.736198 237.58186 27.219753 242.76543 27.219753 c +245.19179 27.219753 247.89381 27.49547 250.87161 28.046913 c +253.8494 28.598356 256.88229 29.425508 259.97037 30.528395 c +263.27902 15.97037 l +259.52921 14.646907 255.86215 13.681896 252.27777 13.075309 c +248.69341 12.468721 245.19179 12.165432 241.77284 12.165432 c +241.77284 12.165432 l +h +294.70239 13.654321 m +278.15918 13.654321 l +278.15918 112.91358 l +294.70239 112.91358 l +294.70239 96.370369 l +294.70239 96.370369 304.76617 106.29628 309.67401 109.60493 c +314.58185 112.9136 320.34436 114.5679 326.96167 114.5679 c +332.80695 114.5679 337.79745 112.94117 341.93326 109.68765 c +346.06909 106.43414 349.01926 101.99509 350.78387 96.370369 c +350.78387 96.370369 361.92294 106.29628 366.99622 109.60493 c +372.06952 112.9136 378.08014 114.5679 385.02832 114.5679 c +392.74854 114.5679 398.92459 111.72801 403.55673 106.04815 c +408.18884 100.36829 410.50488 92.730911 410.50488 83.135803 c +410.50488 13.654321 l +393.96167 13.654321 l +393.96167 80.488892 l +393.96167 87.106209 392.69336 91.820976 390.15674 94.633331 c +387.62009 97.445694 383.98062 98.851852 379.23822 98.851852 c +374.49579 98.851852 369.78104 97.418121 365.09375 94.550621 c +360.40649 91.683113 356.24316 87.878212 352.60364 83.135803 c +352.60364 13.654321 l +336.06042 13.654321 l +336.06042 80.488892 l +336.06042 87.106209 334.79211 91.820976 332.25549 94.633331 c +329.71884 97.445694 326.07938 98.851852 321.33698 98.851852 c +316.59457 98.851852 311.87979 97.418121 307.19254 94.550621 c +302.50525 91.683113 298.34192 87.878212 294.70239 83.135803 c +294.70239 13.654321 l +h +438.9505 21.264198 m +433.10519 27.440361 430.18259 35.656738 430.18259 45.913582 c +430.18259 112.91358 l +446.7258 112.91358 l +446.7258 49.222221 l +446.7258 42.16375 448.3801 36.814835 451.68875 33.175308 c +454.99741 29.535784 459.85004 27.716049 466.2468 27.716049 c +471.32007 27.716049 476.36569 29.177351 481.38382 32.099998 c +486.40195 35.022648 491.39243 39.35141 496.35544 45.086418 c +496.35544 112.91358 l +512.89862 112.91358 l +512.89862 13.654321 l +496.35544 13.654321 l +496.35544 30.197531 l +496.35544 30.197531 485.13364 20.271622 479.89493 16.962963 c +474.65622 13.654305 468.78345 12 462.2764 12 c +452.57101 12 444.79578 15.088035 438.9505 21.264198 c +h +588.65784 13.654321 m +564.50476 51.041977 l +541.84052 13.654321 l +523.4776 13.654321 l +556.56403 63.449383 l +524.63562 112.91358 l +543.164 112.91358 l +566.15906 77.180244 l +589.48499 112.91358 l +607.84796 112.91358 l +574.43066 64.441978 l +607.18622 13.654321 l +588.65784 13.654321 l +h +588.65784 13.654321 m +W* +0 0 608 160 rc +184 151 m +612.84796 151 l +612.84796 7 l +184 7 l +h +f +ep +end +%%Trailer +%%EOF diff --git a/logo/tmux-logo.svg b/logo/tmux-logo.svg new file mode 100644 index 00000000..061cddd9 --- /dev/null +++ b/logo/tmux-logo.svg @@ -0,0 +1,18 @@ + + + + logomark + wordmark + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/logo/tmux-logomark.eps b/logo/tmux-logomark.eps new file mode 100644 index 00000000..8924983b --- /dev/null +++ b/logo/tmux-logomark.eps @@ -0,0 +1,829 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%APL_DSC_Encoding: UTF8 +%APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) +%%Title: (Unknown) +%%Creator: (Unknown) +%%CreationDate: (Unknown) +%%For: (Unknown) +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 0 0 160 160 +%%EndComments +%%BeginProlog +%%BeginFile: cg-pdf.ps +%%Copyright: Copyright 2000-2004 Apple Computer Incorporated. +%%Copyright: All Rights Reserved. +currentpacking true setpacking +/cg_md 141 dict def +cg_md begin +/L3? languagelevel 3 ge def +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/xd{exch def}bd +/cmmtx matrix def +mark +/sc/setcolor +/scs/setcolorspace +/dr/defineresource +/fr/findresource +/T/true +/F/false +/d/setdash +/w/setlinewidth +/J/setlinecap +/j/setlinejoin +/M/setmiterlimit +/i/setflat +/rc/rectclip +/rf/rectfill +/rs/rectstroke +/f/fill +/f*/eofill +/sf/selectfont +/s/show +/xS/xshow +/yS/yshow +/xyS/xyshow +/S/stroke +/m/moveto +/l/lineto +/c/curveto +/h/closepath +/n/newpath +/q/gsave +/Q/grestore +counttomark 2 idiv +{ld}repeat pop +/SC{ + /ColorSpace fr scs +}bd +/sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld +/soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld +/cgmtx matrix def +/sdmtx{cgmtx currentmatrix pop}bd +/CM {cgmtx setmatrix}bd +/cm {cmmtx astore CM concat}bd +/W{clip newpath}bd +/W*{eoclip newpath}bd +statusdict begin product end dup (HP) anchorsearch{ + pop pop pop + true +}{ + pop + (hp) anchorsearch{ + pop pop true + }{ + pop false + }ifelse +}ifelse +{ + { + { + pop pop + (0)dup 0 4 -1 roll put + F charpath + }cshow + } +}{ + {F charpath} +}ifelse +/cply exch bd +/cps {cply stroke}bd +/pgsave 0 def +/bp{/pgsave save store}bd +/ep{pgsave restore showpage}def +/re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd +/scrdict 10 dict def +/scrmtx matrix def +/patarray 0 def +/createpat{patarray 3 1 roll put}bd +/makepat{ +scrmtx astore pop +gsave +initgraphics +CM +patarray exch get +scrmtx +makepattern +grestore +setpattern +}bd +/cg_BeginEPSF{ + userdict save/cg_b4_Inc_state exch put + userdict/cg_endepsf/cg_EndEPSF load put + count userdict/cg_op_count 3 -1 roll put + countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put + 3 sub{end}repeat + /showpage {} def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash newpath + false setstrokeadjust false setoverprint +}bd +/cg_EndEPSF{ + countdictstack 3 sub { end } repeat + cg_dict_array 3 1 index length 3 sub getinterval + {begin}forall + count userdict/cg_op_count get sub{pop}repeat + userdict/cg_b4_Inc_state get restore + F setpacking +}bd +/cg_biproc{currentfile/RunLengthDecode filter}bd +/cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd +/ImageDataSource 0 def +L3?{ + /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd + /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd +}{ + /ImageBandMask 0 def + /ImageBandData 0 def + /cg_mibiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd + /cg_miaiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter + dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd +}ifelse +/imsave 0 def +/BI{save/imsave xd mark}bd +/EI{imsave restore}bd +/ID{ +counttomark 2 idiv +dup 2 add +dict begin +{def} repeat +pop +/ImageType 1 def +/ImageMatrix[Width 0 0 Height neg 0 Height]def +currentdict dup/ImageMask known{ImageMask}{F}ifelse exch +L3?{ + dup/MaskedImage known + { + pop + << + /ImageType 3 + /InterleaveType 2 + /DataDict currentdict + /MaskDict + << /ImageType 1 + /Width Width + /Height Height + /ImageMatrix ImageMatrix + /BitsPerComponent 1 + /Decode [0 1] + currentdict/Interpolate known + {/Interpolate Interpolate}if + >> + >> + }if +}if +exch +{imagemask}{image}ifelse +end +}bd +/cguidfix{statusdict begin mark version end +{cvr}stopped{cleartomark 0}{exch pop}ifelse +2012 lt{dup findfont dup length dict begin +{1 index/FID ne 2 index/UniqueID ne and +{def} {pop pop} ifelse}forall +currentdict end definefont pop +}{pop}ifelse +}bd +/t_array 0 def +/t_i 0 def +/t_c 1 string def +/x_proc{ + exch t_array t_i get add exch moveto + /t_i t_i 1 add store +}bd +/y_proc{ + t_array t_i get add moveto + /t_i t_i 1 add store +}bd +/xy_proc{ + + t_array t_i 2 copy 1 add get 3 1 roll get + 4 -1 roll add 3 1 roll add moveto + /t_i t_i 2 add store +}bd +/sop 0 def +/cp_proc/x_proc ld +/base_charpath +{ + /t_array xs + /t_i 0 def + { + t_c 0 3 -1 roll put + currentpoint + t_c cply sop + cp_proc + }forall + /t_array 0 def +}bd +/sop/stroke ld +/nop{}def +/xsp/base_charpath ld +/ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd +/xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd +/xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd +/ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd +/xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd +/refnt{ +findfont dup length dict copy dup +/Encoding 4 -1 roll put +definefont pop +}bd +/renmfont{ +findfont dup length dict copy definefont pop +}bd +L3? dup dup{save exch}if +/Range 0 def +/DataSource 0 def +/val 0 def +/nRange 0 def +/mulRange 0 def +/d0 0 def +/r0 0 def +/di 0 def +/ri 0 def +/a0 0 def +/a1 0 def +/r1 0 def +/r2 0 def +/dx 0 def +/Nsteps 0 def +/sh3tp 0 def +/ymax 0 def +/ymin 0 def +/xmax 0 def +/xmin 0 def +/setupFunEval +{ + begin + /nRange Range length 2 idiv store + /mulRange + + [ + 0 1 nRange 1 sub + { + 2 mul/nDim2 xd + Range nDim2 get + Range nDim2 1 add get + 1 index sub + + 255 div + exch + }for + ]store + end +}bd +/FunEval +{ + begin + + nRange mul /val xd + + 0 1 nRange 1 sub + { + dup 2 mul/nDim2 xd + val + add DataSource exch get + mulRange nDim2 get mul + mulRange nDim2 1 add get + add + }for + end +}bd +/max +{ + 2 copy lt + {exch pop}{pop}ifelse +}bd +/sh2 +{ + /Coords load aload pop + 3 index 3 index translate + + 3 -1 roll sub + 3 1 roll exch + sub + 2 copy + dup mul exch dup mul add sqrt + dup + scale + atan + + rotate + + /Function load setupFunEval + + + clippath {pathbbox}stopped {0 0 0 0}if newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + currentdict/Extend known + { + /Extend load 0 get + { + 0/Function load FunEval sc + xmin ymin xmin abs ymax ymin sub rectfill + }if + }if + + /Nsteps/Function load/Size get 0 get 1 sub store + /dx 1 Nsteps div store + gsave + /di ymax ymin sub store + /Function load + + 0 1 Nsteps + { + 1 index FunEval sc + 0 ymin dx di rectfill + dx 0 translate + }for + pop + grestore + currentdict/Extend known + { + /Extend load 1 get + { + Nsteps/Function load FunEval sc + 1 ymin xmax 1 sub abs ymax ymin sub rectfill + }if + }if +}bd +/shp +{ + 4 copy + + dup 0 gt{ + 0 exch a1 a0 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a0 a1 arcn + }{ + pop 0 lineto + }ifelse + + fill + + dup 0 gt{ + 0 exch a0 a1 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a1 a0 arcn + }{ + pop 0 lineto + }ifelse + + fill +}bd +/calcmaxs +{ + + xmin dup mul ymin dup mul add sqrt + xmax dup mul ymin dup mul add sqrt + xmin dup mul ymax dup mul add sqrt + xmax dup mul ymax dup mul add sqrt + max max max +}bd +/sh3 +{ + /Coords load aload pop + 5 index 5 index translate + 3 -1 roll 6 -1 roll sub + 3 -1 roll 5 -1 roll sub + 2 copy dup mul exch dup mul add sqrt + /dx xs + 2 copy 0 ne exch 0 ne or + { + + exch atan rotate + }{ + pop pop + }ifelse + + /r2 xs + /r1 xs + /Function load + dup/Size get 0 get 1 sub + /Nsteps xs + setupFunEval + + + + + + dx r2 add r1 lt{ + + 0 + }{ + dx r1 add r2 le + { + 1 + }{ + r1 r2 eq + { + 2 + }{ + 3 + }ifelse + }ifelse + }ifelse + /sh3tp xs + clippath {pathbbox}stopped {0 0 0 0}if + newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + + dx dup mul r2 r1 sub dup mul sub dup 0 gt + { + sqrt r2 r1 sub atan + /a0 exch 180 exch sub store + /a1 a0 neg store + }{ + pop + /a0 0 store + /a1 360 store + }ifelse + currentdict/Extend known + { + /Extend load 0 get r1 0 gt and + { + 0/Function load FunEval sc + + + + + { + { + dx 0 r1 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + r1 0 gt{0 0 r1 0 360 arc fill}if + } + { + + + + + 0 r1 xmin abs r1 add neg r1 shp + } + { + + + r2 r1 gt{ + + 0 r1 + r1 neg r2 r1 sub div dx mul + 0 + shp + }{ + + + + 0 r1 calcmaxs + dup + + r2 add dx mul dx r1 r2 sub sub div + neg + exch 1 index + abs exch sub + shp + }ifelse + } + }sh3tp get exec + }if + }if + + /d0 0 store + /r0 r1 store + /di dx Nsteps div store + /ri r2 r1 sub Nsteps div store + /Function load + 0 1 Nsteps + { + 1 index FunEval sc + d0 di add r0 ri add d0 r0 shp + { + + d0 0 r0 a1 a0 arc + d0 di add 0 r0 ri add a0 a1 arcn + fill + + + d0 0 r0 a0 a1 arc + d0 di add 0 r0 ri add a1 a0 arcn + fill + }pop + + + /d0 d0 di add store + /r0 r0 ri add store + }for + pop + + currentdict/Extend known + { + /Extend load 1 get r2 0 gt and + { + Nsteps/Function load FunEval sc + + + + + { + { + dx 0 r2 0 360 arc fill + } + { + dx 0 r2 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + + + xmax abs r1 add r1 dx r1 shp + } + { + + r2 r1 gt{ + + + + calcmaxs dup + + r1 add dx mul dx r2 r1 sub sub div + exch 1 index + exch sub + dx r2 + shp + }{ + + r1 neg r2 r1 sub div dx mul + 0 + dx + r2 + shp + }ifelse + } + } + sh3tp get exec + }if + }if +}bd +/sh +{ + begin + /ShadingType load dup dup 2 eq exch 3 eq or + { + gsave + newpath + /ColorSpace load scs + currentdict/BBox known + { + /BBox load aload pop + 2 index sub + 3 index + 3 -1 roll exch sub + exch rectclip + }if + 2 eq + {sh2}{sh3}ifelse + grestore + }{ + + pop + (DEBUG: shading type unimplemented\n)print flush + }ifelse + end +}bd +{restore}if not dup{save exch}if + L3?{ + /sh/shfill ld + /csq/clipsave ld + /csQ/cliprestore ld + }if +{restore}if +end +setpacking +%%EndFile +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%PageBoundingBox: 0 0 160 160 +%%BeginPageSetup +cg_md begin +bp +sdmtx +[ /CIEBasedABC 4 dict dup begin +/WhitePoint [ 0.9505 1.0000 1.0891 ] def +/DecodeABC [ +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind +] def +/MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def +/RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def +end ] /Cs1 exch/ColorSpace dr pop +%%EndPageSetup +0.60000002 i +/Cs1 SC +0.10588235 0.72549021 0.12156863 sc +q +0 44 m +160 44 l +160 15.003872 l +160 6.7174625 153.27803 0 145.00154 0 c +14.998466 0 l +6.7150416 0 0 6.7065849 0 15.003872 c +0 44 l +h +0 44 m +160 44 l +160 14 l +0 14 l +0 44 l +h +0 44 m +W* +0 0 160 160 rc +-5 49 m +165 49 l +165 -5 l +-5 -5 l +h +f +Q +0.23529412 0.23529412 0.23529412 sc +q +83 90 m +83 160 l +77 160 l +77 14 l +83 14 l +83 84 l +160 84 l +160 90 l +83 90 l +h +0 144.99352 m +0 153.28137 6.7219648 160 14.998466 160 c +145.00154 160 l +153.28496 160 160 153.27509 160 144.99352 c +160 14 l +0 14 l +0 144.99352 l +h +0 144.99352 m +W* +0 0 160 160 rc +-5 165 m +165 165 l +165 9 l +-5 9 l +h +f +ep +end +%%Trailer +%%EOF diff --git a/logo/tmux-logomark.svg b/logo/tmux-logomark.svg new file mode 100644 index 00000000..c543709d --- /dev/null +++ b/logo/tmux-logomark.svg @@ -0,0 +1,15 @@ + + + + logomark copy + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/mdoc2man.awk b/mdoc2man.awk new file mode 100644 index 00000000..80e8d5ff --- /dev/null +++ b/mdoc2man.awk @@ -0,0 +1,370 @@ +#!/usr/bin/awk +# +# $Id: mdoc2man.awk,v 1.9 2009/10/24 00:52:42 dtucker Exp $ +# +# Version history: +# v4+ Adapted for OpenSSH Portable (see cvs Id and history) +# v3, I put the program under a proper license +# Dan Nelson added .An, .Aq and fixed a typo +# v2, fixed to work on GNU awk --posix and MacOS X +# v1, first attempt, didn't work on MacOS X +# +# Copyright (c) 2003 Peter Stuge +# +# 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. + + +BEGIN { + optlist=0 + oldoptlist=0 + nospace=0 + synopsis=0 + reference=0 + block=0 + ext=0 + extopt=0 + literal=0 + prenl=0 + breakw=0 + line="" +} + +function wtail() { + retval="" + while(w0;i--) { + add(refauthors[i]) + if(i>1) + add(", ") + } + if(nrefauthors>1) + add(" and ") + if(nrefauthors>0) + add(refauthors[0] ", ") + add("\\fI" reftitle "\\fP") + if(length(refissue)) + add(", " refissue) + if(length(refreport)) { + add(", " refreport) + } + if(length(refdate)) + add(", " refdate) + if(length(refopt)) + add(", " refopt) + add(".") + reference=0 + } else if(reference) { + if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() } + if(match(words[w],"^%T$")) { + reftitle=wtail() + sub("^\"","",reftitle) + sub("\"$","",reftitle) + } + if(match(words[w],"^%N$")) { refissue=wtail() } + if(match(words[w],"^%D$")) { refdate=wtail() } + if(match(words[w],"^%O$")) { refopt=wtail() } + if(match(words[w],"^%R$")) { refreport=wtail() } + } else if(match(words[w],"^Nm$")) { + if(synopsis) { + add(".br") + prenl++ + } + n=words[++w] + if(!length(name)) + name=n + if(!length(n)) + n=name + add("\\fB" n "\\fP") + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Nd$")) { + add("\\- " wtail()) + } else if(match(words[w],"^Fl$")) { + add("\\fB\\-" words[++w] "\\fP") + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Ar$")) { + add("\\fI") + if(w==nwords) + add("file ...\\fP") + else { + add(words[++w] "\\fP") + while(match(words[w+1],"^\\|$")) + add(OFS words[++w] " \\fI" words[++w] "\\fP") + } + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Cm$")) { + add("\\fB" words[++w] "\\fP") + while(w") + if(option) + add("]") + if(ext&&!extopt&&!match(line," $")) + add(OFS) + if(!ext&&!extopt&&length(line)) { + print line + prenl=0 + line="" + } +} diff --git a/mode-key.c b/mode-key.c index 94115ebb..a47cda0b 100644 --- a/mode-key.c +++ b/mode-key.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -35,11 +35,23 @@ * * vi command mode is handled by having a mode flag in the struct which allows * two sets of bindings to be swapped between. A couple of editing commands - * (MODEKEYEDIT_SWITCHMODE, MODEKEYEDIT_SWITCHMODEAPPEND, - * MODEKEYEDIT_SWITCHMODEAPPENDLINE, and MODEKEYEDIT_SWITCHMODEBEGINLINE) - * are special-cased to do this. + * (any matching MODEKEYEDIT_SWITCHMODE*) are special-cased to do this. */ +/* Entry in the default mode key tables. */ +struct mode_key_entry { + key_code key; + + /* + * Editing mode for vi: 0 is edit mode, keys not in the table are + * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table + * are returned as MODEKEY_NONE. This is also matched on, allowing some + * keys to be bound in edit mode. + */ + int mode; + enum mode_key_cmd cmd; +}; + /* Edit keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_BACKSPACE, "backspace" }, @@ -67,6 +79,9 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" }, { MODEKEYEDIT_SWITCHMODEAPPENDLINE, "switch-mode-append-line" }, { MODEKEYEDIT_SWITCHMODEBEGINLINE, "switch-mode-begin-line" }, + { MODEKEYEDIT_SWITCHMODECHANGELINE, "switch-mode-change-line" }, + { MODEKEYEDIT_SWITCHMODESUBSTITUTE, "switch-mode-substitute" }, + { MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, "switch-mode-substitute-line" }, { MODEKEYEDIT_TRANSPOSECHARS, "transpose-chars" }, { 0, NULL } @@ -75,14 +90,18 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { /* Choice keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { { MODEKEYCHOICE_BACKSPACE, "backspace" }, + { MODEKEYCHOICE_BOTTOMLINE, "bottom-line"}, { MODEKEYCHOICE_CANCEL, "cancel" }, { MODEKEYCHOICE_CHOOSE, "choose" }, { MODEKEYCHOICE_DOWN, "down" }, + { MODEKEYCHOICE_ENDOFLIST, "end-of-list"}, { MODEKEYCHOICE_PAGEDOWN, "page-down" }, { MODEKEYCHOICE_PAGEUP, "page-up" }, { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" }, { MODEKEYCHOICE_SCROLLUP, "scroll-up" }, { MODEKEYCHOICE_STARTNUMBERPREFIX, "start-number-prefix" }, + { MODEKEYCHOICE_STARTOFLIST, "start-of-list"}, + { MODEKEYCHOICE_TOPLINE, "top-line"}, { MODEKEYCHOICE_TREE_COLLAPSE, "tree-collapse" }, { MODEKEYCHOICE_TREE_COLLAPSE_ALL, "tree-collapse-all" }, { MODEKEYCHOICE_TREE_EXPAND, "tree-expand" }, @@ -95,6 +114,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { /* Copy keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { + { MODEKEYCOPY_APPENDSELECTION, "append-selection" }, { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" }, { MODEKEYCOPY_BOTTOMLINE, "bottom-line" }, { MODEKEYCOPY_CANCEL, "cancel" }, @@ -124,6 +144,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_NEXTSPACEEND, "next-space-end" }, { MODEKEYCOPY_NEXTWORD, "next-word" }, { MODEKEYCOPY_NEXTWORDEND, "next-word-end" }, + { MODEKEYCOPY_OTHEREND, "other-end" }, { MODEKEYCOPY_PREVIOUSPAGE, "page-up" }, { MODEKEYCOPY_PREVIOUSSPACE, "previous-space" }, { MODEKEYCOPY_PREVIOUSWORD, "previous-word" }, @@ -135,6 +156,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" }, { MODEKEYCOPY_SEARCHUP, "search-backward" }, { MODEKEYCOPY_SELECTLINE, "select-line" }, + { MODEKEYCOPY_STARTNAMEDBUFFER, "start-named-buffer" }, { MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" }, { MODEKEYCOPY_STARTOFLINE, "start-of-line" }, { MODEKEYCOPY_STARTSELECTION, "begin-selection" }, @@ -152,6 +174,7 @@ const struct mode_key_entry mode_key_vi_edit[] = { { '\025' /* C-u */, 0, MODEKEYEDIT_DELETELINE }, { '\027' /* C-w */, 0, MODEKEYEDIT_DELETEWORD }, { '\033' /* Escape */, 0, MODEKEYEDIT_SWITCHMODE }, + { '\n', 0, MODEKEYEDIT_ENTER }, { '\r', 0, MODEKEYEDIT_ENTER }, { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE }, { KEYC_DC, 0, MODEKEYEDIT_DELETE }, @@ -166,13 +189,16 @@ const struct mode_key_entry mode_key_vi_edit[] = { { '0', 1, MODEKEYEDIT_STARTOFLINE }, { 'A', 1, MODEKEYEDIT_SWITCHMODEAPPENDLINE }, { 'B', 1, MODEKEYEDIT_PREVIOUSSPACE }, + { 'C', 1, MODEKEYEDIT_SWITCHMODECHANGELINE }, { 'D', 1, MODEKEYEDIT_DELETETOENDOFLINE }, { 'E', 1, MODEKEYEDIT_NEXTSPACEEND }, { 'I', 1, MODEKEYEDIT_SWITCHMODEBEGINLINE }, + { 'S', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTELINE }, { 'W', 1, MODEKEYEDIT_NEXTSPACE }, { 'X', 1, MODEKEYEDIT_BACKSPACE }, { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL }, { '\010' /* C-h */, 1, MODEKEYEDIT_BACKSPACE }, + { '\n', 1, MODEKEYEDIT_ENTER }, { '\r', 1, MODEKEYEDIT_ENTER }, { '^', 1, MODEKEYEDIT_STARTOFLINE }, { 'a', 1, MODEKEYEDIT_SWITCHMODEAPPEND }, @@ -185,6 +211,7 @@ const struct mode_key_entry mode_key_vi_edit[] = { { 'k', 1, MODEKEYEDIT_HISTORYUP }, { 'l', 1, MODEKEYEDIT_CURSORRIGHT }, { 'p', 1, MODEKEYEDIT_PASTE }, + { 's', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTE }, { 'w', 1, MODEKEYEDIT_NEXTWORD }, { 'x', 1, MODEKEYEDIT_DELETE }, { KEYC_BSPACE, 1, MODEKEYEDIT_BACKSPACE }, @@ -215,10 +242,17 @@ const struct mode_key_entry mode_key_vi_choice[] = { { '\005' /* C-e */, 0, MODEKEYCHOICE_SCROLLDOWN }, { '\006' /* C-f */, 0, MODEKEYCHOICE_PAGEDOWN }, { '\031' /* C-y */, 0, MODEKEYCHOICE_SCROLLUP }, + { '\n', 0, MODEKEYCHOICE_CHOOSE }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'j', 0, MODEKEYCHOICE_DOWN }, { 'k', 0, MODEKEYCHOICE_UP }, { 'q', 0, MODEKEYCHOICE_CANCEL }, + { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, + { 'g', 0, MODEKEYCHOICE_STARTOFLIST }, + { 'H', 0, MODEKEYCHOICE_TOPLINE }, + { 'L', 0, MODEKEYCHOICE_BOTTOMLINE }, + { 'G', 0, MODEKEYCHOICE_ENDOFLIST }, + { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, @@ -227,10 +261,14 @@ const struct mode_key_entry mode_key_vi_choice[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, + { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, + { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, + { KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE }, + { KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN }, { 0, -1, 0 } }; @@ -239,6 +277,7 @@ struct mode_key_tree mode_key_tree_vi_choice; /* vi copy mode keys. */ const struct mode_key_entry mode_key_vi_copy[] = { { ' ', 0, MODEKEYCOPY_STARTSELECTION }, + { '"', 0, MODEKEYCOPY_STARTNAMEDBUFFER }, { '$', 0, MODEKEYCOPY_ENDOFLINE }, { ',', 0, MODEKEYCOPY_JUMPREVERSE }, { ';', 0, MODEKEYCOPY_JUMPAGAIN }, @@ -255,6 +294,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { '9', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { ':', 0, MODEKEYCOPY_GOTOLINE }, { '?', 0, MODEKEYCOPY_SEARCHUP }, + { 'A', 0, MODEKEYCOPY_APPENDSELECTION }, { 'B', 0, MODEKEYCOPY_PREVIOUSSPACE }, { 'D', 0, MODEKEYCOPY_COPYENDOFLINE }, { 'E', 0, MODEKEYCOPY_NEXTSPACEEND }, @@ -267,6 +307,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { 'M', 0, MODEKEYCOPY_MIDDLELINE }, { 'N', 0, MODEKEYCOPY_SEARCHREVERSE }, { 'T', 0, MODEKEYCOPY_JUMPTOBACK }, + { 'V', 0, MODEKEYCOPY_SELECTLINE }, { 'W', 0, MODEKEYCOPY_NEXTSPACE }, { '\002' /* C-b */, 0, MODEKEYCOPY_PREVIOUSPAGE }, { '\003' /* C-c */, 0, MODEKEYCOPY_CANCEL }, @@ -277,6 +318,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { '\025' /* C-u */, 0, MODEKEYCOPY_HALFPAGEUP }, { '\031' /* C-y */, 0, MODEKEYCOPY_SCROLLUP }, { '\033' /* Escape */, 0, MODEKEYCOPY_CLEARSELECTION }, + { '\n', 0, MODEKEYCOPY_COPYSELECTION }, { '\r', 0, MODEKEYCOPY_COPYSELECTION }, { '^', 0, MODEKEYCOPY_BACKTOINDENTATION }, { 'b', 0, MODEKEYCOPY_PREVIOUSWORD }, @@ -288,6 +330,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { 'k', 0, MODEKEYCOPY_UP }, { 'l', 0, MODEKEYCOPY_RIGHT }, { 'n', 0, MODEKEYCOPY_SEARCHAGAIN }, + { 'o', 0, MODEKEYCOPY_OTHEREND }, { 't', 0, MODEKEYCOPY_JUMPTO }, { 'q', 0, MODEKEYCOPY_CANCEL }, { 'v', 0, MODEKEYCOPY_RECTANGLETOGGLE }, @@ -301,6 +344,9 @@ const struct mode_key_entry mode_key_vi_copy[] = { { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT }, { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_UP, 0, MODEKEYCOPY_UP }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, + { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, { 0, -1, 0 } }; @@ -324,6 +370,7 @@ const struct mode_key_entry mode_key_emacs_edit[] = { { '\027' /* C-w */, 0, MODEKEYEDIT_DELETEWORD }, { '\031' /* C-y */, 0, MODEKEYEDIT_PASTE }, { '\033' /* Escape */, 0, MODEKEYEDIT_CANCEL }, + { '\n', 0, MODEKEYEDIT_ENTER }, { '\r', 0, MODEKEYEDIT_ENTER }, { 'b' | KEYC_ESCAPE, 0, MODEKEYEDIT_PREVIOUSWORD }, { 'f' | KEYC_ESCAPE, 0, MODEKEYEDIT_NEXTWORDEND }, @@ -358,9 +405,15 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { '\020' /* C-p */, 0, MODEKEYCHOICE_UP }, { '\026' /* C-v */, 0, MODEKEYCHOICE_PAGEDOWN }, { '\033' /* Escape */, 0, MODEKEYCHOICE_CANCEL }, + { '\n', 0, MODEKEYCHOICE_CHOOSE }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCHOICE_PAGEUP }, + { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, + { '<' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTOFLIST }, + { 'R' | KEYC_ESCAPE, 0, MODEKEYCHOICE_TOPLINE }, + { '>' | KEYC_ESCAPE, 0, MODEKEYCHOICE_ENDOFLIST }, + { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, @@ -369,10 +422,14 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, + { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, + { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, + { KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE }, + { KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN }, { 0, -1, 0 } }; @@ -435,6 +492,9 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_UP | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEUP }, { KEYC_UP, 0, MODEKEYCOPY_UP }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, + { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, { 0, -1, 0 } }; @@ -463,9 +523,15 @@ RB_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); int mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2) { - if (mbind1->mode != mbind2->mode) - return (mbind1->mode - mbind2->mode); - return (mbind1->key - mbind2->key); + if (mbind1->mode < mbind2->mode) + return (-1); + if (mbind1->mode > mbind2->mode) + return (1); + if (mbind1->key < mbind2->key) + return (-1); + if (mbind1->key > mbind2->key) + return (1); + return (0); } const char * @@ -528,7 +594,7 @@ mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree) } enum mode_key_cmd -mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) +mode_key_lookup(struct mode_key_data *mdata, key_code key, const char **arg) { struct mode_key_binding *mbind, mtmp; @@ -545,6 +611,9 @@ mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) case MODEKEYEDIT_SWITCHMODEAPPEND: case MODEKEYEDIT_SWITCHMODEAPPENDLINE: case MODEKEYEDIT_SWITCHMODEBEGINLINE: + case MODEKEYEDIT_SWITCHMODECHANGELINE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: mdata->mode = 1 - mdata->mode; /* FALLTHROUGH */ default: diff --git a/names.c b/names.c index f536d2fc..3c25e215 100644 --- a/names.c +++ b/names.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -22,84 +22,111 @@ #include #include #include -#include #include "tmux.h" -void window_name_callback(unused int, unused short, void *); -char *parse_window_name(const char *); +void name_time_callback(int, short, void *); +int name_time_expired(struct window *, struct timeval *); void -queue_window_name(struct window *w) +name_time_callback(__unused int fd, __unused short events, void *arg) { - struct timeval tv; + struct window *w = arg; - tv.tv_sec = 0; - tv.tv_usec = NAME_INTERVAL * 1000L; + /* The event loop will call check_window_name for us on the way out. */ + log_debug("@%u name timer expired", w->id); +} - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); - evtimer_set(&w->name_timer, window_name_callback, w); - evtimer_add(&w->name_timer, &tv); +int +name_time_expired(struct window *w, struct timeval *tv) +{ + struct timeval offset; + + timersub(tv, &w->name_time, &offset); + if (offset.tv_sec != 0 || offset.tv_usec > NAME_INTERVAL) + return (0); + return (NAME_INTERVAL - offset.tv_usec); } void -window_name_callback(unused int fd, unused short events, void *data) +check_window_name(struct window *w) { - struct window *w = data; - char *name, *wname; + struct timeval tv, next; + char *name; + int left; if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) { - if (event_initialized(&w->name_timer)) - event_del(&w->name_timer); + if (!options_get_number(w->options, "automatic-rename")) + return; + + if (~w->active->flags & PANE_CHANGED) { + log_debug("@%u active pane not changed", w->id); return; } - queue_window_name(w); + log_debug("@%u active pane changed", w->id); - if (w->active->screen != &w->active->base) - name = NULL; - else - name = osdep_get_name(w->active->fd, w->active->tty); - if (name == NULL) - wname = default_window_name(w); - else { - /* - * If tmux is using the default command, it will be a login - * shell and argv[0] may have a - prefix. Remove this if it is - * present. Ick. - */ - if (w->active->cmd != NULL && *w->active->cmd == '\0' && - name != NULL && name[0] == '-' && name[1] != '\0') - wname = parse_window_name(name + 1); - else - wname = parse_window_name(name); - free(name); + gettimeofday(&tv, NULL); + left = name_time_expired(w, &tv); + if (left != 0) { + if (!event_initialized(&w->name_event)) + evtimer_set(&w->name_event, name_time_callback, w); + if (!evtimer_pending(&w->name_event, NULL)) { + log_debug("@%u name timer queued (%d left)", w->id, left); + timerclear(&next); + next.tv_usec = left; + event_add(&w->name_event, &next); + } else + log_debug("@%u name timer already queued (%d left)", w->id, left); + return; } + memcpy(&w->name_time, &tv, sizeof w->name_time); + if (event_initialized(&w->name_event)) + evtimer_del(&w->name_event); - if (w->active->fd == -1) { - xasprintf(&name, "%s[dead]", wname); - free(wname); - wname = name; - } + w->active->flags &= ~PANE_CHANGED; - if (strcmp(wname, w->name)) { - window_set_name(w, wname); + name = format_window_name(w); + if (strcmp(name, w->name) != 0) { + log_debug("@%u new name %s (was %s)", w->id, name, w->name); + window_set_name(w, name); server_status_window(w); - } - free(wname); + } else + log_debug("@%u name not changed (still %s)", w->id, w->name); + + free(name); } char * default_window_name(struct window *w) { - if (w->active->screen != &w->active->base) - return (xstrdup("[tmux]")); - if (w->active->cmd != NULL && *w->active->cmd != '\0') - return (parse_window_name(w->active->cmd)); - return (parse_window_name(w->active->shell)); + char *cmd, *s; + + cmd = cmd_stringify_argv(w->active->argc, w->active->argv); + if (cmd != NULL && *cmd != '\0') + s = parse_window_name(cmd); + else + s = parse_window_name(w->active->shell); + free(cmd); + return (s); +} + +char * +format_window_name(struct window *w) +{ + struct format_tree *ft; + char *fmt, *name; + + ft = format_create(NULL, 0); + format_defaults_window(ft, w); + format_defaults_pane(ft, w->active); + + fmt = options_get_string(w->options, "automatic-rename-format"); + name = format_expand(ft, fmt); + + format_free(ft); + return (name); } char * @@ -111,7 +138,7 @@ parse_window_name(const char *in) if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; - while (*name == ' ') + while (*name == ' ' || *name == '-') name++; if ((ptr = strchr(name, ' ')) != NULL) *ptr = '\0'; diff --git a/notify.c b/notify.c index bd5c7d83..696a62bf 100644 --- a/notify.c +++ b/notify.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2012 George Nachman @@ -120,9 +120,9 @@ notify_drain(void) } if (ne->client != NULL) - ne->client->references--; + server_client_unref(ne->client); if (ne->session != NULL) - ne->session->references--; + session_unref(ne->session); if (ne->window != NULL) window_remove_ref(ne->window); @@ -135,7 +135,6 @@ void notify_input(struct window_pane *wp, struct evbuffer *input) { struct client *c; - u_int i; /* * notify_input() is not queued and only does anything when @@ -144,9 +143,8 @@ notify_input(struct window_pane *wp, struct evbuffer *input) if (!notify_enabled) return; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && (c->flags & CLIENT_CONTROL)) + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_CONTROL) control_notify_input(c, wp, input); } } diff --git a/options-table.c b/options-table.c index 30c329c0..0a602ecc 100644 --- a/options-table.c +++ b/options-table.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -27,17 +27,14 @@ * options. These tables are the master copy of the options with their real * (user-visible) types, range limits and default values. At start these are * copied into the runtime global options trees (which only has number and - * string types). These tables are then used to loop up the real type when - * the user sets an option or its value needs to be shown. + * string types). These tables are then used to look up the real type when the + * user sets an option or its value needs to be shown. */ /* Choice option type lists. */ const char *options_table_mode_keys_list[] = { "emacs", "vi", NULL }; -const char *options_table_mode_mouse_list[] = { - "off", "on", "copy-mode", NULL -}; const char *options_table_clock_mode_style_list[] = { "12", "24", NULL }; @@ -51,20 +48,32 @@ const char *options_table_status_position_list[] = { "top", "bottom", NULL }; const char *options_table_bell_action_list[] = { - "none", "any", "current", NULL + "none", "any", "current", "other", NULL }; /* Server options. */ -const struct options_table_entry server_options_table[] = { +const struct options_table_entry options_table[] = { { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = INT_MAX, .default_num = 20 }, + { .name = "default-terminal", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, +#ifdef TMATE + .default_str = "screen-256color" +#else + .default_str = "screen" +#endif + }, + { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 500 @@ -72,26 +81,53 @@ const struct options_table_entry server_options_table[] = { { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, + { .name = "focus-events", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, + .default_num = 0 + }, + + { .name = "history-file", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "message-limit", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 100 + }, + { .name = "quiet", .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ + .scope = OPTIONS_TABLE_SERVER, + .default_num = 0 }, { .name = "set-clipboard", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 1 }, - { .name = NULL } -}; + { .name = "terminal-overrides", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" + ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" + ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" + }, -/* Session options. */ -const struct options_table_entry session_options_table[] = { { .name = "assume-paste-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 1, @@ -99,6 +135,7 @@ const struct options_table_entry session_options_table[] = { { .name = "base-index", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -106,57 +143,56 @@ const struct options_table_entry session_options_table[] = { { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, .default_num = BELL_ANY }, { .name = "bell-on-alert", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, - .default_str = "" - }, - - { .name = "default-path", - .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "" }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = _PATH_BSHELL }, - { .name = "default-terminal", - .type = OPTIONS_TABLE_STRING, - .default_str = "screen-256color" - }, - { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 4 }, { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 1000 @@ -164,20 +200,29 @@ const struct options_table_entry session_options_table[] = { { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, - .minimum = 1, + .scope = OPTIONS_TABLE_SESSION, + .minimum = 0, .maximum = INT_MAX, .default_num = 750 }, { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 2000 }, + { .name = "key-table", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "root" + }, + { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -185,108 +230,91 @@ const struct options_table_entry session_options_table[] = { { .name = "lock-command", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "lock -np" }, - { .name = "lock-server", - .type = OPTIONS_TABLE_FLAG, - .default_num = 1 - }, - { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "message-style" }, { .name = "message-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 3 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 3, + .style = "message-style" }, { .name = "message-command-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "message-command-style" }, { .name = "message-command-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "message-command-style" }, { .name = "message-command-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 3 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 3, + .style = "message-command-style" + }, + + { .name = "message-command-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "bg=black,fg=yellow" }, { .name = "message-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "message-style" }, - { .name = "message-limit", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 20 + { .name = "message-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "bg=yellow,fg=black" }, - { .name = "mouse-resize-pane", + { .name = "mouse", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, - { .name = "mouse-select-pane", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "mouse-select-window", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "mouse-utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "pane-active-border-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "pane-active-border-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 2 - }, - - { .name = "pane-border-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "pane-border-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - { .name = "prefix", .type = OPTIONS_TABLE_KEY, + .scope = OPTIONS_TABLE_SESSION, .default_num = '\002', }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, + .scope = OPTIONS_TABLE_SESSION, .default_num = KEYC_NONE, }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "repeat-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 500 @@ -294,41 +322,52 @@ const struct options_table_entry session_options_table[] = { { .name = "set-remain-on-exit", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, - .default_str = "#S:#I:#W - \"#T\"" + .scope = OPTIONS_TABLE_SESSION, + .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "status", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "status-style" }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 2 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 2, + .style = "status-style" }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "status-style" }, { .name = "status-interval", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 15 @@ -336,91 +375,116 @@ const struct options_table_entry session_options_table[] = { { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_justify_list, .default_num = 0 }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_keys_list, .default_num = MODEKEY_EMACS }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, - .default_str = "[#S]" + .scope = OPTIONS_TABLE_SESSION, + .default_str = "[#S] " }, { .name = "status-left-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "status-left-style" }, { .name = "status-left-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 8, + .style = "status-left-style" }, { .name = "status-left-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 8, + .style = "status-left-style" }, { .name = "status-left-length", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 10 }, + { .name = "status-left-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "default" + }, + { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_position_list, .default_num = 1 }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, - .default_str = "\"#22T\" %H:%M %d-%b-%y" + .scope = OPTIONS_TABLE_SESSION, + .default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 0, + .style = "status-right-style" }, { .name = "status-right-bg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 8, + .style = "status-right-style" }, { .name = "status-right-fg", .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 + .scope = OPTIONS_TABLE_SESSION, + .default_num = 8, + .style = "status-right-style" }, { .name = "status-right-length", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 40 }, - { .name = "status-utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ + { .name = "status-right-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "default" }, - { .name = "terminal-overrides", - .type = OPTIONS_TABLE_STRING, - .default_str = "*88col*:colors=88,*256col*:colors=256" - ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" - ":Cc=\\E]12;%p1%s\\007:Cr=\\E]112\\007" - ":Cs=\\E[%p1%d q:Csr=\\E[2 q,screen*:XT" + { .name = "status-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "bg=green,fg=black" }, { .name = "update-environment", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " "SSH_CONNECTION WINDOWID XAUTHORITY" @@ -428,29 +492,406 @@ const struct options_table_entry session_options_table[] = { { .name = "visual-activity", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-bell", .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "visual-content", - .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-silence", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = " -_@" }, + { .name = "aggressive-resize", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0 + }, + + { .name = "allow-rename", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 1 + }, + + { .name = "alternate-screen", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 1 + }, + + { .name = "automatic-rename", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 1 + }, + + { .name = "automatic-rename-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" + "#{?pane_dead,[dead],}" + }, + + { .name = "clock-mode-colour", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 4 + }, + + { .name = "clock-mode-style", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_clock_mode_style_list, + .default_num = 1 + }, + + { .name = "force-height", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "force-width", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "main-pane-height", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 24 + }, + + { .name = "main-pane-width", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 80 + }, + + { .name = "mode-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0, + .style = "mode-style" + }, + + { .name = "mode-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 3, + .style = "mode-style" + }, + + { .name = "mode-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0, + .style = "mode-style" + }, + + { .name = "mode-keys", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_mode_keys_list, + .default_num = MODEKEY_EMACS + }, + + { .name = "mode-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "bg=yellow,fg=black" + }, + + { .name = "monitor-activity", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0 + }, + + { .name = "monitor-silence", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "other-pane-height", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "other-pane-width", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "pane-active-border-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "pane-active-border-style" + }, + + { .name = "pane-active-border-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 2, + .style = "pane-active-border-style" + }, + + { .name = "pane-active-border-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "fg=green" + }, + + { .name = "pane-base-index", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 0, + .maximum = USHRT_MAX, + .default_num = 0 + }, + + { .name = "pane-border-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "pane-border-style" + }, + + { .name = "pane-border-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "pane-border-style" + }, + + { .name = "pane-border-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default" + }, + + { .name = "remain-on-exit", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0 + }, + + { .name = "synchronize-panes", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0 + }, + + { .name = "window-active-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default" + }, + + { .name = "window-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default" + }, + + { .name = "window-status-activity-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = GRID_ATTR_REVERSE, + .style = "window-status-activity-style" + }, + + { .name = "window-status-activity-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-activity-style" + }, + + { .name = "window-status-activity-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-activity-style" + }, + + { .name = "window-status-activity-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "reverse" + }, + + { .name = "window-status-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0, + .style = "window-status-style" + }, + + { .name = "window-status-bell-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = GRID_ATTR_REVERSE, + .style = "window-status-bell-style" + }, + + { .name = "window-status-bell-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-bell-style" + }, + + { .name = "window-status-bell-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-bell-style" + }, + + { .name = "window-status-bell-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "reverse" + }, + + { .name = "window-status-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-style" + }, + + { .name = "window-status-current-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0, + .style = "window-status-current-style" + }, + + { .name = "window-status-current-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-current-style" + }, + + { .name = "window-status-current-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-current-style" + }, + + { .name = "window-status-current-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "#I:#W#{?window_flags,#{window_flags}, }" + }, + + { .name = "window-status-current-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default" + }, + + { .name = "window-status-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-style" + }, + + { .name = "window-status-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "#I:#W#{?window_flags,#{window_flags}, }" + }, + + { .name = "window-status-last-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0, + .style = "window-status-last-style" + }, + + { .name = "window-status-last-bg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-last-style" + }, + + { .name = "window-status-last-fg", + .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 8, + .style = "window-status-last-style" + }, + + { .name = "window-status-last-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default" + }, + + { .name = "window-status-separator", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = " " + }, + + { .name = "window-status-style", + .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "default" + }, + + { .name = "wrap-search", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 1 + }, + + { .name = "xterm-keys", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0 + }, + +#ifdef TMATE { .name = "tmate-display-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, @@ -477,7 +918,7 @@ const struct options_table_entry session_options_table[] = { { .name = "tmate-server-dsa-fingerprint", .type = OPTIONS_TABLE_STRING, - .default_str = "f5:26:31:c3:8a:78:6e:5c:77:74:0f:41:5b:5f:21:88" + .default_str = "obsolete" }, { .name = "tmate-server-rsa-fingerprint", @@ -489,296 +930,33 @@ const struct options_table_entry session_options_table[] = { .type = OPTIONS_TABLE_STRING, .default_str = "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" }, - - - { .name = NULL } -}; - -/* Window options. */ -const struct options_table_entry window_options_table[] = { - { .name = "aggressive-resize", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "allow-rename", - .type = OPTIONS_TABLE_FLAG, - .default_num = 1 - }, - - { .name = "alternate-screen", - .type = OPTIONS_TABLE_FLAG, - .default_num = 1 - }, - - { .name = "automatic-rename", - .type = OPTIONS_TABLE_FLAG, - .default_num = 1 - }, - - { .name = "c0-change-trigger", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 250, - .minimum = 0, - .maximum = USHRT_MAX - }, - - { .name = "c0-change-interval", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 100, - .minimum = 1, - .maximum = USHRT_MAX - }, - - { .name = "clock-mode-colour", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 4 - }, - - { .name = "clock-mode-style", - .type = OPTIONS_TABLE_CHOICE, - .choices = options_table_clock_mode_style_list, - .default_num = 1 - }, - - { .name = "force-height", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 - }, - - { .name = "force-width", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 - }, - - { .name = "main-pane-height", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 24 - }, - - { .name = "main-pane-width", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 80 - }, - - { .name = "mode-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 - }, - - { .name = "mode-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 3 - }, - - { .name = "mode-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 0 - }, - - { .name = "mode-keys", - .type = OPTIONS_TABLE_CHOICE, - .choices = options_table_mode_keys_list, - .default_num = MODEKEY_EMACS - }, - - { .name = "mode-mouse", - .type = OPTIONS_TABLE_CHOICE, - .choices = options_table_mode_mouse_list, - .default_num = 0 - }, - - { .name = "monitor-activity", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "monitor-content", - .type = OPTIONS_TABLE_STRING, - .default_str = "" - }, - - { .name = "monitor-silence", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 - }, - - { .name = "other-pane-height", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 - }, - - { .name = "other-pane-width", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = INT_MAX, - .default_num = 0 - }, - - { .name = "pane-base-index", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 0, - .maximum = USHRT_MAX, - .default_num = 0 - }, - - { .name = "remain-on-exit", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "synchronize-panes", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ - }, - - { .name = "window-status-activity-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = GRID_ATTR_REVERSE - }, - - { .name = "window-status-activity-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-activity-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-bell-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = GRID_ATTR_REVERSE - }, - - { .name = "window-status-bell-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-bell-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-content-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = GRID_ATTR_REVERSE - }, - - { .name = "window-status-content-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-content-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 - }, - - { .name = "window-status-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-current-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 - }, - - { .name = "window-status-current-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-current-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-current-format", - .type = OPTIONS_TABLE_STRING, - .default_str = "#I:#W#F" - }, - - { .name = "window-status-last-attr", - .type = OPTIONS_TABLE_ATTRIBUTES, - .default_num = 0 - }, - - { .name = "window-status-last-bg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-last-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-fg", - .type = OPTIONS_TABLE_COLOUR, - .default_num = 8 - }, - - { .name = "window-status-format", - .type = OPTIONS_TABLE_STRING, - .default_str = "#I:#W#F" - }, - - { .name = "window-status-separator", - .type = OPTIONS_TABLE_STRING, - .default_str = " " - }, - - { .name = "wrap-search", - .type = OPTIONS_TABLE_FLAG, - .default_num = 1 - }, - - { .name = "xterm-keys", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, +#endif { .name = NULL } }; /* Populate an options tree from a table. */ void -options_table_populate_tree( - const struct options_table_entry *table, struct options *oo) +options_table_populate_tree(enum options_table_scope scope, struct options *oo) { const struct options_table_entry *oe; - for (oe = table; oe->name != NULL; oe++) { - if (oe->default_str != NULL) + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope == OPTIONS_TABLE_NONE) + fatalx("no scope for %s", oe->name); + if (oe->scope != scope) + continue; + switch (oe->type) { + case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); - else + break; + case OPTIONS_TABLE_STYLE: + options_set_style(oo, oe->name, oe->default_str, 0); + break; + default: options_set_number(oo, oe->name, oe->default_num); + break; + } } } @@ -823,39 +1001,32 @@ options_table_print_entry(const struct options_table_entry *oe, s = oe->choices[o->num]; xsnprintf(out, sizeof out, "%s", s); break; + case OPTIONS_TABLE_STYLE: + s = style_tostring(&o->style); + xsnprintf(out, sizeof out, "%s", s); + break; } return (out); } /* Find an option. */ int -options_table_find( - const char *optstr, const struct options_table_entry **table, - const struct options_table_entry **oe) +options_table_find(const char *optstr, const struct options_table_entry **oe) { - static const struct options_table_entry *tables[] = { - server_options_table, - window_options_table, - session_options_table - }; const struct options_table_entry *oe_loop; - u_int i; - for (i = 0; i < nitems(tables); i++) { - for (oe_loop = tables[i]; oe_loop->name != NULL; oe_loop++) { - if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) - continue; + for (oe_loop = options_table; oe_loop->name != NULL; oe_loop++) { + if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) + continue; - /* If already found, ambiguous. */ - if (*oe != NULL) - return (-1); - *oe = oe_loop; - *table = tables[i]; + /* If already found, ambiguous. */ + if (*oe != NULL) + return (-1); + *oe = oe_loop; - /* Bail now if an exact match. */ - if (strcmp((*oe)->name, optstr) == 0) - break; - } + /* Bail now if an exact match. */ + if (strcmp(oe_loop->name, optstr) == 0) + break; } return (0); } diff --git a/options.c b/options.c index 7360906b..02f0f957 100644 --- a/options.c +++ b/options.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -26,37 +26,67 @@ /* * Option handling; each option has a name, type and value and is stored in - * a splay tree. + * a red-black tree. */ +struct options { + RB_HEAD(options_tree, options_entry) tree; + struct options *parent; +}; + +static int options_cmp(struct options_entry *, struct options_entry *); +RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); RB_GENERATE(options_tree, options_entry, entry, options_cmp); -int +static void options_free1(struct options *, struct options_entry *); + +static int options_cmp(struct options_entry *o1, struct options_entry *o2) { return (strcmp(o1->name, o2->name)); } -void -options_init(struct options *oo, struct options *parent) +struct options * +options_create(struct options *parent) { + struct options *oo; + + oo = xcalloc(1, sizeof *oo); RB_INIT(&oo->tree); oo->parent = parent; + return (oo); +} + +static void +options_free1(struct options *oo, struct options_entry *o) +{ + RB_REMOVE(options_tree, &oo->tree, o); + free((char *)o->name); + if (o->type == OPTIONS_STRING) + free(o->str); + free(o); } void options_free(struct options *oo) { - struct options_entry *o; + struct options_entry *o, *o1; - while (!RB_EMPTY(&oo->tree)) { - o = RB_ROOT(&oo->tree); - RB_REMOVE(options_tree, &oo->tree, o); - free(o->name); - if (o->type == OPTIONS_STRING) - free(o->str); - free(o); - } + RB_FOREACH_SAFE (o, options_tree, &oo->tree, o1) + options_free1(oo, o); + free(oo); +} + +struct options_entry * +options_first(struct options *oo) +{ + return (RB_MIN(options_tree, &oo->tree)); +} + +struct options_entry * +options_next(struct options_entry *o) +{ + return (RB_NEXT(options_tree, &oo->tree, o)); } struct options_entry * @@ -64,7 +94,7 @@ options_find1(struct options *oo, const char *name) { struct options_entry p; - p.name = (char *) name; + p.name = (char *)name; return (RB_FIND(options_tree, &oo->tree, &p)); } @@ -73,7 +103,7 @@ options_find(struct options *oo, const char *name) { struct options_entry *o, p; - p.name = (char *) name; + p.name = (char *)name; o = RB_FIND(options_tree, &oo->tree, &p); while (o == NULL) { oo = oo->parent; @@ -89,17 +119,11 @@ options_remove(struct options *oo, const char *name) { struct options_entry *o; - if ((o = options_find1(oo, name)) == NULL) - return; - - RB_REMOVE(options_tree, &oo->tree, o); - free(o->name); - if (o->type == OPTIONS_STRING) - free(o->str); - free(o); + if ((o = options_find1(oo, name)) != NULL) + options_free1(oo, o); } -struct options_entry *printflike3 +struct options_entry * options_set_string(struct options *oo, const char *name, const char *fmt, ...) { struct options_entry *o; @@ -109,6 +133,7 @@ options_set_string(struct options *oo, const char *name, const char *fmt, ...) o = xmalloc(sizeof *o); o->name = xstrdup(name); RB_INSERT(options_tree, &oo->tree, o); + memcpy(&o->style, &grid_default_cell, sizeof o->style); } else if (o->type == OPTIONS_STRING) free(o->str); @@ -125,9 +150,9 @@ options_get_string(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + fatalx("missing option %s", name); if (o->type != OPTIONS_STRING) - fatalx("option not a string"); + fatalx("option %s not a string", name); return (o->str); } @@ -140,6 +165,7 @@ options_set_number(struct options *oo, const char *name, long long value) o = xmalloc(sizeof *o); o->name = xstrdup(name); RB_INSERT(options_tree, &oo->tree, o); + memcpy(&o->style, &grid_default_cell, sizeof o->style); } else if (o->type == OPTIONS_STRING) free(o->str); @@ -154,8 +180,48 @@ options_get_number(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + fatalx("missing option %s", name); if (o->type != OPTIONS_NUMBER) - fatalx("option not a number"); + fatalx("option %s not a number", name); return (o->num); } + +struct options_entry * +options_set_style(struct options *oo, const char *name, const char *value, + int append) +{ + struct options_entry *o; + struct grid_cell tmpgc; + + o = options_find1(oo, name); + if (o == NULL || !append) + memcpy(&tmpgc, &grid_default_cell, sizeof tmpgc); + else + memcpy(&tmpgc, &o->style, sizeof tmpgc); + + if (style_parse(&grid_default_cell, &tmpgc, value) == -1) + return (NULL); + + if (o == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + RB_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + free(o->str); + + o->type = OPTIONS_STYLE; + memcpy(&o->style, &tmpgc, sizeof o->style); + return (o); +} + +struct grid_cell * +options_get_style(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option %s", name); + if (o->type != OPTIONS_STYLE) + fatalx("option %s not a style", name); + return (&o->style); +} diff --git a/osdep-aix.c b/osdep-aix.c index 8d590816..ef7d6c7e 100644 --- a/osdep-aix.c +++ b/osdep-aix.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -16,21 +16,75 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include +#include -#include +#include +#include +#include #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(__unused int fd, char *tty) { - return (NULL); + struct psinfo p; + char *path; + ssize_t bytes; + int f, ttyfd, retval; + pid_t pgrp; + + if ((ttyfd = open(tty, O_RDONLY|O_NOCTTY)) == -1) + return (NULL); + + retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); + close(ttyfd); + if (retval == -1) + return (NULL); + + xasprintf(&path, "/proc/%u/psinfo", (u_int) pgrp); + f = open(path, O_RDONLY); + free(path); + if (f < 0) + return (NULL); + + bytes = read(f, &p, sizeof(p)); + close(f); + if (bytes != sizeof(p)) + return (NULL); + + return (xstrdup(p.pr_fname)); } char * -osdep_get_cwd(unused int fd) +osdep_get_cwd(int fd) { + static char target[MAXPATHLEN + 1]; + char *path; + const char *ttypath; + ssize_t n; + pid_t pgrp; + int len, retval, ttyfd; + + if ((ttypath = ptsname(fd)) == NULL) + return (NULL); + if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) + return (NULL); + + retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); + close(ttyfd); + if (retval == -1) + return (NULL); + + xasprintf(&path, "/proc/%u/cwd", (u_int) pgrp); + n = readlink(path, target, MAXPATHLEN); + free(path); + if (n > 0) { + target[n] = '\0'; + if ((len = strlen(target)) > 1 && target[len - 1] == '/') + target[len - 1] = '\0'; + return (target); + } return (NULL); } diff --git a/osdep-cygwin.c b/osdep-cygwin.c new file mode 100644 index 00000000..9a3ea408 --- /dev/null +++ b/osdep-cygwin.c @@ -0,0 +1,88 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * 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 +#include + +#include +#include +#include +#include + +#include "tmux.h" + +char * +osdep_get_name(int fd, __unused char *tty) +{ + FILE *f; + char *path, *buf; + size_t len; + int ch; + pid_t pgrp; + + if ((pgrp = tcgetpgrp(fd)) == -1) + return (NULL); + + xasprintf(&path, "/proc/%lld/cmdline", (long long) pgrp); + if ((f = fopen(path, "r")) == NULL) { + free(path); + return (NULL); + } + free(path); + + len = 0; + buf = NULL; + while ((ch = fgetc(f)) != EOF) { + if (ch == '\0') + break; + buf = xrealloc(buf, len + 2); + buf[len++] = ch; + } + if (buf != NULL) + buf[len] = '\0'; + + fclose(f); + return (buf); +} + +char * +osdep_get_cwd(int fd) +{ + static char target[MAXPATHLEN + 1]; + char *path; + pid_t pgrp; + ssize_t n; + + if ((pgrp = tcgetpgrp(fd)) == -1) + return (NULL); + + xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); + n = readlink(path, target, MAXPATHLEN); + free(path); + if (n > 0) { + target[n] = '\0'; + return (target); + } + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} diff --git a/osdep-darwin.c b/osdep-darwin.c index 23de9d52..40b18951 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Joshua Elsasser @@ -28,22 +28,22 @@ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); -#define unused __attribute__ ((unused)) +#define __unused __attribute__ ((__unused__)) char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { - struct proc_bsdshortinfo bsdinfo; + struct proc_bsdinfo bsdinfo; pid_t pgrp; int ret; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); - ret = proc_pidinfo(pgrp, PROC_PIDT_SHORTBSDINFO, 0, + ret = proc_pidinfo(pgrp, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof bsdinfo); - if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0') - return (strdup(bsdinfo.pbsi_comm)); + if (ret == sizeof bsdinfo && *bsdinfo.pbi_comm != '\0') + return (strdup(bsdinfo.pbi_comm)); return (NULL); } diff --git a/osdep-dragonfly.c b/osdep-dragonfly.c index ad417d98..f9b0efcf 100644 --- a/osdep-dragonfly.c +++ b/osdep-dragonfly.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/osdep-freebsd.c b/osdep-freebsd.c index d596eab4..d7f419b8 100644 --- a/osdep-freebsd.c +++ b/osdep-freebsd.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -132,8 +132,8 @@ error: return (NULL); } -char * -osdep_get_cwd(int fd) +static char * +osdep_get_cwd_fallback(int fd) { static char wd[PATH_MAX]; struct kinfo_file *info = NULL; @@ -158,6 +158,38 @@ osdep_get_cwd(int fd) return (NULL); } +#ifdef KERN_PROC_CWD +char * +osdep_get_cwd(int fd) +{ + static struct kinfo_file info; + static int fallback; + int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, 0 }; + size_t len = sizeof info; + + if (fallback) + return (osdep_get_cwd_fallback(fd)); + + if ((name[3] = tcgetpgrp(fd)) == -1) + return (NULL); + + if (sysctl(name, 4, &info, &len, NULL, 0) == -1) { + if (errno == ENOENT) { + fallback = 1; + return (osdep_get_cwd_fallback(fd)); + } + return (NULL); + } + return (info.kf_path); +} +#else /* !KERN_PROC_CWD */ +char * +osdep_get_cwd(int fd) +{ + return (osdep_get_cwd_fallback(fd)); +} +#endif /* KERN_PROC_CWD */ + struct event_base * osdep_event_init(void) { diff --git a/osdep-hpux.c b/osdep-hpux.c index 352e375b..a6d75f94 100644 --- a/osdep-hpux.c +++ b/osdep-hpux.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -23,13 +23,13 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } char * -osdep_get_cwd(unused int fd) +osdep_get_cwd(__unused int fd) { return (NULL); } diff --git a/osdep-linux.c b/osdep-linux.c index b65acffc..00501efb 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -27,7 +28,7 @@ #include "tmux.h" char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { FILE *f; char *path, *buf; @@ -50,7 +51,7 @@ osdep_get_name(int fd, unused char *tty) while ((ch = fgetc(f)) != EOF) { if (ch == '\0') break; - buf = xrealloc(buf, 1, len + 2); + buf = xrealloc(buf, len + 2); buf[len++] = ch; } if (buf != NULL) @@ -65,7 +66,7 @@ osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; - pid_t pgrp; + pid_t pgrp, sid; ssize_t n; if ((pgrp = tcgetpgrp(fd)) == -1) @@ -74,6 +75,13 @@ osdep_get_cwd(int fd) xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); + + if (n == -1 && ioctl(fd, TIOCGSID, &sid) != -1) { + xasprintf(&path, "/proc/%lld/cwd", (long long) sid); + n = readlink(path, target, MAXPATHLEN); + free(path); + } + if (n > 0) { target[n] = '\0'; return (target); @@ -84,14 +92,7 @@ osdep_get_cwd(int fd) struct event_base * osdep_event_init(void) { - /* - * On Linux, epoll doesn't work on /dev/null (yes, really). - * - * This has been commented because libevent versions up until the very - * latest (1.4 git or 2.0.10) do not handle signals properly when using - * poll or select, causing hangs. - * - */ - /* setenv("EVENT_NOEPOLL", "1", 1); */ + /* On Linux, epoll doesn't work on /dev/null (yes, really). */ + setenv("EVENT_NOEPOLL", "1", 1); return (event_init()); } diff --git a/osdep-netbsd.c b/osdep-netbsd.c index f16d0dc8..15686860 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/osdep-openbsd.c b/osdep-openbsd.c index 7be38a91..414228b7 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -16,7 +16,8 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include /* MAXCOMLEN */ +#include #include #include #include @@ -34,7 +35,7 @@ #define is_runnable(p) \ ((p)->p_stat == SRUN || (p)->p_stat == SIDL || (p)->p_stat == SONPROC) #define is_stopped(p) \ - ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB || (p)->p_stat == SDEAD) + ((p)->p_stat == SSTOP || (p)->p_stat == SDEAD) struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *osdep_get_name(int, char *); @@ -99,7 +100,7 @@ osdep_get_name(int fd, char *tty) retry: if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) - return (NULL); + goto error; len = (len * 5) / 4; if ((newbuf = realloc(buf, len)) == NULL) @@ -135,7 +136,7 @@ error: return (NULL); } -char* +char * osdep_get_cwd(int fd) { int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; diff --git a/osdep-sunos.c b/osdep-sunos.c index fd644f5d..855cac9d 100644 --- a/osdep-sunos.c +++ b/osdep-sunos.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Todd Carson @@ -69,10 +69,19 @@ osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; + const char *ttypath; ssize_t n; pid_t pgrp; + int retval, ttyfd; - if ((pgrp = tcgetpgrp(fd)) == -1) + if ((ttypath = ptsname(fd)) == NULL) + return (NULL); + if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) + return (NULL); + + retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); + close(ttyfd); + if (retval == -1) return (NULL); xasprintf(&path, "/proc/%u/path/cwd", (u_int) pgrp); diff --git a/osdep-unknown.c b/osdep-unknown.c index 41f435bc..9465db97 100644 --- a/osdep-unknown.c +++ b/osdep-unknown.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -23,7 +23,7 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } diff --git a/paste.c b/paste.c index 7cbbbfb3..d43829a2 100644 --- a/paste.c +++ b/paste.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -25,170 +25,269 @@ #include "tmux.h" /* - * Stack of paste buffers. Note that paste buffer data is not necessarily a C + * Set of paste buffers. Note that paste buffer data is not necessarily a C * string! */ -/* Return each item of the stack in turn. */ +struct paste_buffer { + char *data; + size_t size; + + char *name; + int automatic; + u_int order; + + RB_ENTRY(paste_buffer) name_entry; + RB_ENTRY(paste_buffer) time_entry; +}; + +u_int paste_next_index; +u_int paste_next_order; +u_int paste_num_automatic; +RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; +RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; + +int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); +RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); +RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); + +int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); +RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); +RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); + +int +paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) +{ + return (strcmp(a->name, b->name)); +} + +int +paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) +{ + if (a->order > b->order) + return (-1); + if (a->order < b->order) + return (1); + return (0); +} + +/* Get paste buffer name. */ +const char * +paste_buffer_name(struct paste_buffer *pb) +{ + return (pb->name); +} + +/* Get paste buffer data. */ +const char * +paste_buffer_data(struct paste_buffer *pb, size_t *size) +{ + if (size != NULL) + *size = pb->size; + return (pb->data); +} + +/* Walk paste buffers by name. */ struct paste_buffer * -paste_walk_stack(struct paste_stack *ps, u_int *idx) +paste_walk(struct paste_buffer *pb) +{ + if (pb == NULL) + return (RB_MIN(paste_time_tree, &paste_by_time)); + return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); +} + +/* Get the most recent automatic buffer. */ +struct paste_buffer * +paste_get_top(const char **name) { struct paste_buffer *pb; - pb = paste_get_index(ps, *idx); - (*idx)++; + pb = RB_MIN(paste_time_tree, &paste_by_time); + if (pb == NULL) + return (NULL); + if (name != NULL) + *name = pb->name; return (pb); } -/* Get the top item on the stack. */ +/* Get a paste buffer by name. */ struct paste_buffer * -paste_get_top(struct paste_stack *ps) +paste_get_name(const char *name) { - if (ARRAY_LENGTH(ps) == 0) + struct paste_buffer pbfind; + + if (name == NULL || *name == '\0') return (NULL); - return (ARRAY_FIRST(ps)); + + pbfind.name = (char *)name; + return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); } -/* Get an item by its index. */ -struct paste_buffer * -paste_get_index(struct paste_stack *ps, u_int idx) +/* Free a paste buffer. */ +void +paste_free(struct paste_buffer *pb) { - if (idx >= ARRAY_LENGTH(ps)) - return (NULL); - return (ARRAY_ITEM(ps, idx)); -} - -/* Free the top item on the stack. */ -int -paste_free_top(struct paste_stack *ps) -{ - struct paste_buffer *pb; - - if (ARRAY_LENGTH(ps) == 0) - return (-1); - - pb = ARRAY_FIRST(ps); - ARRAY_REMOVE(ps, 0); + RB_REMOVE(paste_name_tree, &paste_by_name, pb); + RB_REMOVE(paste_time_tree, &paste_by_time, pb); + if (pb->automatic) + paste_num_automatic--; free(pb->data); + free(pb->name); free(pb); - - return (0); -} - -/* Free an item by index. */ -int -paste_free_index(struct paste_stack *ps, u_int idx) -{ - struct paste_buffer *pb; - - if (idx >= ARRAY_LENGTH(ps)) - return (-1); - - pb = ARRAY_ITEM(ps, idx); - ARRAY_REMOVE(ps, idx); - - free(pb->data); - free(pb); - - return (0); } /* - * Add an item onto the top of the stack, freeing the bottom if at limit. Note + * Add an automatic buffer, freeing the oldest automatic item if at limit. Note * that the caller is responsible for allocating data. */ void -paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit) +paste_add(char *data, size_t size) { - struct paste_buffer *pb; + struct paste_buffer *pb, *pb1; + u_int limit; if (size == 0) return; - while (ARRAY_LENGTH(ps) >= limit) { - pb = ARRAY_LAST(ps); - free(pb->data); - free(pb); - ARRAY_TRUNC(ps, 1); + limit = options_get_number(global_options, "buffer-limit"); + RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { + if (paste_num_automatic < limit) + break; + if (pb->automatic) + paste_free(pb); } pb = xmalloc(sizeof *pb); - ARRAY_INSERT(ps, 0, pb); + + pb->name = NULL; + do { + free(pb->name); + xasprintf(&pb->name, "buffer%04u", paste_next_index); + paste_next_index++; + } while (paste_get_name(pb->name) != NULL); pb->data = data; pb->size = size; + + pb->automatic = 1; + paste_num_automatic++; + + pb->order = paste_next_order++; + RB_INSERT(paste_name_tree, &paste_by_name, pb); + RB_INSERT(paste_time_tree, &paste_by_time, pb); } - -/* - * Replace an item on the stack. Note that the caller is responsible for - * allocating data. - */ +/* Rename a paste buffer. */ int -paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) +paste_rename(const char *oldname, const char *newname, char **cause) { - struct paste_buffer *pb; + struct paste_buffer *pb, *pb_new; - if (size == 0) - return (0); + if (cause != NULL) + *cause = NULL; - if (idx >= ARRAY_LENGTH(ps)) + if (oldname == NULL || *oldname == '\0') { + if (cause != NULL) + *cause = xstrdup("no buffer"); return (-1); + } + if (newname == NULL || *newname == '\0') { + if (cause != NULL) + *cause = xstrdup("new name is empty"); + return (-1); + } - pb = ARRAY_ITEM(ps, idx); - free(pb->data); + pb = paste_get_name(oldname); + if (pb == NULL) { + if (cause != NULL) + xasprintf(cause, "no buffer %s", oldname); + return (-1); + } - pb->data = data; - pb->size = size; + pb_new = paste_get_name(newname); + if (pb_new != NULL) { + if (cause != NULL) + xasprintf(cause, "buffer %s already exists", newname); + return (-1); + } + + RB_REMOVE(paste_name_tree, &paste_by_name, pb); + + free(pb->name); + pb->name = xstrdup(newname); + + if (pb->automatic) + paste_num_automatic--; + pb->automatic = 0; + + RB_INSERT(paste_name_tree, &paste_by_name, pb); return (0); } -/* Convert a buffer into a visible string. */ -char * -paste_print(struct paste_buffer *pb, size_t width) +/* + * Add or replace an item in the store. Note that the caller is responsible for + * allocating data. + */ +int +paste_set(char *data, size_t size, const char *name, char **cause) { - char *buf; - size_t len, used; + struct paste_buffer *pb, *old; - if (width < 3) - width = 3; - buf = xmalloc(width * 4 + 1); + if (cause != NULL) + *cause = NULL; + + if (size == 0) { + free(data); + return (0); + } + if (name == NULL) { + paste_add(data, size); + return (0); + } + + if (*name == '\0') { + if (cause != NULL) + *cause = xstrdup("empty buffer name"); + return (-1); + } + + pb = xmalloc(sizeof *pb); + + pb->name = xstrdup(name); + + pb->data = data; + pb->size = size; + + pb->automatic = 0; + pb->order = paste_next_order++; + + if ((old = paste_get_name(name)) != NULL) + paste_free(old); + + RB_INSERT(paste_name_tree, &paste_by_name, pb); + RB_INSERT(paste_time_tree, &paste_by_time, pb); + + return (0); +} + +/* Convert start of buffer into a nice string. */ +char * +paste_make_sample(struct paste_buffer *pb) +{ + char *buf; + size_t len, used; + const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; + const size_t width = 200; len = pb->size; if (len > width) len = width; + buf = xreallocarray(NULL, len, 4 + 4); - used = strvisx(buf, pb->data, len, VIS_OCTAL|VIS_TAB|VIS_NL); + used = utf8_strvis(buf, pb->data, len, flags); if (pb->size > width || used > width) - strlcpy(buf + width - 3, "...", 4); - + strlcpy(buf + width, "...", 4); return (buf); } - -/* Paste into a window pane, filtering '\n' according to separator. */ -void -paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, - const char *sep, int bracket) -{ - const char *data = pb->data, *end = data + pb->size, *lf; - size_t seplen; - - if (bracket) - bufferevent_write(wp->event, "\033[200~", 6); - - seplen = strlen(sep); - while ((lf = memchr(data, '\n', end - data)) != NULL) { - if (lf != data) - bufferevent_write(wp->event, data, lf - data); - bufferevent_write(wp->event, sep, seplen); - data = lf + 1; - } - - if (end != data) - bufferevent_write(wp->event, data, end - data); - - if (bracket) - bufferevent_write(wp->event, "\033[201~", 6); -} diff --git a/presentations/tmux_asiabsdcon11.odt b/presentations/tmux_asiabsdcon11.odt new file mode 100644 index 00000000..ac9f0934 Binary files /dev/null and b/presentations/tmux_asiabsdcon11.odt differ diff --git a/presentations/tmux_asiabsdcon11.pdf b/presentations/tmux_asiabsdcon11.pdf new file mode 100644 index 00000000..76cd9b25 Binary files /dev/null and b/presentations/tmux_asiabsdcon11.pdf differ diff --git a/presentations/tmux_linuxtag_2011.odp b/presentations/tmux_linuxtag_2011.odp new file mode 100644 index 00000000..0b6c9cf1 Binary files /dev/null and b/presentations/tmux_linuxtag_2011.odp differ diff --git a/proc.c b/proc.c new file mode 100644 index 00000000..360d909b --- /dev/null +++ b/proc.c @@ -0,0 +1,268 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Nicholas Marriott + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +struct tmuxproc { + const char *name; + int exit; + + void (*signalcb)(int); +}; + +struct tmuxpeer { + struct tmuxproc *parent; + + struct imsgbuf ibuf; + struct event event; + + int flags; +#define PEER_BAD 0x1 + + void (*dispatchcb)(struct imsg *, void *); + void *arg; +}; + +static int peer_check_version(struct tmuxpeer *, struct imsg *); +static void proc_update_event(struct tmuxpeer *); + +static void +proc_event_cb(__unused int fd, short events, void *arg) +{ + struct tmuxpeer *peer = arg; + ssize_t n; + struct imsg imsg; + + if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { + if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) || + n == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + for (;;) { + if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { + peer->dispatchcb(NULL, peer->arg); + return; + } + if (n == 0) + break; + log_debug("peer %p message %d", peer, imsg.hdr.type); + + if (peer_check_version(peer, &imsg) != 0) { + if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + break; + } + + peer->dispatchcb(&imsg, peer->arg); + imsg_free(&imsg); + } + } + + if (events & EV_WRITE) { + if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { + peer->dispatchcb(NULL, peer->arg); + return; + } + } + + if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + + proc_update_event(peer); +} + +static void +proc_signal_cb(int signo, __unused short events, void *arg) +{ + struct tmuxproc *tp = arg; + + tp->signalcb(signo); +} + +static int +peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) +{ + int version; + + version = imsg->hdr.peerid & 0xff; + if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) { + log_debug("peer %p bad version %d", peer, version); + + proc_send(peer, MSG_VERSION, -1, NULL, 0); + peer->flags |= PEER_BAD; + + return (-1); + } + return (0); +} + +static void +proc_update_event(struct tmuxpeer *peer) +{ + short events; + + event_del(&peer->event); + + events = EV_READ; + if (peer->ibuf.w.queued > 0) + events |= EV_WRITE; + event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); + + event_add(&peer->event, NULL); +} + +int +proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, + size_t len) +{ + struct imsgbuf *ibuf = &peer->ibuf; + void *vp = (void *)buf; + int retval; + + if (peer->flags & PEER_BAD) + return (-1); + log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); + + retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); + if (retval != 1) + return (-1); + proc_update_event(peer); + return (0); +} + +int +proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s) +{ + return (proc_send(peer, type, -1, s, strlen(s) + 1)); +} + +struct tmuxproc * +proc_start(const char *name, struct event_base *base, int forkflag, + void (*signalcb)(int)) +{ + struct tmuxproc *tp; + struct utsname u; + + if (forkflag) { + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + return (NULL); + } + if (daemon(1, 0) != 0) + fatal("daemon failed"); + + clear_signals(0); + if (event_reinit(base) != 0) + fatalx("event_reinit failed"); + } + + log_open(name); + +#ifdef HAVE_SETPROCTITLE + setproctitle("%s (%s)", name, socket_path); +#endif + + if (uname(&u) < 0) + memset(&u, 0, sizeof u); + + log_debug("%s started (%ld): socket %s, protocol %d", name, + (long)getpid(), socket_path, PROTOCOL_VERSION); + log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, + u.version, event_get_version(), event_get_method()); + + tp = xcalloc(1, sizeof *tp); + tp->name = xstrdup(name); + + tp->signalcb = signalcb; + set_signals(proc_signal_cb, tp); + + return (tp); +} + +void +proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) +{ + log_debug("%s loop enter", tp->name); + do + event_loop(EVLOOP_ONCE); + while (!tp->exit && (loopcb == NULL || !loopcb ())); + log_debug("%s loop exit", tp->name); +} + +void +proc_exit(struct tmuxproc *tp) +{ + tp->exit = 1; +} + +struct tmuxpeer * +proc_add_peer(struct tmuxproc *tp, int fd, + void (*dispatchcb)(struct imsg *, void *), void *arg) +{ + struct tmuxpeer *peer; + + peer = xcalloc(1, sizeof *peer); + peer->parent = tp; + + peer->dispatchcb = dispatchcb; + peer->arg = arg; + + imsg_init(&peer->ibuf, fd); + event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + + log_debug("add peer %p: %d (%p)", peer, fd, arg); + + proc_update_event(peer); + return (peer); +} + +void +proc_remove_peer(struct tmuxpeer *peer) +{ + log_debug("remove peer %p", peer); + + event_del(&peer->event); + imsg_clear(&peer->ibuf); + + close(peer->ibuf.fd); + free(peer); +} + +void +proc_kill_peer(struct tmuxpeer *peer) +{ + peer->flags |= PEER_BAD; +} diff --git a/resize.c b/resize.c index 91a8fa97..7d9a2551 100644 --- a/resize.c +++ b/resize.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -50,16 +50,16 @@ recalculate_sizes(void) struct client *c; struct window *w; struct window_pane *wp; - u_int i, j, ssx, ssy, has, limit; - int flag, has_status, is_zoomed; + u_int ssx, ssy, has, limit; + int flag, has_status, is_zoomed, forced; RB_FOREACH(s, sessions, &sessions) { - has_status = options_get_number(&s->options, "status"); + has_status = options_get_number(s->options, "status"); + s->attached = 0; ssx = ssy = UINT_MAX; - for (j = 0; j < ARRAY_LENGTH(&clients); j++) { - c = ARRAY_ITEM(&clients, j); - if (c == NULL || c->flags & CLIENT_SUSPENDED) + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_SUSPENDED) continue; if (c->session == s) { #ifdef TMATE @@ -74,6 +74,7 @@ recalculate_sizes(void) ssy = c->tty.sy - 1; else if (c->tty.sy < ssy) ssy = c->tty.sy; + s->attached++; } } @@ -106,11 +107,10 @@ recalculate_sizes(void) s->sy = ssy; } - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) + RB_FOREACH(w, windows, &windows) { + if (w->active == NULL) continue; - flag = options_get_number(&w->options, "aggressive-resize"); + flag = options_get_number(w->options, "aggressive-resize"); ssx = ssy = UINT_MAX; RB_FOREACH(s, sessions, &sessions) { @@ -119,7 +119,7 @@ recalculate_sizes(void) if (flag) has = s->curw->window == w; else - has = session_has(s, w) != NULL; + has = session_has(s, w); if (has) { if (s->sx < ssx) ssx = s->sx; @@ -130,18 +130,26 @@ recalculate_sizes(void) if (ssx == UINT_MAX || ssy == UINT_MAX) continue; - limit = options_get_number(&w->options, "force-width"); - if (limit != 0 && ssx > limit) + forced = 0; + limit = options_get_number(w->options, "force-width"); + if (limit >= PANE_MINIMUM && ssx > limit) { ssx = limit; - limit = options_get_number(&w->options, "force-height"); - if (limit != 0 && ssy > limit) + forced |= WINDOW_FORCEWIDTH; + } + limit = options_get_number(w->options, "force-height"); + if (limit >= PANE_MINIMUM && ssy > limit) { ssy = limit; + forced |= WINDOW_FORCEHEIGHT; + } if (w->sx == ssx && w->sy == ssy) continue; log_debug("window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy); + w->flags &= ~(WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); + w->flags |= forced; + is_zoomed = w->flags & WINDOW_ZOOMED; if (is_zoomed) window_unzoom(w); diff --git a/screen-redraw.c b/screen-redraw.c index a7bf81ff..9958d04a 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -26,10 +26,13 @@ int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); int screen_redraw_cell_border(struct client *, u_int, u_int); int screen_redraw_check_cell(struct client *, u_int, u_int, struct window_pane **); -int screen_redraw_check_active(u_int, u_int, int, struct window *, - struct window_pane *); +int screen_redraw_check_is(u_int, u_int, int, struct window *, + struct window_pane *, struct window_pane *); -void screen_redraw_draw_number(struct client *, struct window_pane *); +void screen_redraw_draw_borders(struct client *, int, u_int); +void screen_redraw_draw_panes(struct client *, u_int); +void screen_redraw_draw_status(struct client *, u_int); +void screen_redraw_draw_number(struct client *, struct window_pane *, u_int); #define CELL_INSIDE 0 #define CELL_LEFTRIGHT 1 @@ -172,13 +175,13 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, return (CELL_OUTSIDE); } -/* Check active pane indicator. */ +/* Check if the border of a particular pane. */ int -screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, - struct window_pane *wp) +screen_redraw_check_is(u_int px, u_int py, int type, struct window *w, + struct window_pane *wantwp, struct window_pane *wp) { /* Is this off the active pane border? */ - if (screen_redraw_cell_border1(w->active, px, py) != 1) + if (screen_redraw_cell_border1(wantwp, px, py) != 1) return (0); /* If there are more than two panes, that's enough. */ @@ -193,7 +196,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->xoff == 0 && wp->sx == w->sx) { /* This can either be the top pane or the bottom pane. */ if (wp->yoff == 0) { /* top pane */ - if (wp == w->active) + if (wp == wantwp) return (px <= wp->sx / 2); return (px > wp->sx / 2); } @@ -204,7 +207,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->yoff == 0 && wp->sy == w->sy) { /* This can either be the left pane or the right pane. */ if (wp->xoff == 0) { /* left pane */ - if (wp == w->active) + if (wp == wantwp) return (py <= wp->sy / 2); return (py > wp->sy / 2); } @@ -216,15 +219,13 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, /* Redraw entire screen. */ void -screen_redraw_screen(struct client *c, int status_only, int borders_only) +screen_redraw_screen(struct client *c, int draw_panes, int draw_status, + int draw_borders) { - struct window *w = c->session->curw->window; - struct options *oo = &c->session->options; - struct tty *tty = &c->tty; - struct window_pane *wp; - struct grid_cell active_gc, other_gc; - u_int i, j, type, top; - int status, spos, fg, bg; + struct options *oo = c->session->options; + struct tty *tty = &c->tty; + u_int top; + int status, spos; /* Suspended clients should not be updated. */ if (c->flags & CLIENT_SUSPENDED) @@ -239,80 +240,15 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only) top = 0; if (status && spos == 0) top = 1; + if (!status) + draw_status = 0; - /* If only drawing status and it is present, don't need the rest. */ - if (status_only && status) { - if (top) - tty_draw_line(tty, &c->status, 0, 0, 0); - else - tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); - tty_reset(tty); - return; - } - - /* Set up pane border attributes. */ - memcpy(&other_gc, &grid_marker_cell, sizeof other_gc); - memcpy(&active_gc, &grid_marker_cell, sizeof active_gc); - active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; - fg = options_get_number(oo, "pane-border-fg"); - colour_set_fg(&other_gc, fg); - bg = options_get_number(oo, "pane-border-bg"); - colour_set_bg(&other_gc, bg); - fg = options_get_number(oo, "pane-active-border-fg"); - colour_set_fg(&active_gc, fg); - bg = options_get_number(oo, "pane-active-border-bg"); - colour_set_bg(&active_gc, bg); - - /* Draw background and borders. */ - for (j = 0; j < tty->sy - status; j++) { - if (status_only) { - if (spos == 1 && j != tty->sy - 1) - continue; - else if (spos == 0 && j != 0) - break; - } - for (i = 0; i < tty->sx; i++) { - type = screen_redraw_check_cell(c, i, j, &wp); - if (type == CELL_INSIDE) - continue; - if (screen_redraw_check_active(i, j, type, w, wp)) - tty_attributes(tty, &active_gc); - else - tty_attributes(tty, &other_gc); - tty_cursor(tty, i, top + j); - tty_putc(tty, CELL_BORDERS[type]); - } - } - - /* If only drawing borders, that's it. */ - if (borders_only) - return; - - /* Draw the panes, if necessary. */ - TAILQ_FOREACH(wp, &w->panes, entry) { - if (!window_pane_visible(wp)) - continue; - for (i = 0; i < wp->sy; i++) { - if (status_only) { - if (spos == 1 && wp->yoff + i != tty->sy - 1) - continue; - else if (spos == 0 && wp->yoff + i != 0) - break; - } - tty_draw_line( - tty, wp->screen, i, wp->xoff, top + wp->yoff); - } - if (c->flags & CLIENT_IDENTIFY) - screen_redraw_draw_number(c, wp); - } - - /* Draw the status line. */ - if (status) { - if (top) - tty_draw_line(tty, &c->status, 0, 0, 0); - else - tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); - } + if (draw_borders) + screen_redraw_draw_borders(c, status, top); + if (draw_panes) + screen_redraw_draw_panes(c, top); + if (draw_status) + screen_redraw_draw_status(c, top); tty_reset(tty); } @@ -330,17 +266,133 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) yoff++; for (i = 0; i < wp->sy; i++) - tty_draw_line(&c->tty, wp->screen, i, wp->xoff, yoff); + tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff); tty_reset(&c->tty); } +/* Draw the borders. */ +void +screen_redraw_draw_borders(struct client *c, int status, u_int top) +{ + struct session *s = c->session; + struct window *w = s->curw->window; + struct options *oo = w->options; + struct tty *tty = &c->tty; + struct window_pane *wp; + struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; + struct grid_cell msg_gc; + u_int i, j, type, msgx = 0, msgy = 0; + int active, small, flags; + char msg[256]; + const char *tmp; + size_t msglen = 0; + + small = (tty->sy - status + top > w->sy) || (tty->sx > w->sx); + if (small) { + flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); + if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT)) + tmp = "force-width, force-height"; + else if (flags == WINDOW_FORCEWIDTH) + tmp = "force-width"; + else if (flags == WINDOW_FORCEHEIGHT) + tmp = "force-height"; + else + tmp = "a smaller client"; + xsnprintf(msg, sizeof msg, "(size %ux%u from %s)", + w->sx, w->sy, tmp); + msglen = strlen(msg); + + if (tty->sy - 1 - status + top > w->sy && tty->sx >= msglen) { + msgx = tty->sx - msglen; + msgy = tty->sy - 1 - status + top; + } else if (tty->sx - w->sx > msglen) { + msgx = tty->sx - msglen; + msgy = tty->sy - 1 - status + top; + } else + small = 0; + } + + style_apply(&other_gc, oo, "pane-border-style"); + style_apply(&active_gc, oo, "pane-active-border-style"); + active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + + memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); + m_other_gc.attr ^= GRID_ATTR_REVERSE; + memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); + m_active_gc.attr ^= GRID_ATTR_REVERSE; + + for (j = 0; j < tty->sy - status; j++) { + for (i = 0; i < tty->sx; i++) { + type = screen_redraw_check_cell(c, i, j, &wp); + if (type == CELL_INSIDE) + continue; + if (type == CELL_OUTSIDE && small && + i > msgx && j == msgy) + continue; + active = screen_redraw_check_is(i, j, type, w, + w->active, wp); + if (server_is_marked(s, s->curw, marked_pane.wp) && + screen_redraw_check_is(i, j, type, w, + marked_pane.wp, wp)) { + if (active) + tty_attributes(tty, &m_active_gc, NULL); + else + tty_attributes(tty, &m_other_gc, NULL); + } else if (active) + tty_attributes(tty, &active_gc, NULL); + else + tty_attributes(tty, &other_gc, NULL); + tty_cursor(tty, i, top + j); + tty_putc(tty, CELL_BORDERS[type]); + } + } + + if (small) { + memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc); + tty_attributes(tty, &msg_gc, NULL); + tty_cursor(tty, msgx, msgy); + tty_puts(tty, msg); + } +} + +/* Draw the panes. */ +void +screen_redraw_draw_panes(struct client *c, u_int top) +{ + struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; + struct window_pane *wp; + u_int i; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + for (i = 0; i < wp->sy; i++) + tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); + if (c->flags & CLIENT_IDENTIFY) + screen_redraw_draw_number(c, wp, top); + } +} + +/* Draw the status line. */ +void +screen_redraw_draw_status(struct client *c, u_int top) +{ + struct tty *tty = &c->tty; + + if (top) + tty_draw_line(tty, NULL, &c->status, 0, 0, 0); + else + tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1); +} + /* Draw number on a pane. */ void -screen_redraw_draw_number(struct client *c, struct window_pane *wp) +screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) { struct tty *tty = &c->tty; struct session *s = c->session; - struct options *oo = &s->options; + struct options *oo = s->options; struct window *w = wp->window; struct grid_cell gc; u_int idx, px, py, i, j, xoff, yoff; @@ -360,6 +412,9 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) px = wp->sx / 2; py = wp->sy / 2; xoff = wp->xoff; yoff = wp->yoff; + if (top) + yoff++; + if (wp->sx < len * 6 || wp->sy < 5) { tty_cursor(tty, xoff + px - len / 2, yoff + py); goto draw_text; @@ -368,12 +423,12 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) px -= len * 3; py -= 2; - memcpy(&gc, &grid_marker_cell, sizeof gc); + memcpy(&gc, &grid_default_cell, sizeof gc); if (w->active == wp) colour_set_bg(&gc, active_colour); else colour_set_bg(&gc, colour); - tty_attributes(tty, &gc); + tty_attributes(tty, &gc, wp); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -382,7 +437,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) for (j = 0; j < 5; j++) { for (i = px; i < px + 5; i++) { tty_cursor(tty, xoff + i, yoff + py + j); - if (clock_table[idx][j][i - px]) + if (window_clock_table[idx][j][i - px]) tty_putc(tty, ' '); } } @@ -395,12 +450,12 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) tty_cursor(tty, xoff + wp->sx - len, yoff); draw_text: - memcpy(&gc, &grid_marker_cell, sizeof gc); + memcpy(&gc, &grid_default_cell, sizeof gc); if (w->active == wp) colour_set_fg(&gc, active_colour); else colour_set_fg(&gc, colour); - tty_attributes(tty, &gc); + tty_attributes(tty, &gc, wp); tty_puts(tty, buf); tty_cursor(tty, 0, 0); diff --git a/screen-write.c b/screen-write.c index 3e836938..e53d3799 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -25,13 +25,13 @@ void screen_write_initctx(struct screen_write_ctx *, struct tty_ctx *, int); void screen_write_overwrite(struct screen_write_ctx *, u_int); -int screen_write_combine( - struct screen_write_ctx *, const struct utf8_data *); +int screen_write_combine(struct screen_write_ctx *, + const struct utf8_data *); /* Initialise writing with a window. */ void -screen_write_start( - struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) +screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, + struct screen *s) { ctx->wp = wp; if (wp != NULL && s == NULL) @@ -42,11 +42,10 @@ screen_write_start( /* Finish writing. */ void -screen_write_stop(unused struct screen_write_ctx *ctx) +screen_write_stop(__unused struct screen_write_ctx *ctx) { } - /* Reset screen state. */ void screen_write_reset(struct screen_write_ctx *ctx) @@ -56,7 +55,7 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_reset_tabs(s); screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); - s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD); + s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); @@ -65,15 +64,16 @@ screen_write_reset(struct screen_write_ctx *ctx) /* Write character. */ void -screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch) +screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, + u_char ch) { - grid_cell_one(gc, ch); + utf8_set(&gc->data, ch); screen_write_cell(ctx, gc); } /* Calculate string length, with embedded formatting. */ -size_t printflike2 -screen_write_cstrlen(int utf8flag, const char *fmt, ...) +size_t +screen_write_cstrlen(const char *fmt, ...) { va_list ap; char *msg, *msg2, *ptr, *ptr2; @@ -98,7 +98,7 @@ screen_write_cstrlen(int utf8flag, const char *fmt, ...) } *ptr2 = '\0'; - size = screen_write_strlen(utf8flag, "%s", msg2); + size = screen_write_strlen("%s", msg2); free(msg); free(msg2); @@ -107,14 +107,15 @@ screen_write_cstrlen(int utf8flag, const char *fmt, ...) } /* Calculate string length. */ -size_t printflike2 -screen_write_strlen(int utf8flag, const char *fmt, ...) +size_t +screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; - struct utf8_data utf8data; + struct utf8_data ud; u_char *ptr; size_t left, size = 0; + enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -122,19 +123,21 @@ screen_write_strlen(int utf8flag, const char *fmt, ...) ptr = msg; while (*ptr != '\0') { - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < (size_t)ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - size += utf8data.width; + if (more == UTF8_DONE) + size += ud.width; } else { - size++; + if (*ptr > 0x1f && *ptr < 0x7f) + size++; ptr++; } } @@ -144,71 +147,74 @@ screen_write_strlen(int utf8flag, const char *fmt, ...) } /* Write simple string (no UTF-8 or maximum length). */ -void printflike3 -screen_write_puts( - struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...) +void +screen_write_puts(struct screen_write_ctx *ctx, struct grid_cell *gc, + const char *fmt, ...) { va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, -1, gc, 0, fmt, ap); + screen_write_vnputs(ctx, -1, gc, fmt, ap); va_end(ap); } /* Write string with length limit (-1 for unlimited). */ -void printflike5 -screen_write_nputs(struct screen_write_ctx *ctx, - ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) +void +screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, + struct grid_cell *gc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap); + screen_write_vnputs(ctx, maxlen, gc, fmt, ap); va_end(ap); } void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap) + struct grid_cell *gc, const char *fmt, va_list ap) { char *msg; - struct utf8_data utf8data; + struct utf8_data ud; u_char *ptr; size_t left, size = 0; + enum utf8_state more; xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < (size_t)ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (maxlen > 0 && - size + utf8data.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; + if (more == UTF8_DONE) { + if (maxlen > 0 && + size + ud.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; } - break; - } - size += utf8data.width; + size += ud.width; - grid_cell_set(gc, &utf8data); - screen_write_cell(ctx, gc); + utf8_copy(&gc->data, &ud); + screen_write_cell(ctx, gc); + } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; if (*ptr == '\001') gc->attr ^= GRID_ATTR_CHARSET; - else { + else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, gc, *ptr); } @@ -220,16 +226,17 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, } /* Write string, similar to nputs, but with embedded formatting (#[]). */ -void printflike5 -screen_write_cnputs(struct screen_write_ctx *ctx, - ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) +void +screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, + struct grid_cell *gc, const char *fmt, ...) { struct grid_cell lgc; - struct utf8_data utf8data; + struct utf8_data ud; va_list ap; char *msg; u_char *ptr, *last; size_t left, size = 0; + enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -248,39 +255,43 @@ screen_write_cnputs(struct screen_write_ctx *ctx, } *last = '\0'; - screen_write_parsestyle(gc, &lgc, ptr); + style_parse(gc, &lgc, ptr); ptr = last + 1; continue; } - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < (size_t)ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (maxlen > 0 && - size + utf8data.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; + if (more == UTF8_DONE) { + if (maxlen > 0 && + size + ud.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; } - break; - } - size += utf8data.width; + size += ud.width; - grid_cell_set(&lgc, &utf8data); - screen_write_cell(ctx, &lgc); + utf8_copy(&lgc.data, &ud); + screen_write_cell(ctx, &lgc); + } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; - size++; - screen_write_putc(ctx, &lgc, *ptr); + if (*ptr > 0x1f && *ptr < 0x7f) { + size++; + screen_write_putc(ctx, &lgc, *ptr); + } ptr++; } } @@ -288,89 +299,6 @@ screen_write_cnputs(struct screen_write_ctx *ctx, free(msg); } -/* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */ -void -screen_write_parsestyle( - struct grid_cell *defgc, struct grid_cell *gc, const char *in) -{ - const char delimiters[] = " ,"; - char tmp[32]; - int val; - size_t end; - u_char fg, bg, attr, flags; - - if (*in == '\0') - return; - if (strchr(delimiters, in[strlen(in) - 1]) != NULL) - return; - - fg = gc->fg; - bg = gc->bg; - attr = gc->attr; - flags = gc->flags; - do { - end = strcspn(in, delimiters); - if (end > (sizeof tmp) - 1) - return; - memcpy(tmp, in, end); - tmp[end] = '\0'; - - if (strcasecmp(tmp, "default") == 0) { - fg = defgc->fg; - bg = defgc->bg; - attr = defgc->attr; - flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); - flags |= - defgc->flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); - } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { - if ((val = colour_fromstring(tmp + 3)) == -1) - return; - if (*in == 'f' || *in == 'F') { - if (val != 8) { - if (val & 0x100) { - flags |= GRID_FLAG_FG256; - val &= ~0x100; - } else - flags &= ~GRID_FLAG_FG256; - fg = val; - } else { - fg = defgc->fg; - flags &= ~GRID_FLAG_FG256; - flags |= defgc->flags & GRID_FLAG_FG256; - } - } else if (*in == 'b' || *in == 'B') { - if (val != 8) { - if (val & 0x100) { - flags |= GRID_FLAG_BG256; - val &= ~0x100; - } else - flags &= ~GRID_FLAG_BG256; - bg = val; - } else { - bg = defgc->bg; - flags &= ~GRID_FLAG_BG256; - flags |= defgc->flags & GRID_FLAG_BG256; - } - } else - return; - } else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { - if ((val = attributes_fromstring(tmp + 2)) == -1) - return; - attr &= ~val; - } else { - if ((val = attributes_fromstring(tmp)) == -1) - return; - attr |= val; - } - - in += end + strspn(in + end, delimiters); - } while (*in != '\0'); - gc->fg = fg; - gc->bg = bg; - gc->attr = attr; - gc->flags = flags; -} - /* Copy from another screen. */ void screen_write_copy(struct screen_write_ctx *ctx, @@ -379,8 +307,7 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *s = ctx->s; struct grid *gd = src->grid; struct grid_line *gl; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int xx, yy, cx, cy, ax, bx; cx = s->cx; @@ -404,12 +331,8 @@ screen_write_copy(struct screen_write_ctx *ctx, bx = px + nx; for (xx = ax; xx < bx; xx++) { - if (xx >= gl->cellsize) - gc = &grid_default_cell; - else - gc = &gl->celldata[xx]; - grid_cell_get(gc, &ud); - screen_write_cell(ctx, gc); + grid_get_cell(gd, xx, yy, &gc); + screen_write_cell(ctx, &gc); } if (px + nx == gd->sx && px + nx > gl->cellsize) screen_write_clearendofline(ctx); @@ -422,12 +345,12 @@ screen_write_copy(struct screen_write_ctx *ctx, /* Set up context for TTY command. */ void -screen_write_initctx( - struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last) +screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, + int save_last) { struct screen *s = ctx->s; struct grid *gd = s->grid; - const struct grid_cell *gc; + struct grid_cell gc; u_int xx; ttyctx->wp = ctx->wp; @@ -442,14 +365,14 @@ screen_write_initctx( return; /* Save the last cell on the screen. */ - gc = &grid_default_cell; + memcpy(&gc, &grid_default_cell, sizeof gc); for (xx = 1; xx <= screen_size_x(s); xx++) { - gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, screen_size_x(s) - xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; } ttyctx->last_width = xx; - memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell); + memcpy(&ttyctx->last_cell, &gc, sizeof ttyctx->last_cell); } /* Set a mode. */ @@ -488,6 +411,8 @@ screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) if (ny > s->cy - s->rupper) ny = s->cy - s->rupper; } + if (s->cx == screen_size_x(s)) + s->cx--; if (ny == 0) return; @@ -512,6 +437,8 @@ screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) if (ny > s->rlower - s->cy) ny = s->rlower - s->cy; } + if (s->cx == screen_size_x(s)) + s->cx--; if (ny == 0) return; @@ -583,7 +510,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) screen_write_initctx(ctx, &ttyctx, 0); memcpy(&gc, &grid_default_cell, sizeof gc); - grid_cell_one(&gc, 'E'); + utf8_set(&gc.data, 'E'); for (yy = 0; yy < screen_size_y(s); yy++) { for (xx = 0; xx < screen_size_x(s); xx++) @@ -840,8 +767,8 @@ screen_write_reverseindex(struct screen_write_ctx *ctx) /* Set scroll region. */ void -screen_write_scrollregion( - struct screen_write_ctx *ctx, u_int rupper, u_int rlower) +screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, + u_int rlower) { struct screen *s = ctx->s; @@ -947,16 +874,16 @@ screen_write_clearscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; + u_int sx = screen_size_x(s); + u_int sy = screen_size_y(s); screen_write_initctx(ctx, &ttyctx, 0); /* Scroll into history if it is enabled. */ if (s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid); - else { - grid_view_clear( - s->grid, 0, 0, screen_size_x(s), screen_size_y(s)); - } + else + grid_view_clear(s->grid, 0, 0, sx, sy); tty_write(tty_cmd_clearscreen, &ttyctx); } @@ -980,14 +907,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int width, xx, last; - struct grid_cell tmp_gc, *tmp_gcp; - struct utf8_data ud; + struct grid_cell tmp_gc; int insert; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; - width = grid_cell_width(gc); + width = gc->data.width; /* * If this is a wide character and there is no room on the screen, for @@ -1004,8 +930,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * there is space. */ if (width == 0) { - grid_cell_get(gc, &ud); - if (screen_write_combine(ctx, &ud) == 0) { + if (screen_write_combine(ctx, &gc->data) == 0) { screen_write_initctx(ctx, &ttyctx, 0); tty_write(tty_cmd_utf8character, &ttyctx); } @@ -1040,11 +965,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ - for (xx = s->cx + 1; xx < s->cx + width; xx++) { - tmp_gcp = grid_view_get_cell(gd, xx, s->cy); - if (tmp_gcp != NULL) - tmp_gcp->flags |= GRID_FLAG_PADDING; - } + memcpy(&tmp_gc, &grid_default_cell, sizeof tmp_gc); + tmp_gc.flags |= GRID_FLAG_PADDING; + tmp_gc.data.width = 0; + for (xx = s->cx + 1; xx < s->cx + width; xx++) + grid_view_set_cell(gd, xx, s->cy, &tmp_gc); /* Set the cell. */ grid_view_set_cell(gd, s->cx, s->cy, gc); @@ -1066,8 +991,9 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } if (screen_check_selection(s, s->cx - width, s->cy)) { memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); - grid_cell_get(gc, &ud); - grid_cell_set(&tmp_gc, &ud); + utf8_copy(&tmp_gc.data, &gc->data); + tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; + tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmp_gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); @@ -1085,8 +1011,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) { struct screen *s = ctx->s; struct grid *gd = s->grid; - struct grid_cell *gc; - struct utf8_data ud1; + struct grid_cell gc; /* Can't combine if at 0. */ if (s->cx == 0) @@ -1097,17 +1022,18 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) fatalx("UTF-8 data empty"); /* Retrieve the previous cell. */ - gc = grid_view_get_cell(gd, s->cx - 1, s->cy); - grid_cell_get(gc, &ud1); + grid_view_get_cell(gd, s->cx - 1, s->cy, &gc); /* Check there is enough space. */ - if (ud1.size + ud->size > sizeof ud1.data) + if (gc.data.size + ud->size > sizeof gc.data.data) return (-1); - /* Append the data and set the cell. */ - memcpy(ud1.data + ud1.size, ud->data, ud->size); - ud1.size += ud->size; - grid_cell_set(gc, &ud1); + /* Append the data. */ + memcpy(gc.data.data + gc.data.size, ud->data, ud->size); + gc.data.size += ud->size; + + /* Set the new cell. */ + grid_view_set_cell(gd, s->cx - 1, s->cy, &gc); return (0); } @@ -1126,11 +1052,11 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) { struct screen *s = ctx->s; struct grid *gd = s->grid; - const struct grid_cell *gc; + struct grid_cell gc; u_int xx; - gc = grid_view_peek_cell(gd, s->cx, s->cy); - if (gc->flags & GRID_FLAG_PADDING) { + grid_view_get_cell(gd, s->cx, s->cy, &gc); + if (gc.flags & GRID_FLAG_PADDING) { /* * A padding cell, so clear any following and leading padding * cells back to the character. Don't overwrite the current @@ -1138,8 +1064,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) */ xx = s->cx + 1; while (--xx > 0) { - gc = grid_view_peek_cell(gd, xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } @@ -1154,8 +1080,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) */ xx = s->cx + width - 1; while (++xx < screen_size_x(s)) { - gc = grid_view_peek_cell(gd, xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } diff --git a/screen.c b/screen.c index e92c6aa7..5551bb93 100644 --- a/screen.c +++ b/screen.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,7 +18,6 @@ #include -#include #include #include #include @@ -32,14 +31,8 @@ void screen_resize_y(struct screen *, u_int); void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) { - char hn[MAXHOSTNAMELEN]; - s->grid = grid_create(sx, sy, hlimit); - - if (gethostname(hn, MAXHOSTNAMELEN) == 0) - s->title = xstrdup(hn); - else - s->title = xstrdup(""); + s->title = xstrdup(""); s->cstyle = 0; s->ccolour = xstrdup(""); @@ -111,12 +104,8 @@ screen_set_cursor_colour(struct screen *s, const char *colour_string) void screen_set_title(struct screen *s, const char *title) { - char tmp[BUFSIZ]; - - strlcpy(tmp, title, sizeof tmp); - free(s->title); - s->title = xstrdup(tmp); + s->title = xstrdup(title); } /* Resize screen. */ @@ -205,8 +194,6 @@ screen_resize_y(struct screen *s, u_int sy) * Now just increase the history size, if possible, to take * over the lines which are left. If history is off, delete * lines from the top. - * - * XXX Should apply history limit? */ available = s->cy; if (gd->flags & GRID_HISTORY) @@ -220,8 +207,8 @@ screen_resize_y(struct screen *s, u_int sy) } /* Resize line arrays. */ - gd->linedata = xrealloc( - gd->linedata, gd->hsize + sy, sizeof *gd->linedata); + gd->linedata = xreallocarray(gd->linedata, gd->hsize + sy, + sizeof *gd->linedata); /* Size increasing. */ if (sy > oldy) { @@ -274,6 +261,7 @@ screen_clear_selection(struct screen *s) struct screen_sel *sel = &s->sel; sel->flag = 0; + sel->lineflag = LINE_SEL_NONE; } /* Check if cell in selection. */ @@ -281,6 +269,7 @@ int screen_check_selection(struct screen *s, u_int px, u_int py) { struct screen_sel *sel = &s->sel; + u_int xx; if (!sel->flag) return (0); @@ -330,16 +319,24 @@ screen_check_selection(struct screen *s, u_int px, u_int py) if (py < sel->sy || py > sel->ey) return (0); - if ((py == sel->sy && px < sel->sx) - || (py == sel->ey && px > sel->ex)) + if (py == sel->sy && px < sel->sx) + return (0); + + if (py == sel->ey && px > sel->ex) return (0); } else if (sel->sy > sel->ey) { /* starting line > ending line -- upward selection. */ if (py > sel->sy || py < sel->ey) return (0); - if ((py == sel->sy && px >= sel->sx) - || (py == sel->ey && px < sel->ex)) + if (py == sel->ey && px < sel->ex) + return (0); + + if (sel->modekeys == MODEKEY_EMACS) + xx = sel->sx - 1; + else + xx = sel->sx; + if (py == sel->sy && px > xx) return (0); } else { /* starting line == ending line. */ @@ -348,7 +345,11 @@ screen_check_selection(struct screen *s, u_int px, u_int py) if (sel->ex < sel->sx) { /* cursor (ex) is on the left */ - if (px > sel->sx || px < sel->ex) + if (sel->modekeys == MODEKEY_EMACS) + xx = sel->sx - 1; + else + xx = sel->sx; + if (px > xx || px < sel->ex) return (0); } else { /* selection start (sx) is on the left */ @@ -366,7 +367,13 @@ void screen_reflow(struct screen *s, u_int new_x) { struct grid *old = s->grid; + u_int change; s->grid = grid_create(old->sx, old->sy, old->hlimit); - s->cy -= grid_reflow(s->grid, old, new_x); + + change = grid_reflow(s->grid, old, new_x); + if (change < s->cy) + s->cy -= change; + else + s->cy = 0; } diff --git a/server-client.c b/server-client.c index 1a7e3ca0..5565e1ab 100644 --- a/server-client.c +++ b/server-client.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -18,7 +18,9 @@ #include #include +#include +#include #include #include #include @@ -29,46 +31,98 @@ #include "tmux.h" #include "tmate.h" -void server_client_check_focus(struct window_pane *); -void server_client_check_resize(struct window_pane *); -void server_client_check_mouse(struct client *, struct window_pane *); -void server_client_repeat_timer(int, short, void *); -void server_client_check_exit(struct client *); -void server_client_check_redraw(struct client *); -void server_client_set_title(struct client *); -void server_client_reset_state(struct client *); -int server_client_assume_paste(struct session *); +void server_client_free(int, short, void *); +void server_client_check_focus(struct window_pane *); +void server_client_check_resize(struct window_pane *); +key_code server_client_check_mouse(struct client *); +void server_client_repeat_timer(int, short, void *); +void server_client_check_exit(struct client *); +void server_client_check_redraw(struct client *); +void server_client_set_title(struct client *); +void server_client_reset_state(struct client *); +int server_client_assume_paste(struct session *); -int server_client_msg_dispatch(struct client *); -void server_client_msg_command(struct client *, struct msg_command_data *); -void server_client_msg_identify( - struct client *, struct msg_identify_data *, int); -void server_client_msg_shell(struct client *); +void server_client_dispatch(struct imsg *, void *); +void server_client_dispatch_command(struct client *, struct imsg *); +void server_client_dispatch_identify(struct client *, struct imsg *); +void server_client_dispatch_shell(struct client *); + +/* Check if this client is inside this server. */ +int +server_client_check_nested(struct client *c) +{ + struct environ_entry *envent; + struct window_pane *wp; + + if (c->tty.path == NULL) + return (0); + + envent = environ_find(c->environ, "TMUX"); + if (envent == NULL || *envent->value == '\0') + return (0); + + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + if (strcmp(wp->tty, c->tty.path) == 0) + return (1); + } + return (0); +} + +/* Set client key table. */ +void +server_client_set_key_table(struct client *c, const char *name) +{ + if (name == NULL) + name = server_client_get_key_table(c); + + key_bindings_unref_table(c->keytable); + c->keytable = key_bindings_get_table(name, 1); + c->keytable->references++; +} + +/* Get default key table. */ +const char * +server_client_get_key_table(struct client *c) +{ + struct session *s = c->session; + const char *name; + + if (s == NULL) + return ("root"); + + name = options_get_string(s->options, "key-table"); + if (*name == '\0') + return ("root"); + return (name); +} /* Create a new client. */ void server_client_create(int fd) { struct client *c; - u_int i; setblocking(fd, 0); c = xcalloc(1, sizeof *c); - c->references = 0; - imsg_init(&c->ibuf, fd); - server_update_event(c); + c->references = 1; + c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c); if (gettimeofday(&c->creation_time, NULL) != 0) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); + c->environ = environ_create(); + + c->fd = -1; + c->cwd = NULL; + c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; - c->stdin_data = evbuffer_new (); - c->stdout_data = evbuffer_new (); - c->stderr_data = evbuffer_new (); + c->stdin_data = evbuffer_new(); + c->stdout_data = evbuffer_new(); + c->stderr_data = evbuffer_new(); c->tty.fd = -1; c->title = NULL; @@ -79,54 +133,43 @@ server_client_create(int fd) c->tty.sy = 24; screen_init(&c->status, c->tty.sx, 1, 0); - RB_INIT(&c->status_new); - RB_INIT(&c->status_old); c->message_string = NULL; - ARRAY_INIT(&c->message_log); + TAILQ_INIT(&c->message_log); c->prompt_string = NULL; c->prompt_buffer = NULL; c->prompt_index = 0; - c->tty.mouse.xb = c->tty.mouse.button = 3; - c->tty.mouse.x = c->tty.mouse.y = -1; - c->tty.mouse.lx = c->tty.mouse.ly = -1; - c->tty.mouse.sx = c->tty.mouse.sy = -1; - c->tty.mouse.event = MOUSE_EVENT_UP; - c->tty.mouse.flags = 0; - c->flags |= CLIENT_FOCUSED; + c->keytable = key_bindings_get_table("root", 1); + c->keytable->references++; + evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) == NULL) { - ARRAY_SET(&clients, i, c); - return; - } - } - ARRAY_ADD(&clients, c); - log_debug("new client %d", fd); + TAILQ_INSERT_TAIL(&clients, c, entry); + log_debug("new client %p", c); } /* Open client terminal if needed. */ int -server_client_open(struct client *c, struct session *s, char **cause) +server_client_open(struct client *c, char **cause) { - struct options *oo = s != NULL ? &s->options : &global_s_options; - char *overrides; - if (c->flags & CLIENT_CONTROL) return (0); - if (!(c->flags & CLIENT_TERMINAL)) { - *cause = xstrdup ("not a terminal"); + if (strcmp(c->ttyname, "/dev/tty") == 0) { + *cause = xstrdup("can't use /dev/tty"); return (-1); } - overrides = options_get_string(oo, "terminal-overrides"); - if (tty_open(&c->tty, overrides, cause) != 0) + if (!(c->flags & CLIENT_TERMINAL)) { + *cause = xstrdup("not a terminal"); + return (-1); + } + + if (tty_open(&c->tty, cause) != 0) return (-1); return (0); @@ -136,14 +179,18 @@ server_client_open(struct client *c, struct session *s, char **cause) void server_client_lost(struct client *c) { - struct message_entry *msg; - u_int i; + struct message_entry *msg, *msg1; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) == c) - ARRAY_SET(&clients, i, NULL); - } - log_debug("lost client %d", c->ibuf.fd); + c->flags |= CLIENT_DEAD; + + status_prompt_clear(c); + status_message_clear(c); + + if (c->stdin_callback != NULL) + c->stdin_callback(c, 1, c->stdin_callback_data); + + TAILQ_REMOVE(&clients, c, entry); + log_debug("lost client %p", c); /* * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called @@ -151,56 +198,50 @@ server_client_lost(struct client *c) */ if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); + free(c->ttyname); + free(c->term); - evbuffer_free (c->stdin_data); - evbuffer_free (c->stdout_data); + evbuffer_free(c->stdin_data); + evbuffer_free(c->stdout_data); if (c->stderr_data != c->stdout_data) - evbuffer_free (c->stderr_data); + evbuffer_free(c->stderr_data); - status_free_jobs(&c->status_new); - status_free_jobs(&c->status_old); + if (event_initialized(&c->status_timer)) + evtimer_del(&c->status_timer); screen_free(&c->status); free(c->title); + free((void *)c->cwd); evtimer_del(&c->repeat_timer); + key_bindings_unref_table(c->keytable); + if (event_initialized(&c->identify_timer)) evtimer_del(&c->identify_timer); free(c->message_string); - if (event_initialized (&c->message_timer)) + if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); - for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { - msg = &ARRAY_ITEM(&c->message_log, i); + TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { free(msg->msg); + TAILQ_REMOVE(&c->message_log, msg, entry); + free(msg); } - ARRAY_FREE(&c->message_log); free(c->prompt_string); free(c->prompt_buffer); - free(c->cwd); - c->cmdq->dead = 1; + c->cmdq->flags |= CMD_Q_DEAD; cmdq_free(c->cmdq); c->cmdq = NULL; - environ_free(&c->environ); + environ_free(c->environ); - close(c->ibuf.fd); - imsg_clear(&c->ibuf); - if (event_initialized(&c->event)) - event_del(&c->event); + proc_remove_peer(c->peer); + c->peer = NULL; - for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { - if (ARRAY_ITEM(&dead_clients, i) == NULL) { - ARRAY_SET(&dead_clients, i, c); - break; - } - } - if (i == ARRAY_LENGTH(&dead_clients)) - ARRAY_ADD(&dead_clients, c); - c->flags |= CLIENT_DEAD; + server_client_unref(c); server_add_accept(0); /* may be more file descriptors now */ @@ -209,133 +250,268 @@ server_client_lost(struct client *c) server_update_socket(); } -/* Process a single client event. */ +/* Remove reference from a client. */ void -server_client_callback(int fd, short events, void *data) +server_client_unref(struct client *c) { - struct client *c = data; + log_debug("unref client %p (%d references)", c, c->references); - if (c->flags & CLIENT_DEAD) - return; - - if (fd == c->ibuf.fd) { - if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0) - goto client_lost; - - if (c->flags & CLIENT_BAD) { - if (c->ibuf.w.queued == 0) - goto client_lost; - return; - } - - if (events & EV_READ && server_client_msg_dispatch(c) != 0) - goto client_lost; - } - - server_push_stdout(c); - server_push_stderr(c); - - server_update_event(c); - return; - -client_lost: - server_client_lost(c); + c->references--; + if (c->references == 0) + event_once(-1, EV_TIMEOUT, server_client_free, c, NULL); } -/* Handle client status timer. */ +/* Free dead client. */ void -server_client_status_timer(void) +server_client_free(__unused int fd, __unused short events, void *arg) { - struct client *c; - struct session *s; - struct timeval tv; - u_int i; - int interval; - time_t difference; + struct client *c = arg; - if (gettimeofday(&tv, NULL) != 0) - fatal("gettimeofday failed"); + log_debug("free client %p (%d references)", c, c->references); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - if (c->message_string != NULL || c->prompt_string != NULL) { - /* - * Don't need timed redraw for messages/prompts so bail - * now. The status timer isn't reset when they are - * redrawn anyway. - */ - continue; - } - s = c->session; + if (c->references == 0) + free(c); +} - if (!options_get_number(&s->options, "status")) -#ifdef TMATE - if (!(c->flags & CLIENT_FORCE_STATUS)) -#endif - continue; - interval = options_get_number(&s->options, "status-interval"); +/* Detach a client. */ +void +server_client_detach(struct client *c, enum msgtype msgtype) +{ + struct session *s = c->session; - difference = tv.tv_sec - c->status_timer.tv_sec; - if (difference >= interval) { - status_update_jobs(c); - c->flags |= CLIENT_STATUS; - } - } + if (s == NULL) + return; + + hooks_run(c->session->hooks, c, NULL, "client-detached"); + proc_send_s(c->peer, msgtype, s->name); } /* Check for mouse keys. */ -void -server_client_check_mouse(struct client *c, struct window_pane *wp) +key_code +server_client_check_mouse(struct client *c) { - struct session *s = c->session; - struct options *oo = &s->options; - struct mouse_event *m = &c->tty.mouse; - int statusat; + struct session *s = c->session; + struct mouse_event *m = &c->tty.mouse; + struct window *w; + struct window_pane *wp; + enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE; + enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; + u_int x, y, b; + key_code key; - statusat = status_at_line(c); + log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y, + m->lx, m->ly, c->tty.mouse_drag_flag); - /* Is this a window selection click on the status line? */ - if (statusat != -1 && m->y == (u_int)statusat && - options_get_number(oo, "mouse-select-window")) { - if (m->event & MOUSE_EVENT_CLICK) { - status_set_window_at(c, m->x); - } else if (m->event == MOUSE_EVENT_WHEEL) { - if (m->wheel == MOUSE_WHEEL_UP) - session_previous(c->session, 0); - else if (m->wheel == MOUSE_WHEEL_DOWN) - session_next(c->session, 0); - server_redraw_session(s); + /* What type of event is this? */ + if (MOUSE_DRAG(m->b)) { + type = DRAG; + if (c->tty.mouse_drag_flag) { + x = m->x, y = m->y, b = m->b; + log_debug("drag update at %u,%u", x, y); + } else { + x = m->lx, y = m->ly, b = m->lb; + log_debug("drag start at %u,%u", x, y); } - recalculate_sizes(); - return; + } else if (MOUSE_WHEEL(m->b)) { + type = WHEEL; + x = m->x, y = m->y, b = m->b; + log_debug("wheel at %u,%u", x, y); + } else if (MOUSE_BUTTONS(m->b) == 3) { + type = UP; + x = m->x, y = m->y, b = m->lb; + log_debug("up at %u,%u", x, y); + } else { + type = DOWN; + x = m->x, y = m->y, b = m->b; + log_debug("down at %u,%u", x, y); + } + if (type == NOTYPE) + return (KEYC_UNKNOWN); + + /* Always save the session. */ + m->s = s->id; + + /* Is this on the status line? */ + m->statusat = status_at_line(c); + if (m->statusat != -1 && y == (u_int)m->statusat) { + w = status_get_window_at(c, x); + if (w == NULL) + return (KEYC_UNKNOWN); + m->w = w->id; + where = STATUS; + } else + m->w = -1; + + /* Not on status line. Adjust position and check for border or pane. */ + if (where == NOWHERE) { + if (m->statusat == 0 && y > 0) + y--; + else if (m->statusat > 0 && y >= (u_int)m->statusat) + y = m->statusat - 1; + + TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { + if ((wp->xoff + wp->sx == x && + wp->yoff <= 1 + y && + wp->yoff + wp->sy >= y) || + (wp->yoff + wp->sy == y && + wp->xoff <= 1 + x && + wp->xoff + wp->sx >= x)) + break; + } + if (wp != NULL) + where = BORDER; + else { + wp = window_get_active_at(s->curw->window, x, y); + if (wp != NULL) { + where = PANE; + log_debug("mouse at %u,%u is on pane %%%u", + x, y, wp->id); + } + } + if (where == NOWHERE) + return (KEYC_UNKNOWN); + m->wp = wp->id; + m->w = wp->window->id; + } else + m->wp = -1; + + /* Stop dragging if needed. */ + if (type != DRAG && c->tty.mouse_drag_flag) { + if (c->tty.mouse_drag_release != NULL) + c->tty.mouse_drag_release(c, m); + + c->tty.mouse_drag_update = NULL; + c->tty.mouse_drag_release = NULL; + + c->tty.mouse_drag_flag = 0; + return (KEYC_MOUSE); /* not a key, but still may want to pass */ } - /* - * Not on status line - adjust mouse position if status line is at the - * top and limit if at the bottom. From here on a struct mouse - * represents the offset onto the window itself. - */ - if (statusat == 0 && m->y > 0) - m->y--; - else if (statusat > 0 && m->y >= (u_int)statusat) - m->y = statusat - 1; + /* Convert to a key binding. */ + key = KEYC_UNKNOWN; + switch (type) { + case NOTYPE: + break; + case DRAG: + if (c->tty.mouse_drag_update != NULL) + c->tty.mouse_drag_update(c, m); + else { + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_MOUSEDRAG1_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAG1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_MOUSEDRAG2_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAG2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEDRAG3_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAG3_BORDER; + break; + } + } - /* Is this a pane selection? Allow down only in copy mode. */ - if (options_get_number(oo, "mouse-select-pane") && - (m->event == MOUSE_EVENT_DOWN || wp->mode != &window_copy_mode)) { - window_set_active_at(wp->window, m->x, m->y); - server_redraw_window_borders(wp->window); - wp = wp->window->active; /* may have changed */ + c->tty.mouse_drag_flag = 1; + break; + case WHEEL: + if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { + if (where == PANE) + key = KEYC_WHEELUP_PANE; + if (where == STATUS) + key = KEYC_WHEELUP_STATUS; + if (where == BORDER) + key = KEYC_WHEELUP_BORDER; + } else { + if (where == PANE) + key = KEYC_WHEELDOWN_PANE; + if (where == STATUS) + key = KEYC_WHEELDOWN_STATUS; + if (where == BORDER) + key = KEYC_WHEELDOWN_BORDER; + } + break; + case UP: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_MOUSEUP1_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_MOUSEUP2_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEUP3_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP3_BORDER; + break; + } + break; + case DOWN: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_MOUSEDOWN1_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDOWN1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_MOUSEDOWN2_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDOWN2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEDOWN3_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDOWN3_BORDER; + break; + } + break; } + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); - /* Check if trying to resize pane. */ - if (options_get_number(oo, "mouse-resize-pane")) - layout_resize_pane_mouse(c); + /* Apply modifiers if any. */ + if (b & MOUSE_MASK_META) + key |= KEYC_ESCAPE; + if (b & MOUSE_MASK_CTRL) + key |= KEYC_CTRL; + if (b & MOUSE_MASK_SHIFT) + key |= KEYC_SHIFT; - /* Update last and pass through to client. */ - window_pane_mouse(wp, c->session, m); + return (key); } /* Is this fast enough to probably be a paste? */ @@ -345,46 +521,47 @@ server_client_assume_paste(struct session *s) struct timeval tv; int t; - if ((t = options_get_number(&s->options, "assume-paste-time")) == 0) + if ((t = options_get_number(s->options, "assume-paste-time")) == 0) return (0); timersub(&s->activity_time, &s->last_activity_time, &tv); - if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) - return (1); + if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) { + log_debug("session %s pasting (flag %d)", s->name, + !!(s->flags & SESSION_PASTING)); + if (s->flags & SESSION_PASTING) + return (1); + s->flags |= SESSION_PASTING; + return (0); + } + log_debug("session %s not pasting", s->name); + s->flags &= ~SESSION_PASTING; return (0); } /* Handle data key input from client. */ void -server_client_handle_key(struct client *c, int key) +server_client_handle_key(struct client *c, key_code key) { - struct session *s; + struct mouse_event *m = &c->tty.mouse; + struct session *s = c->session; struct window *w; struct window_pane *wp; struct timeval tv; - struct key_binding *bd; - int xtimeout, isprefix, ispaste; + struct key_table *table; + struct key_binding bd_find, *bd; + int xtimeout; /* Check the client is good to accept input. */ - if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; - - if (c->session == NULL) - return; - s = c->session; + w = s->curw->window; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); + session_update_activity(s, &c->activity_time); - memcpy(&s->last_activity_time, &s->activity_time, - sizeof s->last_activity_time); - memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); - - w = c->session->curw->window; - wp = w->active; - - /* Special case: number keys jump to pane in identify mode. */ + /* Number keys jump to pane in identify mode. */ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { if (c->flags & CLIENT_READONLY) return; @@ -414,78 +591,108 @@ server_client_handle_key(struct client *c, int key) if (key == KEYC_MOUSE) { if (c->flags & CLIENT_READONLY) return; - server_client_check_mouse(c, wp); - return; - } - - /* Is this a prefix key? */ - if (key == options_get_number(&s->options, "prefix")) - isprefix = 1; - else if (key == options_get_number(&s->options, "prefix2")) - isprefix = 1; - else - isprefix = 0; - - /* Treat prefix as a regular key when pasting is detected. */ - ispaste = server_client_assume_paste(s); - if (ispaste) - isprefix = 0; - - /* No previous prefix key. */ - if (!(c->flags & CLIENT_PREFIX)) { - if (isprefix) { - c->flags |= CLIENT_PREFIX; - server_status_client(c); + key = server_client_check_mouse(c); + if (key == KEYC_UNKNOWN) return; - } - /* Try as a non-prefix key binding. */ - if (ispaste || (bd = key_bindings_lookup(key)) == NULL) { - if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); - } else - key_bindings_dispatch(bd, c); - return; - } + m->valid = 1; + m->key = key; - /* Prefix key already pressed. Reset prefix and lookup key. */ - c->flags &= ~CLIENT_PREFIX; - server_status_client(c); - if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { - /* If repeating, treat this as a key, else ignore. */ - if (c->flags & CLIENT_REPEAT) { + if (!options_get_number(s->options, "mouse")) + goto forward; + } else + m->valid = 0; + + /* Treat everything as a regular key when pasting is detected. */ + if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) + goto forward; + +retry: + /* Try to see if there is a key binding in the current table. */ + bd_find.key = key; + bd = RB_FIND(key_bindings, &c->keytable->key_bindings, &bd_find); + if (bd != NULL) { + /* + * Key was matched in this table. If currently repeating but a + * non-repeating binding was found, stop repeating and try + * again in the root table. + */ + if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); + server_status_client(c); + goto retry; } + + /* + * Take a reference to this table to make sure the key binding + * doesn't disappear. + */ + table = c->keytable; + table->references++; + + /* + * If this is a repeating key, start the timer. Otherwise reset + * the client back to the root table. + */ + xtimeout = options_get_number(s->options, "repeat-time"); + if (xtimeout != 0 && bd->can_repeat) { + c->flags |= CLIENT_REPEAT; + + tv.tv_sec = xtimeout / 1000; + tv.tv_usec = (xtimeout % 1000) * 1000L; + evtimer_del(&c->repeat_timer); + evtimer_add(&c->repeat_timer, &tv); + } else { + c->flags &= ~CLIENT_REPEAT; + server_client_set_key_table(c, NULL); + } + server_status_client(c); + + /* Dispatch the key binding. */ + key_bindings_dispatch(bd, c, m); + key_bindings_unref_table(table); return; } - /* If already repeating, but this key can't repeat, skip it. */ - if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { + /* + * No match in this table. If repeating, switch the client back to the + * root table and try again. + */ + if (c->flags & CLIENT_REPEAT) { + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); + server_status_client(c); + goto retry; + } + + /* If no match and we're not in the root table, that's it. */ + if (strcmp(c->keytable->name, server_client_get_key_table(c)) != 0) { + server_client_set_key_table(c, NULL); + server_status_client(c); return; } - /* If this key can repeat, reset the repeat flags and timer. */ - xtimeout = options_get_number(&s->options, "repeat-time"); - if (xtimeout != 0 && bd->can_repeat) { - c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; - - tv.tv_sec = xtimeout / 1000; - tv.tv_usec = (xtimeout % 1000) * 1000L; - evtimer_del(&c->repeat_timer); - evtimer_add(&c->repeat_timer, &tv); + /* + * No match, but in the root table. Prefix switches to the prefix table + * and everything else is passed through. + */ + if (key == (key_code)options_get_number(s->options, "prefix") || + key == (key_code)options_get_number(s->options, "prefix2")) { + server_client_set_key_table(c, "prefix"); + server_status_client(c); + return; } - /* Dispatch the command. */ - key_bindings_dispatch(bd, c); +forward: + if (c->flags & CLIENT_READONLY) + return; + if (KEYC_IS_MOUSE(key)) + wp = cmd_mouse_pane(m, NULL, NULL); + else + wp = w->active; + if (wp != NULL) + window_pane_key(wp, c, s, key, m); } /* Client functions that need to happen every loop. */ @@ -495,15 +702,11 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; - u_int i; - +#ifdef TMATE int tmate_should_sync_layout = 0; +#endif - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL) - continue; - + TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); if (c->session != NULL) { server_client_check_redraw(c); @@ -515,24 +718,27 @@ server_client_loop(void) * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. */ - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) - continue; - + RB_FOREACH(w, windows, &windows) { +#ifdef TMATE if (w->flags & WINDOW_REDRAW) tmate_should_sync_layout = 1; +#endif w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { - server_client_check_focus(wp); - server_client_check_resize(wp); + if (wp->fd != -1) { + server_client_check_focus(wp); + server_client_check_resize(wp); + } wp->flags &= ~PANE_REDRAW; } + check_window_name(w); } +#ifdef TMATE if (tmate_should_sync_layout) tmate_sync_layout(); +#endif } /* Check if pane should be resized. */ @@ -541,7 +747,7 @@ server_client_check_resize(struct window_pane *wp) { struct winsize ws; - if (wp->fd == -1 || !(wp->flags & PANE_RESIZE)) + if (!(wp->flags & PANE_RESIZE)) return; memset(&ws, 0, sizeof ws); @@ -556,7 +762,7 @@ server_client_check_resize(struct window_pane *wp) * other platforms and ignoring it doesn't seem to cause any * issues. */ - if (errno != EINVAL) + if (errno != EINVAL && errno != ENXIO) #endif fatal("ioctl failed"); } @@ -568,8 +774,16 @@ server_client_check_resize(struct window_pane *wp) void server_client_check_focus(struct window_pane *wp) { - u_int i; struct client *c; + int push; + + /* Are focus events off? */ + if (!options_get_number(global_options, "focus-events")) + return; + + /* Do we need to push the focus state? */ + push = wp->flags & PANE_FOCUSPUSH; + wp->flags &= ~PANE_FOCUSPUSH; /* If we don't care about focus, forget it. */ if (!(wp->base.mode & MODE_FOCUSON)) @@ -587,12 +801,8 @@ server_client_check_focus(struct window_pane *wp) * If our window is the current window in any focused clients with an * attached session, we're focused. */ - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - - if (!(c->flags & CLIENT_FOCUSED)) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) continue; if (c->session->flags & SESSION_UNATTACHED) continue; @@ -602,13 +812,13 @@ server_client_check_focus(struct window_pane *wp) } not_focused: - if (wp->flags & PANE_FOCUSED) + if (push || (wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[O", 3); wp->flags &= ~PANE_FOCUSED; return; focused: - if (!(wp->flags & PANE_FOCUSED)) + if (push || !(wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[I", 3); wp->flags |= PANE_FOCUSED; } @@ -628,8 +838,7 @@ server_client_reset_state(struct client *c) struct window *w = c->session->curw->window; struct window_pane *wp = w->active; struct screen *s = wp->screen; - struct options *oo = &c->session->options; - struct options *wo = &w->options; + struct options *oo = c->session->options; int status, mode, o; if (c->flags & CLIENT_SUSPENDED) @@ -648,47 +857,17 @@ server_client_reset_state(struct client *c) if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) tty_cursor(&c->tty, 0, 0); else { - o = status && options_get_number (oo, "status-position") == 0; + o = status && options_get_number(oo, "status-position") == 0; tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy); } /* - * Resizing panes with the mouse requires at least button mode to give - * a smooth appearance. + * Set mouse mode if requested. To support dragging, always use button + * mode. */ mode = s->mode; - if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) && - !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY))) - mode |= MODE_MOUSE_BUTTON; - - /* - * Any mode will do for mouse-select-pane, but set standard mode if - * none. - */ - if ((mode & ALL_MOUSE_MODES) == 0) { - if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL && - options_get_number(oo, "mouse-select-pane")) - mode |= MODE_MOUSE_STANDARD; - else if (options_get_number(oo, "mouse-resize-pane")) - mode |= MODE_MOUSE_STANDARD; - else if (options_get_number(oo, "mouse-select-window")) - mode |= MODE_MOUSE_STANDARD; - else if (options_get_number(wo, "mode-mouse")) - mode |= MODE_MOUSE_STANDARD; - } - - /* - * Set UTF-8 mouse input if required. If the terminal is UTF-8, the - * user has set mouse-utf8 and any mouse mode is in effect, turn on - * UTF-8 mouse input. If the receiving terminal hasn't requested it - * (that is, it isn't in s->mode), then it'll be converted in - * input_mouse. - */ - if ((c->tty.flags & TTY_UTF8) && - (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8")) - mode |= MODE_MOUSE_UTF8; - else - mode &= ~MODE_MOUSE_UTF8; + if (options_get_number(oo, "mouse")) + mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode, s); @@ -697,14 +876,14 @@ server_client_reset_state(struct client *c) /* Repeat time callback. */ void -server_client_repeat_timer(unused int fd, unused short events, void *data) +server_client_repeat_timer(__unused int fd, __unused short events, void *data) { struct client *c = data; if (c->flags & CLIENT_REPEAT) { - if (c->flags & CLIENT_PREFIX) - server_status_client(c); - c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); + server_client_set_key_table(c, NULL); + c->flags &= ~CLIENT_REPEAT; + server_status_client(c); } } @@ -712,8 +891,6 @@ server_client_repeat_timer(unused int fd, unused short events, void *data) void server_client_check_exit(struct client *c) { - struct msg_exit_data exitdata; - if (!(c->flags & CLIENT_EXIT)) return; @@ -724,9 +901,7 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; - exitdata.retcode = c->retcode; - server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); - + proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); c->flags &= ~CLIENT_EXIT; } @@ -735,17 +910,15 @@ void server_client_check_redraw(struct client *c) { struct session *s = c->session; + struct tty *tty = &c->tty; struct window_pane *wp; int flags, redraw; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; - flags = c->tty.flags & TTY_FREEZE; - c->tty.flags &= ~TTY_FREEZE; - if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { - if (options_get_number(&s->options, "set-titles")) + if (options_get_number(s->options, "set-titles")) server_client_set_title(c); if (c->message_string != NULL) @@ -758,183 +931,194 @@ server_client_check_redraw(struct client *c) c->flags &= ~CLIENT_STATUS; } + flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR); + tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR; + if (c->flags & CLIENT_REDRAW) { - screen_redraw_screen(c, 0, 0); + tty_update_mode(tty, tty->mode, NULL); + screen_redraw_screen(c, 1, 1, 1); c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); } else if (c->flags & CLIENT_REDRAWWINDOW) { + tty_update_mode(tty, tty->mode, NULL); TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) screen_redraw_pane(c, wp); c->flags &= ~CLIENT_REDRAWWINDOW; } else { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { - if (wp->flags & PANE_REDRAW) + if (wp->flags & PANE_REDRAW) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); + } } } - if (c->flags & CLIENT_BORDERS) - screen_redraw_screen(c, 0, 1); + if (c->flags & CLIENT_BORDERS) { + tty_update_mode(tty, tty->mode, NULL); + screen_redraw_screen(c, 0, 0, 1); + } - if (c->flags & CLIENT_STATUS) - screen_redraw_screen(c, 1, 0); + if (c->flags & CLIENT_STATUS) { + tty_update_mode(tty, tty->mode, NULL); + screen_redraw_screen(c, 0, 1, 0); + } - c->tty.flags |= flags; + tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; + tty_update_mode(tty, tty->mode, NULL); - c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS); + c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS| + CLIENT_STATUSFORCE); } /* Set client title. */ void server_client_set_title(struct client *c) { - struct session *s = c->session; - const char *template; - char *title; + struct session *s = c->session; + const char *template; + char *title; + struct format_tree *ft; - template = options_get_string(&s->options, "set-titles-string"); + template = options_get_string(s->options, "set-titles-string"); - title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1); + ft = format_create(NULL, 0); + format_defaults(ft, c, NULL, NULL, NULL); + + title = format_expand_time(ft, template, time(NULL)); if (c->title == NULL || strcmp(title, c->title) != 0) { free(c->title); c->title = xstrdup(title); tty_set_title(&c->tty, c->title); } free(title); + + format_free(ft); } /* Dispatch message from client. */ -int -server_client_msg_dispatch(struct client *c) +void +server_client_dispatch(struct imsg *imsg, void *arg) { - struct imsg imsg; - struct msg_command_data commanddata; - struct msg_identify_data identifydata; - struct msg_environ_data environdata; + struct client *c = arg; struct msg_stdin_data stdindata; - ssize_t n, datalen; + const char *data; + ssize_t datalen; + struct session *s; - if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) - return (-1); + if (c->flags & CLIENT_DEAD) + return; - for (;;) { - if ((n = imsg_get(&c->ibuf, &imsg)) == -1) - return (-1); - if (n == 0) - return (0); - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + if (imsg == NULL) { + server_client_lost(c); + return; + } - if (imsg.hdr.peerid != PROTOCOL_VERSION) { - server_write_client(c, MSG_VERSION, NULL, 0); - c->flags |= CLIENT_BAD; - imsg_free(&imsg); - continue; + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_IDENTIFY_FLAGS: + case MSG_IDENTIFY_TERM: + case MSG_IDENTIFY_TTYNAME: + case MSG_IDENTIFY_CWD: + case MSG_IDENTIFY_STDIN: + case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_CLIENTPID: + case MSG_IDENTIFY_DONE: + server_client_dispatch_identify(c, imsg); + break; + case MSG_COMMAND: + server_client_dispatch_command(c, imsg); + break; + case MSG_STDIN: + if (datalen != sizeof stdindata) + fatalx("bad MSG_STDIN size"); + memcpy(&stdindata, data, sizeof stdindata); + + if (c->stdin_callback == NULL) + break; + if (stdindata.size <= 0) + c->stdin_closed = 1; + else { + evbuffer_add(c->stdin_data, stdindata.data, + stdindata.size); } + c->stdin_callback(c, c->stdin_closed, + c->stdin_callback_data); + break; + case MSG_RESIZE: + if (datalen != 0) + fatalx("bad MSG_RESIZE size"); - log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); - switch (imsg.hdr.type) { - case MSG_COMMAND: - if (datalen != sizeof commanddata) - fatalx("bad MSG_COMMAND size"); - memcpy(&commanddata, imsg.data, sizeof commanddata); - - server_client_msg_command(c, &commanddata); + if (c->flags & CLIENT_CONTROL) break; - case MSG_IDENTIFY: - if (datalen != sizeof identifydata) - fatalx("bad MSG_IDENTIFY size"); - if (imsg.fd == -1) - fatalx("MSG_IDENTIFY missing fd"); - memcpy(&identifydata, imsg.data, sizeof identifydata); - - server_client_msg_identify(c, &identifydata, imsg.fd); - break; - case MSG_STDIN: - if (datalen != sizeof stdindata) - fatalx("bad MSG_STDIN size"); - memcpy(&stdindata, imsg.data, sizeof stdindata); - - if (c->stdin_callback == NULL) - break; - if (stdindata.size <= 0) - c->stdin_closed = 1; - else { - evbuffer_add(c->stdin_data, stdindata.data, - stdindata.size); - } - c->stdin_callback(c, c->stdin_closed, - c->stdin_callback_data); - break; - case MSG_RESIZE: - if (datalen != 0) - fatalx("bad MSG_RESIZE size"); - - if (c->flags & CLIENT_CONTROL) - break; - if (tty_resize(&c->tty)) { - recalculate_sizes(); - server_redraw_client(c); - } - break; - case MSG_EXITING: - if (datalen != 0) - fatalx("bad MSG_EXITING size"); - - c->session = NULL; - tty_close(&c->tty); - server_write_client(c, MSG_EXITED, NULL, 0); - break; - case MSG_WAKEUP: - case MSG_UNLOCK: - if (datalen != 0) - fatalx("bad MSG_WAKEUP size"); - - if (!(c->flags & CLIENT_SUSPENDED)) - break; - c->flags &= ~CLIENT_SUSPENDED; - - if (gettimeofday(&c->activity_time, NULL) != 0) - fatal("gettimeofday"); - if (c->session != NULL) - session_update_activity(c->session); - - tty_start_tty(&c->tty); - server_redraw_client(c); + if (tty_resize(&c->tty)) { recalculate_sizes(); - break; - case MSG_ENVIRON: - if (datalen != sizeof environdata) - fatalx("bad MSG_ENVIRON size"); - memcpy(&environdata, imsg.data, sizeof environdata); - - environdata.var[(sizeof environdata.var) - 1] = '\0'; - if (strchr(environdata.var, '=') != NULL) - environ_put(&c->environ, environdata.var); - break; - case MSG_SHELL: - if (datalen != 0) - fatalx("bad MSG_SHELL size"); - - server_client_msg_shell(c); - break; - default: - fatalx("unexpected message"); + server_redraw_client(c); } + if (c->session != NULL) + hooks_run(c->session->hooks, c, NULL, "client-resized"); + break; + case MSG_EXITING: + if (datalen != 0) + fatalx("bad MSG_EXITING size"); - imsg_free(&imsg); + c->session = NULL; + tty_close(&c->tty); + proc_send(c->peer, MSG_EXITED, -1, NULL, 0); + break; + case MSG_WAKEUP: + case MSG_UNLOCK: + if (datalen != 0) + fatalx("bad MSG_WAKEUP size"); + + if (!(c->flags & CLIENT_SUSPENDED)) + break; + c->flags &= ~CLIENT_SUSPENDED; + + if (c->tty.fd == -1) /* exited in the meantime */ + break; + s = c->session; + + if (gettimeofday(&c->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + if (s != NULL) + session_update_activity(s, &c->activity_time); + + tty_start_tty(&c->tty); + server_redraw_client(c); + recalculate_sizes(); + break; + case MSG_SHELL: + if (datalen != 0) + fatalx("bad MSG_SHELL size"); + + server_client_dispatch_shell(c); + break; } } /* Handle command message. */ void -server_client_msg_command(struct client *c, struct msg_command_data *data) +server_client_dispatch_command(struct client *c, struct imsg *imsg) { - struct cmd_list *cmdlist = NULL; - int argc; - char **argv, *cause; + struct msg_command_data data; + char *buf; + size_t len; + struct cmd_list *cmdlist = NULL; + int argc; + char **argv, *cause; - argc = data->argc; - data->argv[(sizeof data->argv) - 1] = '\0'; - if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { + if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data) + fatalx("bad MSG_COMMAND size"); + memcpy(&data, imsg->data, sizeof data); + + buf = (char *)imsg->data + sizeof data; + len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data; + if (len > 0 && buf[len - 1] != '\0') + fatalx("bad MSG_COMMAND string"); + + argc = data.argc; + if (cmd_unpack_argv(buf, len, argc, &argv) != 0) { cmdq_error(c->cmdq, "command too long"); goto error; } @@ -952,7 +1136,10 @@ server_client_msg_command(struct client *c, struct msg_command_data *data) } cmd_free_argv(argc, argv); - cmdq_run(c->cmdq, cmdlist); + if (c != cfg_client || cfg_finished) + cmdq_run(c->cmdq, cmdlist, NULL); + else + cmdq_append(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); return; @@ -965,63 +1152,213 @@ error: /* Handle identify message. */ void -server_client_msg_identify( - struct client *c, struct msg_identify_data *data, int fd) +server_client_dispatch_identify(struct client *c, struct imsg *imsg) { - c->cwd = NULL; - data->cwd[(sizeof data->cwd) - 1] = '\0'; - if (*data->cwd != '\0') - c->cwd = xstrdup(data->cwd); + const char *data, *home; + size_t datalen; + int flags; - if (data->flags & IDENTIFY_CONTROL) { + if (c->flags & CLIENT_IDENTIFIED) + fatalx("out-of-order identify message"); + + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_IDENTIFY_FLAGS: + if (datalen != sizeof flags) + fatalx("bad MSG_IDENTIFY_FLAGS size"); + memcpy(&flags, data, sizeof flags); + c->flags |= flags; + log_debug("client %p IDENTIFY_FLAGS %#x", c, flags); + break; + case MSG_IDENTIFY_TERM: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_TERM string"); + c->term = xstrdup(data); + log_debug("client %p IDENTIFY_TERM %s", c, data); + break; + case MSG_IDENTIFY_TTYNAME: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_TTYNAME string"); + c->ttyname = xstrdup(data); + log_debug("client %p IDENTIFY_TTYNAME %s", c, data); + break; + case MSG_IDENTIFY_CWD: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_CWD string"); + if (access(data, X_OK) == 0) + c->cwd = xstrdup(data); + else if ((home = find_home()) != NULL) + c->cwd = xstrdup(home); + else + c->cwd = xstrdup("/"); + log_debug("client %p IDENTIFY_CWD %s", c, data); + break; + case MSG_IDENTIFY_STDIN: + if (datalen != 0) + fatalx("bad MSG_IDENTIFY_STDIN size"); + c->fd = imsg->fd; + log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); + break; + case MSG_IDENTIFY_ENVIRON: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_ENVIRON string"); + if (strchr(data, '=') != NULL) + environ_put(c->environ, data); + log_debug("client %p IDENTIFY_ENVIRON %s", c, data); + break; + case MSG_IDENTIFY_CLIENTPID: + if (datalen != sizeof c->pid) + fatalx("bad MSG_IDENTIFY_CLIENTPID size"); + memcpy(&c->pid, data, sizeof c->pid); + log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid); + break; + default: + break; + } + + if (imsg->hdr.type != MSG_IDENTIFY_DONE) + return; + c->flags |= CLIENT_IDENTIFIED; + +#ifdef __CYGWIN__ + c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); +#endif + + if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; + evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; - c->flags |= CLIENT_CONTROL; - if (data->flags & IDENTIFY_TERMIOS) + + if (c->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); - server_write_client(c, MSG_STDIN, NULL, 0); + proc_send(c->peer, MSG_STDIN, -1, NULL, 0); c->tty.fd = -1; - c->tty.log_fd = -1; - close(fd); + close(c->fd); + c->fd = -1; + return; } - if (!isatty(fd)) { - close(fd); + if (c->fd == -1) + return; + if (tty_init(&c->tty, c, c->fd, c->term) != 0) { + close(c->fd); + c->fd = -1; return; } - data->term[(sizeof data->term) - 1] = '\0'; - tty_init(&c->tty, c, fd, data->term); - if (data->flags & IDENTIFY_UTF8) + if (c->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; - if (data->flags & IDENTIFY_256COLOURS) + if (c->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; - else if (data->flags & IDENTIFY_88COLOURS) - c->tty.term_flags |= TERM_88COLOURS; tty_resize(&c->tty); - if (!(data->flags & IDENTIFY_CONTROL)) + if (!(c->flags & CLIENT_CONTROL)) c->flags |= CLIENT_TERMINAL; } /* Handle shell message. */ void -server_client_msg_shell(struct client *c) +server_client_dispatch_shell(struct client *c) { - struct msg_shell_data data; - const char *shell; - - shell = options_get_string(&global_s_options, "default-shell"); + const char *shell; + shell = options_get_string(global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) - strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); + proc_send_s(c->peer, MSG_SHELL, shell); - server_write_client(c, MSG_SHELL, &data, sizeof data); - c->flags |= CLIENT_BAD; /* it will die after exec */ + proc_kill_peer(c->peer); +} + +/* Event callback to push more stdout data if any left. */ +static void +server_client_stdout_cb(__unused int fd, __unused short events, void *arg) +{ + struct client *c = arg; + + if (~c->flags & CLIENT_DEAD) + server_client_push_stdout(c); + server_client_unref(c); +} + +/* Push stdout to client if possible. */ +void +server_client_push_stdout(struct client *c) +{ + struct msg_stdout_data data; + size_t sent, left; + + left = EVBUFFER_LENGTH(c->stdout_data); + while (left != 0) { + sent = left; + if (sent > sizeof data.data) + sent = sizeof data.data; + memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent); + data.size = sent; + + if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0) + break; + evbuffer_drain(c->stdout_data, sent); + + left = EVBUFFER_LENGTH(c->stdout_data); + log_debug("%s: client %p, sent %zu, left %zu", __func__, c, + sent, left); + } + if (left != 0) { + c->references++; + event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL); + log_debug("%s: client %p, queued", __func__, c); + } +} + +/* Event callback to push more stderr data if any left. */ +static void +server_client_stderr_cb(__unused int fd, __unused short events, void *arg) +{ + struct client *c = arg; + + if (~c->flags & CLIENT_DEAD) + server_client_push_stderr(c); + server_client_unref(c); +} + +/* Push stderr to client if possible. */ +void +server_client_push_stderr(struct client *c) +{ + struct msg_stderr_data data; + size_t sent, left; + + if (c->stderr_data == c->stdout_data) { + server_client_push_stdout(c); + return; + } + + left = EVBUFFER_LENGTH(c->stderr_data); + while (left != 0) { + sent = left; + if (sent > sizeof data.data) + sent = sizeof data.data; + memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent); + data.size = sent; + + if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0) + break; + evbuffer_drain(c->stderr_data, sent); + + left = EVBUFFER_LENGTH(c->stderr_data); + log_debug("%s: client %p, sent %zu, left %zu", __func__, c, + sent, left); + } + if (left != 0) { + c->references++; + event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL); + log_debug("%s: client %p, queued", __func__, c); + } } diff --git a/server-fn.c b/server-fn.c index 566925f0..5c005c6f 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -31,61 +32,19 @@ void server_callback_identify(int, short, void *); void server_fill_environ(struct session *s, struct environ *env) { - char var[MAXPATHLEN], *term; - u_int idx; - long pid; + char *term; + u_int idx; + long pid; if (s != NULL) { - term = options_get_string(&s->options, "default-terminal"); - environ_set(env, "TERM", term); + term = options_get_string(global_options, "default-terminal"); + environ_set(env, "TERM", "%s", term); idx = s->id; } else - idx = -1; + idx = (u_int)-1; pid = getpid(); - xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx); - environ_set(env, "TMUX", var); -} - -void -server_write_ready(struct client *c) -{ - if (c->flags & CLIENT_CONTROL) - return; - server_write_client(c, MSG_READY, NULL, 0); -} - -int -server_write_client( - struct client *c, enum msgtype type, const void *buf, size_t len) -{ - struct imsgbuf *ibuf = &c->ibuf; - int error; - - if (c->flags & CLIENT_BAD) - return (-1); - log_debug("writing %d to client %d", type, c->ibuf.fd); - error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, - (void *) buf, len); - if (error == 1) - server_update_event(c); - return (error == 1 ? 0 : -1); -} - -void -server_write_session( - struct session *s, enum msgtype type, const void *buf, size_t len) -{ - struct client *c; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - if (c->session == s) - server_write_client(c, type, buf, len); - } + environ_set(env, "TMUX", "%s,%ld,%u", socket_path, pid, idx); } void @@ -104,12 +63,8 @@ void server_redraw_session(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; + TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_redraw_client(c); } @@ -132,12 +87,8 @@ void server_status_session(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; + TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_status_client(c); } @@ -160,13 +111,9 @@ void server_redraw_window(struct window *w) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - if (c->session->curw->window == w) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && c->session->curw->window == w) server_redraw_client(c); } w->flags |= WINDOW_REDRAW; @@ -176,13 +123,9 @@ void server_redraw_window_borders(struct window *w) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - if (c->session->curw->window == w) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && c->session->curw->window == w) c->flags |= CLIENT_BORDERS; } } @@ -199,7 +142,7 @@ server_status_window(struct window *w) */ RB_FOREACH(s, sessions, &sessions) { - if (session_has(s, w) != NULL) + if (session_has(s, w)) server_status_session(s); } } @@ -208,13 +151,10 @@ void server_lock(void) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - server_lock_client(c); + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL) + server_lock_client(c); } } @@ -222,22 +162,17 @@ void server_lock_session(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL || c->session != s) - continue; - server_lock_client(c); + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == s) + server_lock_client(c); } } void server_lock_client(struct client *c) { - const char *cmd; - size_t cmdlen; - struct msg_lock_data lockdata; + const char *cmd; if (c->flags & CLIENT_CONTROL) return; @@ -245,9 +180,8 @@ server_lock_client(struct client *c) if (c->flags & CLIENT_SUSPENDED) return; - cmd = options_get_string(&c->session->options, "lock-command"); - cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd); - if (cmdlen >= sizeof lockdata.cmd) + cmd = options_get_string(c->session->options, "lock-command"); + if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) return; tty_stop_tty(&c->tty); @@ -256,22 +190,24 @@ server_lock_client(struct client *c) tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata); + proc_send_s(c->peer, MSG_LOCK, cmd); } void server_kill_window(struct window *w) { - struct session *s, *next_s; - struct winlink *wl; + struct session *s, *next_s, *target_s; + struct session_group *sg; + struct winlink *wl; next_s = RB_MIN(sessions, &sessions); while (next_s != NULL) { s = next_s; next_s = RB_NEXT(sessions, &sessions, s); - if (session_has(s, w) == NULL) + if (!session_has(s, w)) continue; + server_unzoom_window(w); while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { if (session_detach(s, wl)) { server_destroy_session_group(s); @@ -280,14 +216,21 @@ server_kill_window(struct window *w) server_redraw_session_group(s); } - if (options_get_number(&s->options, "renumber-windows")) - session_renumber_windows(s); + if (options_get_number(s->options, "renumber-windows")) { + if ((sg = session_group_find(s)) != NULL) { + TAILQ_FOREACH(target_s, &sg->sessions, gentry) + session_renumber_windows(target_s); + } else + session_renumber_windows(s); + } } + recalculate_sizes(); } int server_link_window(struct session *src, struct winlink *srcwl, - struct session *dst, int dstidx, int killflag, int selectflag, char **cause) + struct session *dst, int dstidx, int killflag, int selectflag, + char **cause) { struct winlink *dstwl; struct session_group *srcsg, *dstsg; @@ -326,7 +269,7 @@ server_link_window(struct session *src, struct winlink *srcwl, } if (dstidx == -1) - dstidx = -1 - options_get_number(&dst->options, "base-index"); + dstidx = -1 - options_get_number(dst->options, "base-index"); dstwl = session_attach(dst, srcwl->window, dstidx, cause); if (dstwl == NULL) return (-1); @@ -348,21 +291,25 @@ server_unlink_window(struct session *s, struct winlink *wl) } void -server_destroy_pane(struct window_pane *wp) +server_destroy_pane(struct window_pane *wp, int hooks) { struct window *w = wp->window; int old_fd; struct screen_write_ctx ctx; struct grid_cell gc; + struct cmd_find_state fs; old_fd = wp->fd; if (wp->fd != -1) { +#ifdef HAVE_UTEMPTER + utempter_remove_record(wp->fd); +#endif bufferevent_free(wp->event); close(wp->fd); wp->fd = -1; } - if (options_get_number(&w->options, "remain-on-exit")) { + if (options_get_number(w->options, "remain-on-exit")) { if (old_fd == -1) return; screen_write_start(&ctx, wp, &wp->base); @@ -374,6 +321,9 @@ server_destroy_pane(struct window_pane *wp) screen_write_puts(&ctx, &gc, "Pane is dead"); screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; + + if (hooks && cmd_find_from_pane(&fs, wp) == 0) + hooks_run(hooks_get(fs.s), NULL, &fs, "pane-died"); return; } @@ -381,6 +331,9 @@ server_destroy_pane(struct window_pane *wp) layout_close_pane(wp); window_remove_pane(w, wp); + if (hooks && cmd_find_from_window(&fs, w) == 0) + hooks_run(hooks_get(fs.s), NULL, &fs, "pane-exited"); + if (TAILQ_EMPTY(&w->panes)) server_kill_window(w); else @@ -391,14 +344,15 @@ void server_destroy_session_group(struct session *s) { struct session_group *sg; + struct session *s1; if ((sg = session_group_find(s)) == NULL) server_destroy_session(s); else { - TAILQ_FOREACH(s, &sg->sessions, gentry) + TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { server_destroy_session(s); - TAILQ_REMOVE(&session_groups, sg, entry); - free(sg); + session_destroy(s); + } } } @@ -423,16 +377,14 @@ server_destroy_session(struct session *s) { struct client *c; struct session *s_new; - u_int i; - if (!options_get_number(&s->options, "detach-on-destroy")) + if (!options_get_number(s->options, "detach-on-destroy")) s_new = server_next_session(s); else s_new = NULL; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) continue; if (s_new == NULL) { c->session = NULL; @@ -440,16 +392,20 @@ server_destroy_session(struct session *s) } else { c->last_session = NULL; c->session = s_new; + server_client_set_key_table(c, NULL); + status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s_new); + session_update_activity(s_new, NULL); + gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); + alerts_check_session(s_new); } } recalculate_sizes(); } void -server_check_unattached (void) +server_check_unattached(void) { struct session *s; @@ -460,7 +416,7 @@ server_check_unattached (void) RB_FOREACH(s, sessions, &sessions) { if (!(s->flags & SESSION_UNATTACHED)) continue; - if (options_get_number (&s->options, "destroy-unattached")) + if (options_get_number (s->options, "destroy-unattached")) session_destroy(s); } } @@ -471,11 +427,11 @@ server_set_identify(struct client *c) struct timeval tv; int delay; - delay = options_get_number(&c->session->options, "display-panes-time"); + delay = options_get_number(c->session->options, "display-panes-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; - if (event_initialized (&c->identify_timer)) + if (event_initialized(&c->identify_timer)) evtimer_del(&c->identify_timer); evtimer_set(&c->identify_timer, server_callback_identify, c); evtimer_add(&c->identify_timer, &tv); @@ -496,73 +452,13 @@ server_clear_identify(struct client *c) } void -server_callback_identify(unused int fd, unused short events, void *data) +server_callback_identify(__unused int fd, __unused short events, void *data) { struct client *c = data; server_clear_identify(c); } -void -server_update_event(struct client *c) -{ - short events; - - events = 0; - if (!(c->flags & CLIENT_BAD)) - events |= EV_READ; - if (c->ibuf.w.queued > 0) - events |= EV_WRITE; - if (event_initialized(&c->event)) - event_del(&c->event); - event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); - event_add(&c->event, NULL); -} - -/* Push stdout to client if possible. */ -void -server_push_stdout(struct client *c) -{ - struct msg_stdout_data data; - size_t size; - - size = EVBUFFER_LENGTH(c->stdout_data); - if (size == 0) - return; - if (size > sizeof data.data) - size = sizeof data.data; - - memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); - data.size = size; - - if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) - evbuffer_drain(c->stdout_data, size); -} - -/* Push stderr to client if possible. */ -void -server_push_stderr(struct client *c) -{ - struct msg_stderr_data data; - size_t size; - - if (c->stderr_data == c->stdout_data) { - server_push_stdout(c); - return; - } - size = EVBUFFER_LENGTH(c->stderr_data); - if (size == 0) - return; - if (size > sizeof data.data) - size = sizeof data.data; - - memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); - data.size = size; - - if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) - evbuffer_drain(c->stderr_data, size); -} - /* Set stdin callback. */ int server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, @@ -587,9 +483,9 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, c->references++; if (c->stdin_closed) - c->stdin_callback (c, 1, c->stdin_callback_data); + c->stdin_callback(c, 1, c->stdin_callback_data); - server_write_client(c, MSG_STDIN, NULL, 0); + proc_send(c->peer, MSG_STDIN, -1, NULL, 0); return (0); } @@ -597,7 +493,8 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, void server_unzoom_window(struct window *w) { - window_unzoom(w); - server_redraw_window(w); - server_status_window(w); + if (window_unzoom(w) == 0) { + server_redraw_window(w); + server_status_window(w); + } } diff --git a/server-window.c b/server-window.c deleted file mode 100644 index 4f5a5504..00000000 --- a/server-window.c +++ /dev/null @@ -1,248 +0,0 @@ -/* $Id$ */ - -/* - * Copyright (c) 2009 Nicholas Marriott - * - * 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 - -#include -#include -#include - -#include "tmux.h" - -int server_window_check_bell(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( - struct session *, struct winlink *, struct window_pane *); -void ring_bell(struct session *); - -/* Window functions that need to happen every loop. */ -void -server_window_loop(void) -{ - struct window *w; - struct winlink *wl; - struct window_pane *wp; - struct session *s; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) - continue; - - RB_FOREACH(s, sessions, &sessions) { - wl = session_has(s, w); - if (wl == NULL) - continue; - - if (server_window_check_bell(s, wl) || - server_window_check_activity(s, wl) || - server_window_check_silence(s, wl)) - server_status_session(s); - TAILQ_FOREACH(wp, &w->panes, entry) - server_window_check_content(s, wl, wp); - } - } -} - -/* Check for bell in window. */ -int -server_window_check_bell(struct session *s, struct winlink *wl) -{ - struct client *c; - struct window *w = wl->window; - u_int i; - int action, visual; - - if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) - return (0); - if (s->curw != wl || s->flags & SESSION_UNATTACHED) - wl->flags |= WINLINK_BELL; - if (s->flags & SESSION_UNATTACHED) - return (0); - if (s->curw->window == wl->window) - w->flags &= ~WINDOW_BELL; - - visual = options_get_number(&s->options, "visual-bell"); - action = options_get_number(&s->options, "bell-action"); - if (action == BELL_NONE) - return (0); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s || (c->flags & CLIENT_CONTROL)) - continue; - if (!visual) { - tty_bell(&c->tty); - continue; - } - if (c->session->curw->window == w) - status_message_set(c, "Bell in current window"); - else if (action == BELL_ANY) { - status_message_set(c, "Bell in window %u", - winlink_find_by_window(&s->windows, w)->idx); - } - } - - return (1); -} - -/* Check for activity in window. */ -int -server_window_check_activity(struct session *s, struct winlink *wl) -{ - struct client *c; - struct window *w = wl->window; - u_int i; - - if (s->curw->window == wl->window) - w->flags &= ~WINDOW_ACTIVITY; - - if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) - return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) - return (0); - - if (!options_get_number(&w->options, "monitor-activity")) - return (0); - - if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); - wl->flags |= WINLINK_ACTIVITY; - - if (options_get_number(&s->options, "visual-activity")) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - status_message_set(c, "Activity in window %u", - winlink_find_by_window(&s->windows, w)->idx); - } - } - - 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 && !(s->flags & SESSION_UNATTACHED)) { - /* - * 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); - - if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); - 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. */ -int -server_window_check_content( - struct session *s, struct winlink *wl, struct window_pane *wp) -{ - struct client *c; - struct window *w = wl->window; - u_int i; - char *found, *ptr; - - /* Activity flag must be set for new content. */ - if (s->curw->window == w) - w->flags &= ~WINDOW_ACTIVITY; - - if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_CONTENT) - return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) - return (0); - - ptr = options_get_string(&w->options, "monitor-content"); - if (ptr == NULL || *ptr == '\0') - return (0); - if ((found = window_pane_search(wp, ptr, NULL)) == NULL) - return (0); - free(found); - - if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); - wl->flags |= WINLINK_CONTENT; - - if (options_get_number(&s->options, "visual-content")) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - status_message_set(c, "Content in window %u", - winlink_find_by_window(&s->windows, w)->idx); - } - } - - return (1); -} - -/* Ring terminal bell. */ -void -ring_bell(struct session *s) -{ - struct client *c; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session == s && !(c->flags & CLIENT_CONTROL)) - tty_bell(&c->tty); - } -} diff --git a/server.c b/server.c index 80c17689..ce8a7cfe 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -42,30 +41,62 @@ * Main server functions. */ -/* Client list. */ -struct clients clients; -struct clients dead_clients; +struct clients clients; -int server_fd; -int server_shutdown; -struct event server_ev_accept; -struct event server_ev_second; +struct tmuxproc *server_proc; +int server_fd; +int server_exit; +struct event server_ev_accept; -struct paste_stack global_buffers; +struct cmd_find_state marked_pane; -int server_create_socket(void); -void server_loop(void); -int server_should_shutdown(void); -void server_send_shutdown(void); -void server_clean_dead(void); -void server_accept_callback(int, short, void *); -void server_signal_callback(int, short, void *); -void server_child_signal(void); -void server_child_exited(pid_t, int); -void server_child_stopped(pid_t, int); -void server_second_callback(int, short, void *); -void server_lock_server(void); -void server_lock_sessions(void); +int server_create_socket(void); +int server_loop(void); +int server_should_exit(void); +void server_send_exit(void); +void server_accept(int, short, void *); +void server_signal(int); +void server_child_signal(void); +void server_child_exited(pid_t, int); +void server_child_stopped(pid_t, int); + +/* Set marked pane. */ +void +server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + cmd_find_clear_state(&marked_pane, NULL, 0); + marked_pane.s = s; + marked_pane.wl = wl; + marked_pane.w = wl->window; + marked_pane.wp = wp; +} + +/* Clear marked pane. */ +void +server_clear_marked(void) +{ + cmd_find_clear_state(&marked_pane, NULL, 0); +} + +/* Is this the marked pane? */ +int +server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + if (s == NULL || wl == NULL || wp == NULL) + return (0); + if (marked_pane.s != s || marked_pane.wl != wl) + return (0); + if (marked_pane.wp != wp) + return (0); + return (server_check_marked()); +} + +/* Check if the marked pane is still valid. */ +int +server_check_marked(void) +{ + return (cmd_find_valid_state(&marked_pane)); +} /* Create server socket. */ int @@ -81,220 +112,135 @@ server_create_socket(void) size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); if (size >= sizeof sa.sun_path) { errno = ENAMETOOLONG; - fatal("socket failed"); + return (-1); } unlink(sa.sun_path); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); + return (-1); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); - if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) - fatal("bind failed"); + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) + return (-1); umask(mask); if (listen(fd, 16) == -1) - fatal("listen failed"); + return (-1); setblocking(fd, 0); - server_update_socket(); - return (fd); } /* Fork new server. */ int -server_start(int lockfd, char *lockfile) +server_start(struct event_base *base, int lockfd, char *lockfile) { - int pair[2]; - struct timeval tv; - char *cause; + int pair[2]; - /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); - switch (fork()) { - case -1: - fatal("fork failed"); - case 0: - break; - default: + server_proc = proc_start("server", base, 1, server_signal); + if (server_proc == NULL) { close(pair[1]); return (pair[0]); } close(pair[0]); - /* - * Must daemonise before loading configuration as the PID changes so - * $TMUX would be wrong for sessions created in the config file. - */ - if (daemon(1, 0) != 0) - fatal("daemon failed"); + if (log_get_level() > 3) + tty_create_log(); - /* event_init() was called in our parent, need to reinit. */ - if (event_reinit(ev_base) != 0) - fatal("event_reinit failed"); - clear_signals(0); - - logfile("server"); - log_debug("server started, pid %ld", (long) getpid()); - - ARRAY_INIT(&windows); - RB_INIT(&all_window_panes); - ARRAY_INIT(&clients); - ARRAY_INIT(&dead_clients); - RB_INIT(&sessions); - RB_INIT(&dead_sessions); - TAILQ_INIT(&session_groups); - ARRAY_INIT(&global_buffers); - mode_key_init_trees(); - key_bindings_init(); - utf8_build(); - - start_time = time(NULL); - log_debug("socket path %s", socket_path); -#ifdef HAVE_SETPROCTITLE - setproctitle("server (%s)", socket_path); +#ifdef __OpenBSD__ + if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " + "tty ps", NULL) != 0) + fatal("pledge failed"); #endif + RB_INIT(&windows); + RB_INIT(&all_window_panes); + TAILQ_INIT(&clients); + RB_INIT(&sessions); + TAILQ_INIT(&session_groups); + mode_key_init_trees(); + key_bindings_init(); + + gettimeofday(&start_time, NULL); + server_fd = server_create_socket(); + if (server_fd == -1) + fatal("couldn't create socket"); + server_update_socket(); server_client_create(pair[1]); - unlink(lockfile); - free(lockfile); - close(lockfd); - - cfg_cmd_q = cmdq_new(NULL); - cfg_cmd_q->emptyfn = cfg_default_done; - cfg_finished = 0; - cfg_references = 1; - ARRAY_INIT(&cfg_causes); - - if (access(SYSTEM_CFG, R_OK) == 0) { - if (load_cfg(SYSTEM_CFG, cfg_cmd_q, &cause) == -1) { - xasprintf(&cause, "%s: %s", SYSTEM_CFG, cause); - ARRAY_ADD(&cfg_causes, cause); - } - } else if (errno != ENOENT) { - xasprintf(&cause, "%s: %s", SYSTEM_CFG, strerror(errno)); - ARRAY_ADD(&cfg_causes, cause); - } - if (cfg_file != NULL) { - if (load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) { - xasprintf(&cause, "%s: %s", cfg_file, cause); - ARRAY_ADD(&cfg_causes, cause); - } - } - if (tmate_cfg_file != NULL) { - if (load_cfg(tmate_cfg_file, cfg_cmd_q, &cause) == -1) { - xasprintf(&cause, "%s: %s", tmate_cfg_file, cause); - ARRAY_ADD(&cfg_causes, cause); - } + if (lockfd >= 0) { + unlink(lockfile); + free(lockfile); + close(lockfd); } - tmate_session_init(); - cmdq_continue(cfg_cmd_q); +#ifdef TMATE + tmate_session_init(base); +#endif + + start_cfg(); + + status_prompt_load_history(); server_add_accept(0); - memset(&tv, 0, sizeof tv); - tv.tv_sec = 1; - evtimer_set(&server_ev_second, server_second_callback, NULL); - evtimer_add(&server_ev_second, &tv); - - set_signals(server_signal_callback); - - /* tmate_session_start() is called in cfg_default_done */ - server_loop(); + proc_loop(server_proc, server_loop); + status_prompt_save_history(); exit(0); } -/* Main server loop. */ -void +/* Server loop callback. */ +int server_loop(void) { - while (!server_should_shutdown()) { - event_loop(EVLOOP_ONCE); + struct client *c; - server_window_loop(); - server_client_loop(); + server_client_loop(); - key_bindings_clean(); - server_clean_dead(); - } -} - -/* Check if the server should be shutting down (no more clients or sessions). */ -int -server_should_shutdown(void) -{ - u_int i; - - if (!options_get_number(&global_options, "exit-unattached")) { + if (!options_get_number(global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) != NULL) + + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL) return (0); } + + /* + * No attached clients therefore want to exit - flush any waiting + * clients but don't actually exit until they've gone. + */ + cmd_wait_for_flush(); + if (!TAILQ_EMPTY(&clients)) + return (0); + return (1); } -/* Shutdown the server by killing all clients and windows. */ +/* Exit the server by killing all clients and windows. */ void -server_send_shutdown(void) +server_send_exit(void) { - struct client *c; - struct session *s, *next_s; - u_int i; + struct client *c, *c1; + struct session *s, *s1; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL) { - if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) - server_client_lost(c); - else - server_write_client(c, MSG_SHUTDOWN, NULL, 0); - c->session = NULL; - } + cmd_wait_for_flush(); + + TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { + if (c->flags & CLIENT_SUSPENDED) + server_client_lost(c); + else + proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); + c->session = NULL; } - s = RB_MIN(sessions, &sessions); - while (s != NULL) { - next_s = RB_NEXT(sessions, &sessions, s); + RB_FOREACH_SAFE(s, sessions, &sessions, s1) session_destroy(s); - s = next_s; - } -} - -/* Free dead, unreferenced clients and sessions. */ -void -server_clean_dead(void) -{ - struct session *s, *next_s; - struct client *c; - u_int i; - - s = RB_MIN(sessions, &dead_sessions); - while (s != NULL) { - next_s = RB_NEXT(sessions, &dead_sessions, s); - if (s->references == 0) { - RB_REMOVE(sessions, &dead_sessions, s); - free(s->name); - free(s); - } - s = next_s; - } - - for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { - c = ARRAY_ITEM(&dead_clients, i); - if (c == NULL || c->references != 0) - continue; - ARRAY_SET(&dead_clients, i, NULL); - free(c); - } } /* Update socket execute permissions based on whether sessions are attached. */ @@ -335,7 +281,7 @@ server_update_socket(void) /* Callback for server socket. */ void -server_accept_callback(int fd, short events, unused void *data) +server_accept(int fd, short events, __unused void *data) { struct sockaddr_storage sa; socklen_t slen = sizeof sa; @@ -356,7 +302,7 @@ server_accept_callback(int fd, short events, unused void *data) } fatal("accept failed"); } - if (server_shutdown) { + if (server_exit) { close(newfd); return; } @@ -376,32 +322,38 @@ server_add_accept(int timeout) event_del(&server_ev_accept); if (timeout == 0) { - event_set(&server_ev_accept, - server_fd, EV_READ, server_accept_callback, NULL); + event_set(&server_ev_accept, server_fd, EV_READ, server_accept, + NULL); event_add(&server_ev_accept, NULL); } else { - event_set(&server_ev_accept, - server_fd, EV_TIMEOUT, server_accept_callback, NULL); + event_set(&server_ev_accept, server_fd, EV_TIMEOUT, + server_accept, NULL); event_add(&server_ev_accept, &tv); } } /* Signal handler. */ void -server_signal_callback(int sig, unused short events, unused void *data) +server_signal(int sig) { + int fd; + switch (sig) { case SIGTERM: - server_shutdown = 1; - server_send_shutdown(); + server_exit = 1; + server_send_exit(); break; case SIGCHLD: server_child_signal(); break; case SIGUSR1: event_del(&server_ev_accept); - close(server_fd); - server_fd = server_create_socket(); + fd = server_create_socket(); + if (fd != -1) { + close(server_fd); + server_fd = fd; + server_update_socket(); + } server_add_accept(0); break; } @@ -434,17 +386,15 @@ server_child_signal(void) void server_child_exited(pid_t pid, int status) { - struct window *w; + struct window *w, *w1; struct window_pane *wp; struct job *job; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; + RB_FOREACH_SAFE(w, windows, &windows, w1) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { - server_destroy_pane(wp); + wp->status = status; + server_destroy_pane(wp, 1); break; } } @@ -464,14 +414,11 @@ server_child_stopped(pid_t pid, int status) { struct window *w; struct window_pane *wp; - u_int i; if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) return; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; + RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { if (killpg(pid, SIGCONT) != 0) @@ -480,77 +427,3 @@ server_child_stopped(pid_t pid, int status) } } } - -/* Handle once-per-second timer events. */ -void -server_second_callback(unused int fd, unused short events, unused void *arg) -{ - struct window *w; - struct window_pane *wp; - struct timeval tv; - u_int i; - - if (options_get_number(&global_s_options, "lock-server")) - server_lock_server(); - else - server_lock_sessions(); - - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) - continue; - - TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->mode != NULL && wp->mode->timer != NULL) - wp->mode->timer(wp); - } - } - - server_client_status_timer(); - - evtimer_del(&server_ev_second); - memset(&tv, 0, sizeof tv); - tv.tv_sec = 1; - evtimer_add(&server_ev_second, &tv); -} - -/* Lock the server if ALL sessions have hit the time limit. */ -void -server_lock_server(void) -{ - struct session *s; - int timeout; - time_t t; - - t = time(NULL); - RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; - timeout = options_get_number(&s->options, "lock-after-time"); - if (timeout <= 0 || t <= s->activity_time.tv_sec + timeout) - return; /* not timed out */ - } - - server_lock(); - recalculate_sizes(); -} - -/* Lock any sessions which have timed out. */ -void -server_lock_sessions(void) -{ - struct session *s; - int timeout; - time_t t; - - t = time(NULL); - RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; - timeout = options_get_number(&s->options, "lock-after-time"); - if (timeout > 0 && t > s->activity_time.tv_sec + timeout) { - server_lock_session(s); - recalculate_sizes(); - } - } -} diff --git a/session.c b/session.c index 25bcdf11..ab821a81 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -27,12 +27,14 @@ #include "tmux.h" #include "tmate.h" -/* Global session list. */ struct sessions sessions; -struct sessions dead_sessions; u_int next_session_id; struct session_groups session_groups; +void session_free(int, short, void *); + +void session_lock_timer(int, short, void *); + struct winlink *session_next_alert(struct winlink *); struct winlink *session_previous_alert(struct winlink *); @@ -70,6 +72,22 @@ session_find(const char *name) return (RB_FIND(sessions, &sessions, &s)); } +/* Find session by id parsed from a string. */ +struct session * +session_find_by_id_str(const char *s) +{ + const char *errstr; + u_int id; + + if (*s != '$') + return (NULL); + + id = strtonum(s + 1, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (NULL); + return (session_find_by_id(id)); +} + /* Find session by id. */ struct session * session_find_by_id(u_int id) @@ -85,11 +103,12 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * -session_create(const char *name, const char *cmd, const char *cwd, - struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, - char **cause) +session_create(const char *name, int argc, char **argv, const char *path, + const char *cwd, struct environ *env, struct termios *tio, int idx, + u_int sx, u_int sy, char **cause) { struct session *s; + struct winlink *wl; #ifdef TMATE if (next_session_id != 0) { @@ -98,24 +117,22 @@ session_create(const char *name, const char *cmd, const char *cwd, } #endif - s = xmalloc(sizeof *s); - s->references = 0; + s = xcalloc(1, sizeof *s); + s->references = 1; s->flags = 0; - if (gettimeofday(&s->creation_time, NULL) != 0) - fatal("gettimeofday failed"); - session_update_activity(s); - s->cwd = xstrdup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); - options_init(&s->options, &global_s_options); - environ_init(&s->environ); + s->environ = environ_create(); if (env != NULL) - environ_copy(env, &s->environ); + environ_copy(env, s->environ); + + s->options = options_create(global_s_options); + s->hooks = hooks_create(global_hooks); s->tio = NULL; if (tio != NULL) { @@ -133,14 +150,21 @@ session_create(const char *name, const char *cmd, const char *cwd, s->name = NULL; do { s->id = next_session_id++; - free (s->name); + free(s->name); xasprintf(&s->name, "%u", s->id); } while (RB_FIND(sessions, &sessions, s) != NULL); } RB_INSERT(sessions, &sessions, s); - if (cmd != NULL) { - if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) { + log_debug("new session %s $%u", s->name, s->id); + + if (gettimeofday(&s->creation_time, NULL) != 0) + fatal("gettimeofday failed"); + session_update_activity(s, &s->creation_time); + + if (argc >= 0) { + wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); + if (wl == NULL) { session_destroy(s); return (NULL); } @@ -153,11 +177,42 @@ session_create(const char *name, const char *cmd, const char *cwd, return (s); } +/* Remove a reference from a session. */ +void +session_unref(struct session *s) +{ + log_debug("session %s has %d references", s->name, s->references); + + s->references--; + if (s->references == 0) + event_once(-1, EV_TIMEOUT, session_free, s, NULL); +} + +/* Free session. */ +void +session_free(__unused int fd, __unused short events, void *arg) +{ + struct session *s = arg; + + log_debug("session %s freed (%d references)", s->name, s->references); + + if (s->references == 0) { + environ_free(s->environ); + + options_free(s->options); + hooks_free(s->hooks); + + free(s->name); + free(s); + } +} + /* Destroy a session. */ void session_destroy(struct session *s) { struct winlink *wl; + log_debug("session %s destroyed", s->name); RB_REMOVE(sessions, &sessions, s); @@ -165,9 +220,10 @@ session_destroy(struct session *s) free(s->tio); + if (event_initialized(&s->lock_timer)) + event_del(&s->lock_timer); + session_group_remove(s); - environ_free(&s->environ); - options_free(&s->options); while (!TAILQ_EMPTY(&s->lastw)) winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); @@ -177,24 +233,62 @@ session_destroy(struct session *s) winlink_remove(&s->windows, wl); } - free(s->cwd); + free((void *)s->cwd); - RB_INSERT(sessions, &dead_sessions, s); + session_unref(s); } -/* Check a session name is valid: not empty and no colons. */ +/* Check a session name is valid: not empty and no colons or periods. */ int session_check_name(const char *name) { - return (*name != '\0' && strchr(name, ':') == NULL); + return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } -/* Update session active time. */ +/* Lock session if it has timed out. */ void -session_update_activity(struct session *s) +session_lock_timer(__unused int fd, __unused short events, void *arg) { - if (gettimeofday(&s->activity_time, NULL) != 0) - fatal("gettimeofday"); + struct session *s = arg; + + if (s->flags & SESSION_UNATTACHED) + return; + + log_debug("session %s locked, activity time %lld", s->name, + (long long)s->activity_time.tv_sec); + + server_lock_session(s); + recalculate_sizes(); +} + +/* Update activity time. */ +void +session_update_activity(struct session *s, struct timeval *from) +{ + struct timeval *last = &s->last_activity_time; + struct timeval tv; + + memcpy(last, &s->activity_time, sizeof *last); + if (from == NULL) + gettimeofday(&s->activity_time, NULL); + else + memcpy(&s->activity_time, from, sizeof s->activity_time); + + log_debug("session %s activity %lld.%06d (last %lld.%06d)", s->name, + (long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec, + (long long)last->tv_sec, (int)last->tv_usec); + + if (evtimer_initialized(&s->lock_timer)) + evtimer_del(&s->lock_timer); + else + evtimer_set(&s->lock_timer, session_lock_timer, s); + + if (~s->flags & SESSION_UNATTACHED) { + timerclear(&tv); + tv.tv_sec = options_get_number(s->options, "lock-after-time"); + if (tv.tv_sec != 0) + evtimer_add(&s->lock_timer, &tv); + } } /* Find the next usable session. */ @@ -233,12 +327,12 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * -session_new(struct session *s, - const char *name, const char *cmd, const char *cwd, int idx, char **cause) +session_new(struct session *s, const char *name, int argc, char **argv, + const char *path, const char *cwd, int idx, char **cause) { struct window *w; struct winlink *wl; - struct environ env; + struct environ *env; const char *shell; u_int hlimit; @@ -247,32 +341,31 @@ session_new(struct session *s, return (NULL); } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); - shell = options_get_string(&s->options, "default-shell"); + shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - hlimit = options_get_number(&s->options, "history-limit"); - w = window_create( - name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause); + hlimit = options_get_number(s->options, "history-limit"); + w = window_create(name, argc, argv, path, shell, cwd, env, s->tio, + s->sx, s->sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); - environ_free(&env); + environ_free(env); return (NULL); } winlink_set_window(wl, w); notify_window_linked(s, w); - environ_free(&env); + environ_free(env); - if (options_get_number(&s->options, "set-remain-on-exit")) - options_set_number(&w->options, "remain-on-exit", 1); + if (options_get_number(s->options, "set-remain-on-exit")) + options_set_number(w->options, "remain-on-exit", 1); session_group_synchronize_from(s); - return (wl); } @@ -323,16 +416,30 @@ session_detach(struct session *s, struct winlink *wl) } /* Return if session has window. */ -struct winlink * +int session_has(struct session *s, struct window *w) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window == w) - return (wl); + return (1); } - return (NULL); + return (0); +} + +/* + * Return 1 if a window is linked outside this session (not including session + * groups). The window must be in this session! + */ +int +session_is_linked(struct session *s, struct window *w) +{ + struct session_group *sg; + + if ((sg = session_group_find(s)) != NULL) + return (w->references != session_group_count(sg)); + return (w->references != 1); } struct winlink * @@ -440,6 +547,7 @@ session_set_current(struct session *s, struct winlink *wl) tmate_sync_layout(); #endif + window_update_activity(wl->window); return (0); } @@ -511,6 +619,19 @@ session_group_remove(struct session *s) } } +/* Count number of sessions in session group. */ +u_int +session_group_count(struct session_group *sg) +{ + struct session *s; + u_int n; + + n = 0; + TAILQ_FOREACH(s, &sg->sessions, gentry) + n++; + return (n); +} + /* Synchronize a session to its session group. */ void session_group_synchronize_to(struct session *s) @@ -598,8 +719,9 @@ session_group_synchronize1(struct session *target, struct session *s) /* Then free the old winlinks list. */ while (!RB_EMPTY(&old_windows)) { wl = RB_ROOT(&old_windows); - if (winlink_find_by_window_id(&s->windows, wl->window->id) == NULL) - notify_window_unlinked(s, wl->window); + wl2 = winlink_find_by_window_id(&s->windows, wl->window->id); + if (wl2 == NULL) + notify_window_unlinked(s, wl->window); winlink_remove(&old_windows, wl); } } @@ -618,7 +740,7 @@ session_renumber_windows(struct session *s) RB_INIT(&s->windows); /* Start renumbering from the base-index if it's set. */ - new_idx = options_get_number(&s->options, "base-index"); + new_idx = options_get_number(s->options, "base-index"); new_curw_idx = 0; /* Go through the winlinks and assign new indexes. */ @@ -637,7 +759,7 @@ session_renumber_windows(struct session *s) memcpy(&old_lastw, &s->lastw, sizeof old_lastw); TAILQ_INIT(&s->lastw); TAILQ_FOREACH(wl, &old_lastw, sentry) { - wl_new = winlink_find_by_index(&s->windows, wl->idx); + wl_new = winlink_find_by_window(&s->windows, wl->window); if (wl_new != NULL) TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry); } diff --git a/signal.c b/signal.c index 56333522..9a4d58c2 100644 --- a/signal.c +++ b/signal.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -17,6 +17,8 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + #include #include @@ -30,7 +32,7 @@ struct event ev_sigusr1; struct event ev_sigwinch; void -set_signals(void(*handler)(int, short, unused void *)) +set_signals(void (*handler)(int, short, void *), void *arg) { struct sigaction sigact; @@ -47,17 +49,17 @@ set_signals(void(*handler)(int, short, unused void *)) if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); - signal_set(&ev_sighup, SIGHUP, handler, NULL); + signal_set(&ev_sighup, SIGHUP, handler, arg); signal_add(&ev_sighup, NULL); - signal_set(&ev_sigchld, SIGCHLD, handler, NULL); + signal_set(&ev_sigchld, SIGCHLD, handler, arg); signal_add(&ev_sigchld, NULL); - signal_set(&ev_sigcont, SIGCONT, handler, NULL); + signal_set(&ev_sigcont, SIGCONT, handler, arg); signal_add(&ev_sigcont, NULL); - signal_set(&ev_sigterm, SIGTERM, handler, NULL); + signal_set(&ev_sigterm, SIGTERM, handler, arg); signal_add(&ev_sigterm, NULL); - signal_set(&ev_sigusr1, SIGUSR1, handler, NULL); + signal_set(&ev_sigusr1, SIGUSR1, handler, arg); signal_add(&ev_sigusr1, NULL); - signal_set(&ev_sigwinch, SIGWINCH, handler, NULL); + signal_set(&ev_sigwinch, SIGWINCH, handler, arg); signal_add(&ev_sigwinch, NULL); } diff --git a/status.c b/status.c index 3eaa933d..c51878c5 100644 --- a/status.c +++ b/status.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -30,35 +30,167 @@ #include "tmux.h" #include "tmate.h" -char *status_redraw_get_left( - struct client *, time_t, int, struct grid_cell *, size_t *); -char *status_redraw_get_right( - struct client *, time_t, int, struct grid_cell *, size_t *); -char *status_find_job(struct client *, char **); -void status_job_free(void *); -void status_job_callback(struct job *); -char *status_print( - struct client *, struct winlink *, time_t, struct grid_cell *); -void status_replace1(struct client *, struct session *, struct winlink *, - struct window_pane *, char **, char **, char *, size_t, int); +char *status_redraw_get_left(struct client *, time_t, struct grid_cell *, + size_t *); +char *status_redraw_get_right(struct client *, time_t, struct grid_cell *, + size_t *); +char *status_print(struct client *, struct winlink *, time_t, + struct grid_cell *); +char *status_replace(struct client *, struct winlink *, const char *, time_t); void status_message_callback(int, short, void *); +void status_timer_callback(int, short, void *); 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 *); + +const char **status_prompt_complete_list(u_int *, const char *); +char *status_prompt_complete_prefix(const char **, u_int); +char *status_prompt_complete(struct session *, const char *); + +char *status_prompt_find_history_file(void); /* Status prompt history. */ -ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER; +#define PROMPT_HISTORY 100 +char **status_prompt_hlist; +u_int status_prompt_hsize; -/* Status output tree. */ -RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); - -/* Output tree comparison function. */ -int -status_out_cmp(struct status_out *so1, struct status_out *so2) +/* Find the history file to load/save from/to. */ +char * +status_prompt_find_history_file(void) { - return (strcmp(so1->cmd, so2->cmd)); + const char *home, *history_file; + char *path; + + history_file = options_get_string(global_options, "history-file"); + if (*history_file == '\0') + return (NULL); + if (*history_file == '/') + return (xstrdup(history_file)); + + if (history_file[0] != '~' || history_file[1] != '/') + return (NULL); + if ((home = find_home()) == NULL) + return (NULL); + xasprintf(&path, "%s%s", home, history_file + 1); + return (path); +} + +/* Load status prompt history from file. */ +void +status_prompt_load_history(void) +{ + FILE *f; + char *history_file, *line, *tmp; + size_t length; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("loading history from %s", history_file); + + f = fopen(history_file, "r"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (;;) { + if ((line = fgetln(f, &length)) == NULL) + break; + + if (length > 0) { + if (line[length - 1] == '\n') { + line[length - 1] = '\0'; + status_prompt_add_history(line); + } else { + tmp = xmalloc(length + 1); + memcpy(tmp, line, length); + tmp[length] = '\0'; + status_prompt_add_history(tmp); + free(tmp); + } + } + } + fclose(f); +} + +/* Save status prompt history to file. */ +void +status_prompt_save_history(void) +{ + FILE *f; + u_int i; + char *history_file; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("saving history to %s", history_file); + + f = fopen(history_file, "w"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (i = 0; i < status_prompt_hsize; i++) { + fputs(status_prompt_hlist[i], f); + fputc('\n', f); + } + fclose(f); + +} + +/* Status timer callback. */ +void +status_timer_callback(__unused int fd, __unused short events, void *arg) +{ + struct client *c = arg; + struct session *s = c->session; + struct timeval tv; + + evtimer_del(&c->status_timer); + + if (s == NULL) + return; + + if (c->message_string == NULL && c->prompt_string == NULL) + c->flags |= CLIENT_STATUS; + + timerclear(&tv); + tv.tv_sec = options_get_number(s->options, "status-interval"); + + if (tv.tv_sec != 0) + evtimer_add(&c->status_timer, &tv); + log_debug("client %p, status interval %d", c, (int)tv.tv_sec); +} + +/* Start status timer for client. */ +void +status_timer_start(struct client *c) +{ + struct session *s = c->session; + + if (event_initialized(&c->status_timer)) + evtimer_del(&c->status_timer); + else + evtimer_set(&c->status_timer, status_timer_callback, c); + + if (s != NULL && options_get_number(s->options, "status")) + status_timer_callback(-1, 0, c); +} + +/* Start status timer for all clients. */ +void +status_timer_start_all(void) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) + status_timer_start(c); } /* Get screen line of status line. -1 means off. */ @@ -67,39 +199,31 @@ status_at_line(struct client *c) { struct session *s = c->session; - if (!options_get_number(&s->options, "status")) + if (!options_get_number(s->options, "status")) return (-1); - if (options_get_number(&s->options, "status-position") == 0) + if (options_get_number(s->options, "status-position") == 0) return (0); return (c->tty.sy - 1); } /* Retrieve options for left string. */ char * -status_redraw_get_left(struct client *c, - time_t t, int utf8flag, struct grid_cell *gc, size_t *size) +status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc, + size_t *size) { struct session *s = c->session; + const char *template; char *left; - int fg, bg, attr; size_t leftlen; - fg = options_get_number(&s->options, "status-left-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(&s->options, "status-left-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(&s->options, "status-left-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, s->options, "status-left-style"); - left = status_replace(c, NULL, - NULL, NULL, options_get_string(&s->options, "status-left"), t, 1); + template = options_get_string(s->options, "status-left"); + left = status_replace(c, NULL, template, t); - *size = options_get_number(&s->options, "status-left-length"); - leftlen = screen_write_cstrlen(utf8flag, "%s", left); + *size = options_get_number(s->options, "status-left-length"); + leftlen = screen_write_cstrlen("%s", left); if (leftlen < *size) *size = leftlen; return (left); @@ -107,49 +231,45 @@ status_redraw_get_left(struct client *c, /* Retrieve options for right string. */ char * -status_redraw_get_right(struct client *c, - time_t t, int utf8flag, struct grid_cell *gc, size_t *size) +status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc, + size_t *size) { struct session *s = c->session; + const char *template; char *right; - int fg, bg, attr; size_t rightlen; - fg = options_get_number(&s->options, "status-right-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(&s->options, "status-right-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(&s->options, "status-right-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, s->options, "status-right-style"); - right = status_replace(c, NULL, - NULL, NULL, options_get_string(&s->options, "status-right"), t, 1); + template = options_get_string(s->options, "status-right"); + right = status_replace(c, NULL, template, t); - *size = options_get_number(&s->options, "status-right-length"); - rightlen = screen_write_cstrlen(utf8flag, "%s", right); + *size = options_get_number(s->options, "status-right-length"); + rightlen = screen_write_cstrlen("%s", right); if (rightlen < *size) *size = rightlen; return (right); } -/* Set window at window list position. */ -void -status_set_window_at(struct client *c, u_int x) +/* Get window at window list position. */ +struct window * +status_get_window_at(struct client *c, u_int x) { struct session *s = c->session; struct winlink *wl; + struct options *oo; + size_t len; x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { - if (x < wl->status_width && - session_select(s, wl->idx) == 0) { - server_redraw_session(s); - } - x -= wl->status_width + 1; + oo = wl->window->options; + len = strlen(options_get_string(oo, "window-status-separator")); + + if (x < wl->status_width) + return (wl->window); + x -= wl->status_width + len; } + return (NULL); } /* Draw status for client on the last lines of given context. */ @@ -167,10 +287,10 @@ status_redraw(struct client *c) u_int offset, needed; u_int wlstart, wlwidth, wlavailable, wloffset, wlsize; size_t llen, rlen, seplen; - int larrow, rarrow, utf8flag; + int larrow, rarrow; /* No status line? */ - if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) + if (c->tty.sy == 0 || !options_get_number(s->options, "status")) #ifdef TMATE if (c->tty.sy == 0 || !(c->flags & CLIENT_FORCE_STATUS)) #endif @@ -178,16 +298,11 @@ status_redraw(struct client *c) left = right = NULL; larrow = rarrow = 0; - /* Update status timer. */ - if (gettimeofday(&c->status_timer, NULL) != 0) - fatal("gettimeofday failed"); - t = c->status_timer.tv_sec; + /* Store current time. */ + t = time(NULL); /* Set up default colour. */ - memcpy(&stdgc, &grid_default_cell, sizeof gc); - colour_set_fg(&stdgc, options_get_number(&s->options, "status-fg")); - colour_set_bg(&stdgc, options_get_number(&s->options, "status-bg")); - stdgc.attr |= options_get_number(&s->options, "status-attr"); + style_apply(&stdgc, s->options, "status-style"); /* Create the target screen. */ memcpy(&old_status, &c->status, sizeof old_status); @@ -201,16 +316,15 @@ status_redraw(struct client *c) if (c->tty.sy <= 1) goto out; - /* Get UTF-8 flag. */ - utf8flag = options_get_number(&s->options, "status-utf8"); - /* Work out left and right strings. */ memcpy(&lgc, &stdgc, sizeof lgc); - left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen); + left = status_redraw_get_left(c, t, &lgc, &llen); memcpy(&rgc, &stdgc, sizeof rgc); - right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen); + right = status_redraw_get_right(c, t, &rgc, &rlen); +#ifdef TMATE tmate_status(left, right); +#endif /* * Figure out how much space we have for the window list. If there @@ -218,9 +332,9 @@ status_redraw(struct client *c) */ needed = 0; if (llen != 0) - needed += llen + 1; + needed += llen; if (rlen != 0) - needed += rlen + 1; + needed += rlen; if (c->tty.sx == 0 || c->tty.sx <= needed) goto out; wlavailable = c->tty.sx - needed; @@ -231,15 +345,14 @@ status_redraw(struct client *c) free(wl->status_text); memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell); wl->status_text = status_print(c, wl, t, &wl->status_cell); - wl->status_width = - screen_write_cstrlen(utf8flag, "%s", wl->status_text); + wl->status_width = screen_write_cstrlen("%s", wl->status_text); if (wl == s->curw) wloffset = wlwidth; - oo = &wl->window->options; + oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); - seplen = screen_write_strlen(utf8flag, "%s", sep); + seplen = screen_write_strlen("%s", sep); wlwidth += wl->status_width + seplen; } @@ -249,12 +362,12 @@ status_redraw(struct client *c) /* And draw the window list into it. */ screen_write_start(&ctx, NULL, &window_list); RB_FOREACH(wl, winlinks, &s->windows) { - screen_write_cnputs(&ctx, - -1, &wl->status_cell, utf8flag, "%s", wl->status_text); + screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s", + wl->status_text); - oo = &wl->window->options; + oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); - screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep); + screen_write_nputs(&ctx, -1, &stdgc, "%s", sep); } screen_write_stop(&ctx); @@ -325,10 +438,8 @@ draw: /* Draw the left string and arrow. */ screen_write_cursormove(&ctx, 0, 0); - if (llen != 0) { - screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left); - screen_write_putc(&ctx, &stdgc, ' '); - } + if (llen != 0) + screen_write_cnputs(&ctx, llen, &lgc, "%s", left); if (larrow != 0) { memcpy(&gc, &stdgc, sizeof gc); if (larrow == -1) @@ -338,26 +449,24 @@ draw: /* Draw the right string and arrow. */ if (rarrow != 0) { - screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, 0); + screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0); memcpy(&gc, &stdgc, sizeof gc); if (rarrow == -1) gc.attr ^= GRID_ATTR_REVERSE; screen_write_putc(&ctx, &gc, '>'); } else - screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0); - if (rlen != 0) { - screen_write_putc(&ctx, &stdgc, ' '); - screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right); - } + screen_write_cursormove(&ctx, c->tty.sx - rlen, 0); + if (rlen != 0) + screen_write_cnputs(&ctx, rlen, &rgc, "%s", right); /* Figure out the offset for the window list. */ if (llen != 0) - wloffset = llen + 1; + wloffset = llen; else wloffset = 0; if (wlwidth < wlavailable) { - switch (options_get_number(&s->options, "status-justify")) { - case 1: /* centered */ + switch (options_get_number(s->options, "status-justify")) { + case 1: /* centred */ wloffset += (wlavailable - wlwidth) / 2; break; case 2: /* right */ @@ -388,397 +497,67 @@ out: return (1); } -/* Replace a single special sequence (prefixed by #). */ -void -status_replace1(struct client *c, struct session *s, struct winlink *wl, - struct window_pane *wp, char **iptr, char **optr, char *out, - size_t outsize, int jobsflag) -{ - char ch, tmp[256], *ptr, *endptr, *freeptr; - size_t ptrlen; - long limit; - u_int idx; - - errno = 0; - limit = strtol(*iptr, &endptr, 10); - if ((limit == 0 && errno != EINVAL) || - (limit == LONG_MIN && errno != ERANGE) || - (limit == LONG_MAX && errno != ERANGE) || - limit != 0) - *iptr = endptr; - if (limit <= 0) - limit = LONG_MAX; - - freeptr = NULL; - - switch (*(*iptr)++) { - case '(': - if (!jobsflag) { - ch = ')'; - goto skip_to; - } - if ((ptr = status_find_job(c, iptr)) == NULL) - return; - goto do_replace; - case 'D': - xsnprintf(tmp, sizeof tmp, "%%%u", wp->id); - ptr = tmp; - goto do_replace; - case 'H': - if (gethostname(tmp, sizeof tmp) != 0) - fatal("gethostname failed"); - ptr = tmp; - goto do_replace; - case 'h': - if (gethostname(tmp, sizeof tmp) != 0) - fatal("gethostname failed"); - if ((ptr = strchr(tmp, '.')) != NULL) - *ptr = '\0'; - ptr = tmp; - goto do_replace; - case 'I': - xsnprintf(tmp, sizeof tmp, "%d", wl->idx); - ptr = tmp; - goto do_replace; - case 'P': - if (window_pane_index(wp, &idx) != 0) - fatalx("index not found"); - xsnprintf(tmp, sizeof tmp, "%u", idx); - ptr = tmp; - goto do_replace; - case 'S': - ptr = s->name; - goto do_replace; - case 'T': - ptr = wp->base.title; - goto do_replace; - case 'W': - ptr = wl->window->name; - goto do_replace; - case 'F': - ptr = window_printable_flags(s, wl); - freeptr = ptr; - goto do_replace; - case '[': - /* - * Embedded style, handled at display time. Leave present and - * skip input until ]. - */ - ch = ']'; - goto skip_to; - case '{': - ptr = (char *) "#{"; - goto do_replace; - case '#': - *(*optr)++ = '#'; - break; - } - - return; - -do_replace: - ptrlen = strlen(ptr); - if ((size_t) limit < ptrlen) - ptrlen = limit; - - if (*optr + ptrlen >= out + outsize - 1) - goto out; - while (ptrlen > 0 && *ptr != '\0') { - *(*optr)++ = *ptr++; - ptrlen--; - } - -out: - free(freeptr); - return; - -skip_to: - *(*optr)++ = '#'; - - (*iptr)--; /* include ch */ - while (**iptr != ch && **iptr != '\0') { - if (*optr >= out + outsize - 1) - break; - *(*optr)++ = *(*iptr)++; - } -} - /* Replace special sequences in fmt. */ char * -status_replace(struct client *c, struct session *s, struct winlink *wl, - struct window_pane *wp, const char *fmt, time_t t, int jobsflag) +status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) { - static char out[BUFSIZ]; - char in[BUFSIZ], ch, *iptr, *optr, *expanded; - size_t len; struct format_tree *ft; + char *expanded; if (fmt == NULL) return (xstrdup("")); - if (s == NULL) - s = c->session; - if (wl == NULL) - wl = s->curw; - if (wp == NULL) - wp = wl->window->active; + if (c->flags & CLIENT_STATUSFORCE) + ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE); + else + ft = format_create(NULL, FORMAT_STATUS); + format_defaults(ft, c, NULL, wl, NULL); - len = strftime(in, sizeof in, fmt, localtime(&t)); - in[len] = '\0'; + expanded = format_expand_time(ft, fmt, t); - iptr = in; - optr = out; - - while (*iptr != '\0') { - if (optr >= out + (sizeof out) - 1) - break; - ch = *iptr++; - - if (ch != '#' || *iptr == '\0') { - *optr++ = ch; - continue; - } - status_replace1( - c, s, wl, wp, &iptr, &optr, out, sizeof out, jobsflag); - } - *optr = '\0'; - - ft = format_create(); - format_client(ft, c); - format_session(ft, s); - format_winlink(ft, s, wl); - format_window_pane(ft, wp); - expanded = format_expand(ft, out); format_free(ft); return (expanded); } -/* Figure out job name and get its result, starting it off if necessary. */ -char * -status_find_job(struct client *c, char **iptr) -{ - struct status_out *so, so_find; - char *cmd; - int lastesc; - size_t len; - - if (**iptr == '\0') - return (NULL); - if (**iptr == ')') { /* no command given */ - (*iptr)++; - return (NULL); - } - - cmd = xmalloc(strlen(*iptr) + 1); - len = 0; - - lastesc = 0; - for (; **iptr != '\0'; (*iptr)++) { - if (!lastesc && **iptr == ')') - break; /* unescaped ) is the end */ - if (!lastesc && **iptr == '\\') { - lastesc = 1; - continue; /* skip \ if not escaped */ - } - lastesc = 0; - cmd[len++] = **iptr; - } - if (**iptr == '\0') /* no terminating ) */ { - free(cmd); - return (NULL); - } - (*iptr)++; /* skip final ) */ - cmd[len] = '\0'; - - /* First try in the new tree. */ - so_find.cmd = cmd; - so = RB_FIND(status_out_tree, &c->status_new, &so_find); - if (so != NULL && so->out != NULL) { - free(cmd); - return (so->out); - } - - /* If not found at all, start the job and add to the tree. */ - if (so == NULL) { - job_run(cmd, NULL, status_job_callback, status_job_free, c); - c->references++; - - so = xmalloc(sizeof *so); - so->cmd = xstrdup(cmd); - so->out = NULL; - RB_INSERT(status_out_tree, &c->status_new, so); - } - - /* Lookup in the old tree. */ - so_find.cmd = cmd; - so = RB_FIND(status_out_tree, &c->status_old, &so_find); - free(cmd); - if (so != NULL) - return (so->out); - return (NULL); -} - -/* Free job tree. */ -void -status_free_jobs(struct status_out_tree *sotree) -{ - struct status_out *so, *so_next; - - so_next = RB_MIN(status_out_tree, sotree); - while (so_next != NULL) { - so = so_next; - so_next = RB_NEXT(status_out_tree, sotree, so); - - RB_REMOVE(status_out_tree, sotree, so); - free(so->out); - free(so->cmd); - free(so); - } -} - -/* Update jobs on status interval. */ -void -status_update_jobs(struct client *c) -{ - /* Free the old tree. */ - status_free_jobs(&c->status_old); - - /* Move the new to old. */ - memcpy(&c->status_old, &c->status_new, sizeof c->status_old); - RB_INIT(&c->status_new); -} - -/* Free status job. */ -void -status_job_free(void *data) -{ - struct client *c = data; - - c->references--; -} - -/* Job has finished: save its result. */ -void -status_job_callback(struct job *job) -{ - struct client *c = job->data; - struct status_out *so, so_find; - char *line, *buf; - size_t len; - - if (c->flags & CLIENT_DEAD) - return; - - so_find.cmd = job->cmd; - so = RB_FIND(status_out_tree, &c->status_new, &so_find); - if (so == NULL || so->out != NULL) - return; - - buf = NULL; - if ((line = evbuffer_readline(job->event->input)) == NULL) { - len = EVBUFFER_LENGTH(job->event->input); - buf = xmalloc(len + 1); - if (len != 0) - memcpy(buf, EVBUFFER_DATA(job->event->input), len); - buf[len] = '\0'; - } else - buf = line; - - so->out = buf; - server_status_client(c); -} - /* Return winlink status line entry and adjust gc as necessary. */ char * -status_print( - struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc) +status_print(struct client *c, struct winlink *wl, time_t t, + struct grid_cell *gc) { - struct options *oo = &wl->window->options; + struct options *oo = wl->window->options; struct session *s = c->session; const char *fmt; char *text; - int fg, bg, attr; - fg = options_get_number(oo, "window-status-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, oo, "window-status-style"); fmt = options_get_string(oo, "window-status-format"); if (wl == s->curw) { - fg = options_get_number(oo, "window-status-current-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-current-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-current-attr"); - if (attr != 0) - gc->attr = attr; + style_apply_update(gc, oo, "window-status-current-style"); fmt = options_get_string(oo, "window-status-current-format"); } - if (wl == TAILQ_FIRST(&s->lastw)) { - fg = options_get_number(oo, "window-status-last-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-last-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-last-attr"); - if (attr != 0) - gc->attr = attr; - } + if (wl == TAILQ_FIRST(&s->lastw)) + style_apply_update(gc, oo, "window-status-last-style"); - if (wl->flags & WINLINK_BELL) { - fg = options_get_number(oo, "window-status-bell-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-bell-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-bell-attr"); - if (attr != 0) - gc->attr = attr; - } else if (wl->flags & WINLINK_CONTENT) { - fg = options_get_number(oo, "window-status-content-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-content-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-content-attr"); - if (attr != 0) - gc->attr = attr; - } else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) { - fg = options_get_number(oo, "window-status-activity-fg"); - if (fg != 8) - colour_set_fg(gc, fg); - bg = options_get_number(oo, "window-status-activity-bg"); - if (bg != 8) - colour_set_bg(gc, bg); - attr = options_get_number(oo, "window-status-activity-attr"); - if (attr != 0) - gc->attr = attr; - } + if (wl->flags & WINLINK_BELL) + style_apply_update(gc, oo, "window-status-bell-style"); + else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) + style_apply_update(gc, oo, "window-status-activity-style"); - text = status_replace(c, NULL, wl, NULL, fmt, t, 1); + text = status_replace(c, wl, fmt, t); return (text); } /* Set a status line message. */ -void printflike2 +void status_message_set(struct client *c, const char *fmt, ...) { struct timeval tv; - struct session *s = c->session; - struct message_entry *msg; + struct message_entry *msg, *msg1; va_list ap; int delay; - u_int i, limit; + u_int first, limit; + + limit = options_get_number(global_options, "message-limit"); status_prompt_clear(c); status_message_clear(c); @@ -787,33 +566,35 @@ status_message_set(struct client *c, const char *fmt, ...) xvasprintf(&c->message_string, fmt, ap); va_end(ap); - ARRAY_EXPAND(&c->message_log, 1); - msg = &ARRAY_LAST(&c->message_log); + msg = xcalloc(1, sizeof *msg); msg->msg_time = time(NULL); + msg->msg_num = c->message_next++; msg->msg = xstrdup(c->message_string); + TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - if (s == NULL) - limit = 0; - else - limit = options_get_number(&s->options, "message-limit"); - if (ARRAY_LENGTH(&c->message_log) > limit) { - limit = ARRAY_LENGTH(&c->message_log) - limit; - for (i = 0; i < limit; i++) { - msg = &ARRAY_FIRST(&c->message_log); - free(msg->msg); - ARRAY_REMOVE(&c->message_log, 0); - } + first = c->message_next - limit; + TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { + if (msg->msg_num >= first) + continue; + free(msg->msg); + TAILQ_REMOVE(&c->message_log, msg, entry); + free(msg); } +#ifdef TMATE /* FIXME tmux: session can be NULL */ - delay = options_get_number(&c->session->options, "display-time"); - tv.tv_sec = delay / 1000; - tv.tv_usec = (delay % 1000) * 1000L; +#endif - if (event_initialized (&c->message_timer)) - evtimer_del(&c->message_timer); - evtimer_set(&c->message_timer, status_message_callback, c); - evtimer_add(&c->message_timer, &tv); + delay = options_get_number(c->session->options, "display-time"); + if (delay > 0) { + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + + if (event_initialized(&c->message_timer)) + evtimer_del(&c->message_timer); + evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); + } c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_STATUS; @@ -843,7 +624,7 @@ status_message_clear(struct client *c) /* Clear status line message after timer expires. */ void -status_message_callback(unused int fd, unused short event, void *data) +status_message_callback(__unused int fd, __unused short event, void *data) { struct client *c = data; @@ -859,28 +640,22 @@ status_message_redraw(struct client *c) struct screen old_status; size_t len; struct grid_cell gc; - int utf8flag; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(&s->options, "status-utf8"); - - len = screen_write_strlen(utf8flag, "%s", c->message_string); + len = screen_write_strlen("%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; - memcpy(&gc, &grid_default_cell, sizeof gc); - colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); - colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); - gc.attr |= options_get_number(&s->options, "message-attr"); + style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); - screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string); + screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); for (; len < c->tty.sx; len++) screen_write_putc(&ctx, &gc, ' '); @@ -900,16 +675,20 @@ status_prompt_set(struct client *c, const char *msg, const char *input, int (*callbackfn)(void *, const char *), void (*freefn)(void *), void *data, int flags) { - int keys; + struct format_tree *ft; + int keys; + time_t t; + + ft = format_create(NULL, 0); + format_defaults(ft, c, NULL, NULL, NULL); + t = time(NULL); status_message_clear(c); status_prompt_clear(c); - c->prompt_string = status_replace(c, NULL, NULL, NULL, msg, - time(NULL), 0); + c->prompt_string = format_expand_time(ft, msg, t); - c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input, - time(NULL), 0); + c->prompt_buffer = format_expand_time(ft, input, t); c->prompt_index = strlen(c->prompt_buffer); c->prompt_callbackfn = callbackfn; @@ -920,7 +699,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, c->prompt_flags = flags; - keys = options_get_number(&c->session->options, "status-keys"); + keys = options_get_number(c->session->options, "status-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit); else @@ -928,6 +707,8 @@ status_prompt_set(struct client *c, const char *msg, const char *input, c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_STATUS; + + format_free(ft); } /* Remove status line prompt. */ @@ -956,18 +737,25 @@ status_prompt_clear(struct client *c) void status_prompt_update(struct client *c, const char *msg, const char *input) { + struct format_tree *ft; + time_t t; + + ft = format_create(NULL, 0); + format_defaults(ft, c, NULL, NULL, NULL); + t = time(NULL); + free(c->prompt_string); - c->prompt_string = status_replace(c, NULL, NULL, NULL, msg, - time(NULL), 0); + c->prompt_string = format_expand_time(ft, msg, t); free(c->prompt_buffer); - c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input, - time(NULL), 0); + c->prompt_buffer = format_expand_time(ft, input, t); c->prompt_index = strlen(c->prompt_buffer); c->prompt_hindex = 0; c->flags |= CLIENT_STATUS; + + format_free(ft); } /* Draw client prompt on status line of present else on last line. */ @@ -978,49 +766,40 @@ status_prompt_redraw(struct client *c) struct session *s = c->session; struct screen old_status; size_t i, size, left, len, off; - struct grid_cell gc, *gcp; - int utf8flag; + struct grid_cell gc; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(&s->options, "status-utf8"); - - len = screen_write_strlen(utf8flag, "%s", c->prompt_string); + len = screen_write_strlen("%s", c->prompt_string); if (len > c->tty.sx) len = c->tty.sx; off = 0; - memcpy(&gc, &grid_default_cell, sizeof gc); /* Change colours for command mode. */ - if (c->prompt_mdata.mode == 1) { - colour_set_fg(&gc, options_get_number(&s->options, "message-command-fg")); - colour_set_bg(&gc, options_get_number(&s->options, "message-command-bg")); - gc.attr |= options_get_number(&s->options, "message-command-attr"); - } else { - colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); - colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); - gc.attr |= options_get_number(&s->options, "message-attr"); - } + if (c->prompt_mdata.mode == 1) + style_apply(&gc, s->options, "message-command-style"); + else + style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); - screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string); + screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string); left = c->tty.sx - len; if (left != 0) { - size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer); + size = screen_write_strlen("%s", c->prompt_buffer); if (c->prompt_index >= left) { off = c->prompt_index - left + 1; if (c->prompt_index == size) left--; size = left; } - screen_write_nputs( - &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off); + screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer + + off); for (i = len + size; i < c->tty.sx; i++) screen_write_putc(&ctx, &gc, ' '); @@ -1030,8 +809,9 @@ status_prompt_redraw(struct client *c) /* Apply fake cursor. */ off = len + c->prompt_index - off; - gcp = grid_view_get_cell(c->status.grid, off, 0); - gcp->attr ^= GRID_ATTR_REVERSE; + grid_view_get_cell(c->status.grid, off, 0, &gc); + gc.attr ^= GRID_ATTR_REVERSE; + grid_view_set_cell(c->status.grid, off, 0, &gc); if (grid_compare(c->status.grid, old_status.grid) == 0) { screen_free(&old_status); @@ -1043,16 +823,15 @@ status_prompt_redraw(struct client *c) /* Handle keys in prompt. */ void -status_prompt_key(struct client *c, int key) +status_prompt_key(struct client *c, key_code key) { struct session *sess = c->session; - struct options *oo = &sess->options; + struct options *oo = sess->options; struct paste_buffer *pb; char *s, *first, *last, word[64], swapc; - const char *histstr; - const char *wsep = NULL; + const char *histstr, *bufdata, *wsep = NULL; u_char ch; - size_t size, n, off, idx; + size_t size, n, off, idx, bufsize; size = strlen(c->prompt_buffer); switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) { @@ -1120,7 +899,7 @@ status_prompt_key(struct client *c, int key) word[last - first] = '\0'; /* And try to complete it. */ - if ((s = status_prompt_complete(word)) == NULL) + if ((s = status_prompt_complete(sess, word)) == NULL) break; /* Trim out word. */ @@ -1131,7 +910,7 @@ status_prompt_key(struct client *c, int key) /* Insert the new word. */ size += strlen(s); off = first - c->prompt_buffer; - c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1); + c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1); first = c->prompt_buffer + off; memmove(first + strlen(s), first, n); memcpy(first, s, strlen(s)); @@ -1155,6 +934,7 @@ status_prompt_key(struct client *c, int key) } break; case MODEKEYEDIT_DELETE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTE: if (c->prompt_index != size) { memmove(c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index + 1, @@ -1163,11 +943,13 @@ status_prompt_key(struct client *c, int key) } break; case MODEKEYEDIT_DELETELINE: + case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: *c->prompt_buffer = '\0'; c->prompt_index = 0; c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_DELETETOENDOFLINE: + case MODEKEYEDIT_SWITCHMODECHANGELINE: if (c->prompt_index < size) { c->prompt_buffer[c->prompt_index] = '\0'; c->flags |= CLIENT_STATUS; @@ -1246,6 +1028,11 @@ status_prompt_key(struct client *c, int key) break; } + /* Back up to the end-of-word like vi. */ + if (options_get_number(oo, "status-keys") == MODEKEY_VI && + c->prompt_index != 0) + c->prompt_index--; + c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PREVIOUSSPACE: @@ -1293,24 +1080,25 @@ status_prompt_key(struct client *c, int key) c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PASTE: - if ((pb = paste_get_top(&global_buffers)) == NULL) + if ((pb = paste_get_top(NULL)) == NULL) break; - for (n = 0; n < pb->size; n++) { - ch = (u_char) pb->data[n]; + bufdata = paste_buffer_data(pb, &bufsize); + for (n = 0; n < bufsize; n++) { + ch = (u_char)bufdata[n]; if (ch < 32 || ch == 127) break; } - c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1); + c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1); if (c->prompt_index == size) { - memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); c->prompt_index += n; c->prompt_buffer[c->prompt_index] = '\0'; } else { memmove(c->prompt_buffer + c->prompt_index + n, c->prompt_buffer + c->prompt_index, size + 1 - c->prompt_index); - memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); c->prompt_index += n; } @@ -1339,9 +1127,9 @@ status_prompt_key(struct client *c, int key) status_prompt_clear(c); break; case MODEKEY_OTHER: - if ((key & 0xff00) != 0 || key < 32 || key == 127) + if (key <= 0x1f || key >= 0x7f) break; - c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2); + c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2); if (c->prompt_index == size) { c->prompt_buffer[c->prompt_index++] = key; @@ -1354,8 +1142,8 @@ status_prompt_key(struct client *c, int key) } if (c->prompt_flags & PROMPT_SINGLE) { - 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); } @@ -1370,114 +1158,223 @@ status_prompt_key(struct client *c, int key) 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. + * 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) + if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) return (NULL); (*idx)++; - return (ARRAY_ITEM(&status_prompt_history, size - *idx)); + return (status_prompt_hlist[status_prompt_hsize - *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) + if (status_prompt_hsize == 0 || *idx == 0) return (""); (*idx)--; if (*idx == 0) return (""); - return (ARRAY_ITEM(&status_prompt_history, size - *idx)); + return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Add line to the history. */ void status_prompt_add_history(const char *line) { - u_int size; + size_t size; - size = ARRAY_LENGTH(&status_prompt_history); - if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0) + if (status_prompt_hsize > 0 && + strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) return; - if (size == PROMPT_HISTORY) { - free(ARRAY_FIRST(&status_prompt_history)); - ARRAY_REMOVE(&status_prompt_history, 0); + if (status_prompt_hsize == PROMPT_HISTORY) { + free(status_prompt_hlist[0]); + + size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; + memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); + + status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); + return; } - ARRAY_ADD(&status_prompt_history, xstrdup(line)); + status_prompt_hlist = xreallocarray(status_prompt_hlist, + status_prompt_hsize + 1, sizeof *status_prompt_hlist); + status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); +} + +/* Build completion list. */ +const char ** +status_prompt_complete_list(u_int *size, const char *s) +{ + const char **list = NULL, **layout; + const struct cmd_entry **cmdent; + const struct options_table_entry *oe; + const char *layouts[] = { + "even-horizontal", "even-vertical", "main-horizontal", + "main-vertical", "tiled", NULL + }; + + *size = 0; + for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { + if (strncmp((*cmdent)->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = (*cmdent)->name; + } + } + for (oe = options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = oe->name; + } + } + for (layout = layouts; *layout != NULL; layout++) { + if (strncmp(*layout, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = *layout; + } + } + return (list); +} + +/* Find longest prefix. */ +char * +status_prompt_complete_prefix(const char **list, u_int size) +{ + char *out; + u_int i; + size_t j; + + out = xstrdup(list[0]); + for (i = 1; i < size; i++) { + j = strlen(list[i]); + if (j > strlen(out)) + j = strlen(out); + for (; j > 0; j--) { + if (out[j - 1] != list[i][j - 1]) + out[j - 1] = '\0'; + } + } + return (out); } /* Complete word. */ char * -status_prompt_complete(const char *s) +status_prompt_complete(struct session *sess, const char *s) { - const struct cmd_entry **cmdent; - const struct options_table_entry *oe; - ARRAY_DECL(, const char *) list; - char *prefix, *s2; - u_int i; - size_t j; + const char **list = NULL, *colon; + u_int size = 0, i; + struct session *s_loop; + struct winlink *wl; + struct window *w; + char *copy, *out, *tmp; if (*s == '\0') return (NULL); + out = NULL; - /* First, build a list of all the possible matches. */ - ARRAY_INIT(&list); - for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { - if (strncmp((*cmdent)->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, (*cmdent)->name); - } - for (oe = server_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); - } - for (oe = session_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); - } - for (oe = window_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); + if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { + list = status_prompt_complete_list(&size, s); + if (size == 0) + out = NULL; + else if (size == 1) + xasprintf(&out, "%s ", list[0]); + else + out = status_prompt_complete_prefix(list, size); + free(list); + return (out); } + copy = xstrdup(s); - /* If none, bail now. */ - if (ARRAY_LENGTH(&list) == 0) { - ARRAY_FREE(&list); - return (NULL); - } + colon = ":"; + if (copy[strlen(copy) - 1] == ':') + copy[strlen(copy) - 1] = '\0'; + else + colon = ""; + s = copy + 2; - /* If an exact match, return it, with a trailing space. */ - if (ARRAY_LENGTH(&list) == 1) { - xasprintf(&s2, "%s ", ARRAY_FIRST(&list)); - ARRAY_FREE(&list); - return (s2); - } - - /* Now loop through the list and find the longest common prefix. */ - prefix = xstrdup(ARRAY_FIRST(&list)); - for (i = 1; i < ARRAY_LENGTH(&list); i++) { - s = ARRAY_ITEM(&list, i); - - j = strlen(s); - if (j > strlen(prefix)) - j = strlen(prefix); - for (; j > 0; j--) { - if (prefix[j - 1] != s[j - 1]) - prefix[j - 1] = '\0'; + RB_FOREACH(s_loop, sessions, &sessions) { + if (strncmp(s_loop->name, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 2, sizeof *list); + list[size++] = s_loop->name; } } + if (size == 1) { + out = xstrdup(list[0]); + if (session_find(list[0]) != NULL) + colon = ":"; + } else if (size != 0) + out = status_prompt_complete_prefix(list, size); + if (out != NULL) { + xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); + out = tmp; + goto found; + } - ARRAY_FREE(&list); - return (prefix); + colon = ""; + if (*s == ':') { + RB_FOREACH(wl, winlinks, &sess->windows) { + xasprintf(&tmp, ":%s", wl->window->name); + if (strncmp(tmp, s, strlen(s)) == 0){ + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + + xasprintf(&tmp, ":%d", wl->idx); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + } + } else { + RB_FOREACH(s_loop, sessions, &sessions) { + RB_FOREACH(wl, winlinks, &s_loop->windows) { + w = wl->window; + + xasprintf(&tmp, "%s:%s", s_loop->name, w->name); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + + xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + } + } + } + if (size == 1) { + out = xstrdup(list[0]); + colon = " "; + } else if (size != 0) + out = status_prompt_complete_prefix(list, size); + if (out != NULL) { + xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); + out = tmp; + } + + for (i = 0; i < size; i++) + free((void *)list[i]); + +found: + free(copy); + free(list); + return (out); } diff --git a/style.c b/style.c new file mode 100644 index 00000000..c00b0fee --- /dev/null +++ b/style.c @@ -0,0 +1,266 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2014 Tiago Cunha + * + * 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 + +#include + +#include "tmux.h" + +/* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */ +int +style_parse(const struct grid_cell *defgc, struct grid_cell *gc, + const char *in) +{ + struct grid_cell savedgc; + const char delimiters[] = " ,"; + char tmp[32]; + int val; + size_t end; + u_char fg, bg, attr, flags; + + if (*in == '\0') + return (0); + if (strchr(delimiters, in[strlen(in) - 1]) != NULL) + return (-1); + memcpy(&savedgc, gc, sizeof savedgc); + + fg = gc->fg; + bg = gc->bg; + attr = gc->attr; + flags = gc->flags; + do { + end = strcspn(in, delimiters); + if (end > (sizeof tmp) - 1) + goto error; + memcpy(tmp, in, end); + tmp[end] = '\0'; + + if (strcasecmp(tmp, "default") == 0) { + fg = defgc->fg; + bg = defgc->bg; + attr = defgc->attr; + flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + flags |= + defgc->flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); + } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { + if ((val = colour_fromstring(tmp + 3)) == -1) + goto error; + if (*in == 'f' || *in == 'F') { + if (val != 8) { + if (val & 0x100) { + flags |= GRID_FLAG_FG256; + val &= ~0x100; + } else + flags &= ~GRID_FLAG_FG256; + fg = val; + } else { + fg = defgc->fg; + flags &= ~GRID_FLAG_FG256; + flags |= defgc->flags & GRID_FLAG_FG256; + } + } else if (*in == 'b' || *in == 'B') { + if (val != 8) { + if (val & 0x100) { + flags |= GRID_FLAG_BG256; + val &= ~0x100; + } else + flags &= ~GRID_FLAG_BG256; + bg = val; + } else { + bg = defgc->bg; + flags &= ~GRID_FLAG_BG256; + flags |= defgc->flags & GRID_FLAG_BG256; + } + } else + goto error; + } else if (strcasecmp(tmp, "none") == 0) + attr = 0; + else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { + if ((val = attributes_fromstring(tmp + 2)) == -1) + goto error; + attr &= ~val; + } else { + if ((val = attributes_fromstring(tmp)) == -1) + goto error; + attr |= val; + } + + in += end + strspn(in + end, delimiters); + } while (*in != '\0'); + gc->fg = fg; + gc->bg = bg; + gc->attr = attr; + gc->flags = flags; + + return (0); + +error: + memcpy(gc, &savedgc, sizeof *gc); + return (-1); +} + +/* Convert style to a string. */ +const char * +style_tostring(struct grid_cell *gc) +{ + int c, off = 0, comma = 0; + static char s[256]; + + *s = '\0'; + + if (gc->fg != 8 || gc->flags & GRID_FLAG_FG256) { + if (gc->flags & GRID_FLAG_FG256) + c = gc->fg | 0x100; + else + c = gc->fg; + off += xsnprintf(s, sizeof s, "fg=%s", colour_tostring(c)); + comma = 1; + } + + if (gc->bg != 8 || gc->flags & GRID_FLAG_BG256) { + if (gc->flags & GRID_FLAG_BG256) + c = gc->bg | 0x100; + else + c = gc->bg; + off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", + comma ? "," : "", colour_tostring(c)); + comma = 1; + } + + if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) { + xsnprintf(s + off, sizeof s - off, "%s%s", + comma ? "," : "", attributes_tostring(gc->attr)); + } + + if (*s == '\0') + return ("default"); + return (s); +} + +/* Synchronize new -style option with the old one. */ +void +style_update_new(struct options *oo, const char *name, const char *newname) +{ + int value; + struct grid_cell *gc; + struct options_entry *o; + + /* It's a colour or attribute, but with no -style equivalent. */ + if (newname == NULL) + return; + + o = options_find1(oo, newname); + if (o == NULL) + o = options_set_style(oo, newname, "default", 0); + gc = &o->style; + + o = options_find1(oo, name); + if (o == NULL) + o = options_set_number(oo, name, 8); + value = o->num; + + if (strstr(name, "-bg") != NULL) + colour_set_bg(gc, value); + else if (strstr(name, "-fg") != NULL) + colour_set_fg(gc, value); + else if (strstr(name, "-attr") != NULL) + gc->attr = value; +} + +/* Synchronize all the old options with the new -style one. */ +void +style_update_old(struct options *oo, const char *name, struct grid_cell *gc) +{ + char newname[128]; + int c, size; + + size = strrchr(name, '-') - name; + + if (gc->flags & GRID_FLAG_BG256) + c = gc->bg | 0x100; + else + c = gc->bg; + xsnprintf(newname, sizeof newname, "%.*s-bg", size, name); + options_set_number(oo, newname, c); + + if (gc->flags & GRID_FLAG_FG256) + c = gc->fg | 0x100; + else + c = gc->fg; + xsnprintf(newname, sizeof newname, "%.*s-fg", size, name); + options_set_number(oo, newname, c); + + xsnprintf(newname, sizeof newname, "%.*s-attr", size, name); + options_set_number(oo, newname, gc->attr); +} + +/* Apply a style. */ +void +style_apply(struct grid_cell *gc, struct options *oo, const char *name) +{ + struct grid_cell *gcp; + + memcpy(gc, &grid_default_cell, sizeof *gc); + gcp = options_get_style(oo, name); + if (gcp->flags & GRID_FLAG_FG256) + colour_set_fg(gc, gcp->fg | 0x100); + else + colour_set_fg(gc, gcp->fg); + if (gcp->flags & GRID_FLAG_BG256) + colour_set_bg(gc, gcp->bg | 0x100); + else + colour_set_bg(gc, gcp->bg); + gc->attr |= gcp->attr; +} + +/* Apply a style, updating if default. */ +void +style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) +{ + struct grid_cell *gcp; + + gcp = options_get_style(oo, name); + if (gcp->fg != 8 || gcp->flags & GRID_FLAG_FG256) { + if (gcp->flags & GRID_FLAG_FG256) + colour_set_fg(gc, gcp->fg | 0x100); + else + colour_set_fg(gc, gcp->fg); + } + if (gcp->bg != 8 || gcp->flags & GRID_FLAG_BG256) { + if (gcp->flags & GRID_FLAG_BG256) + colour_set_bg(gc, gcp->bg | 0x100); + else + colour_set_bg(gc, gcp->bg); + } + if (gcp->attr != 0) + gc->attr |= gcp->attr; +} + +/* Check if two styles are the same. */ +int +style_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + return gc1->fg == gc2->fg && + gc1->bg == gc2->bg && + (gc1->flags & ~GRID_FLAG_PADDING) == + (gc2->flags & ~GRID_FLAG_PADDING) && + (gc1->attr & ~GRID_ATTR_CHARSET) == + (gc2->attr & ~GRID_ATTR_CHARSET); +} diff --git a/tmate-debug.c b/tmate-debug.c index 9e160c43..61a8f676 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "tmate.h" #if DEBUG @@ -75,7 +76,7 @@ void tmate_print_trace(void) } -static void handle_sigsegv(int sig) +static void handle_sigsegv(__unused int sig) { /* TODO send stack trace to server */ tmate_info("CRASH, printing stack trace"); diff --git a/tmate-decoder.c b/tmate-decoder.c index a0434d79..a3821e0f 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -102,7 +102,7 @@ static void tmate_client_pane_key(struct tmate_unpacker *uk) if (!wp) return; - window_pane_key(wp, s, key); + window_pane_key(wp, NULL, s, key, NULL); } static void tmate_client_resize(struct tmate_unpacker *uk) @@ -115,11 +115,15 @@ static void tmate_client_resize(struct tmate_unpacker *uk) /* TODO Handle reconnection cases */ } +extern char **cfg_causes; +extern u_int cfg_ncauses; + static void tmate_client_exec_cmd(struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; char *cause; + u_int i; int client_id = unpack_int(uk); char *cmd_str = unpack_string(uk); @@ -130,21 +134,21 @@ static void tmate_client_exec_cmd(struct tmate_unpacker *uk) goto out; } - /* error messages land in cfg_causes */ - ARRAY_FREE(&cfg_causes); - cmd_q = cmdq_new(NULL); - cmdq_run(cmd_q, cmdlist); + cmdq_run(cmd_q, cmdlist, NULL); cmd_list_free(cmdlist); cmdq_free(cmd_q); - if (!ARRAY_EMPTY(&cfg_causes)) { - cause = ARRAY_ITEM(&cfg_causes, 0); - tmate_failed_cmd(client_id, cause); - free(cause); - ARRAY_FREE(&cfg_causes); + /* error messages land in cfg_causes */ + for (i = 0; i < cfg_ncauses; i++) { + tmate_failed_cmd(client_id, cfg_causes[i]); + free(cfg_causes[i]); } + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; + out: free(cmd_str); } @@ -163,7 +167,7 @@ static void tmate_client_env(struct tmate_unpacker *uk) extern void signal_waiting_clients(const char *name); static void tmate_client_ready(struct tmate_decoder *decoder, - struct tmate_unpacker *uk) + __unused struct tmate_unpacker *uk) { decoder->ready = 1; signal_waiting_clients("tmate-ready"); diff --git a/tmate-encoder.c b/tmate-encoder.c index 94dc2137..6f9bc0e4 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -1,4 +1,5 @@ #include "tmate.h" +#include "window-copy.h" #define DEFAULT_ENCODER (&tmate_session.encoder) @@ -114,6 +115,8 @@ void tmate_sync_layout(void) pack(int, active_window_idx); } +/* TODO add a buffer for pty_data ? */ + void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) { size_t max_write, to_write; @@ -133,6 +136,11 @@ void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) } } +extern const struct cmd_entry cmd_bind_key_entry; +extern const struct cmd_entry cmd_unbind_key_entry; +extern const struct cmd_entry cmd_set_option_entry; +extern const struct cmd_entry cmd_set_window_option_entry; + static const struct cmd_entry *replicated_cmds[] = { &cmd_bind_key_entry, &cmd_unbind_key_entry, diff --git a/tmate-msg.c b/tmate-msg.c index 001c7cef..6f19b798 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -9,45 +9,48 @@ static void tmate_status_message_client(struct client *c, const char *message) { struct timeval tv; struct session *s = c->session; - struct message_entry *msg; + struct message_entry *msg, *msg1; int delay; - u_int i, limit; + u_int first, limit; status_prompt_clear(c); status_message_clear(c); xasprintf(&c->message_string, "[tmate] %s", message); - ARRAY_EXPAND(&c->message_log, 1); - msg = &ARRAY_LAST(&c->message_log); + msg = xcalloc(1, sizeof *msg); msg->msg_time = time(NULL); + msg->msg_num = c->message_next++; msg->msg = xstrdup(c->message_string); + TAILQ_INSERT_TAIL(&c->message_log, msg, entry); if (s) { - limit = options_get_number(&s->options, "message-limit"); - delay = options_get_number(&s->options, "tmate-display-time"); + limit = options_get_number(s->options, "message-limit"); + delay = options_get_number(s->options, "tmate-display-time"); } else { /* Very early in the connection process we won't have a session */ - limit = options_get_number(&global_s_options, "message-limit"); - delay = options_get_number(&global_s_options, "tmate-display-time"); + limit = options_get_number(global_s_options, "message-limit"); + delay = options_get_number(global_s_options, "tmate-display-time"); } - if (ARRAY_LENGTH(&c->message_log) > limit) { - limit = ARRAY_LENGTH(&c->message_log) - limit; - for (i = 0; i < limit; i++) { - msg = &ARRAY_FIRST(&c->message_log); - free(msg->msg); - ARRAY_REMOVE(&c->message_log, 0); - } + first = c->message_next - limit; + TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { + if (msg->msg_num >= first) + continue; + free(msg->msg); + TAILQ_REMOVE(&c->message_log, msg, entry); + free(msg); } - tv.tv_sec = delay / 1000; - tv.tv_usec = (delay % 1000) * 1000L; + if (delay > 0) { + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; - if (event_initialized (&c->message_timer)) - evtimer_del(&c->message_timer); - evtimer_set(&c->message_timer, status_message_callback, c); - evtimer_add(&c->message_timer, &tv); + if (event_initialized(&c->message_timer)) + evtimer_del(&c->message_timer); + evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); + } c->flags |= CLIENT_STATUS | CLIENT_FORCE_STATUS; @@ -57,14 +60,12 @@ static void tmate_status_message_client(struct client *c, const char *message) void __tmate_status_message(const char *fmt, va_list ap) { struct client *c; - unsigned int i; char *message; xvasprintf(&message, fmt, ap); tmate_debug("%s", message); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (c && !(c->flags & CLIENT_READONLY)) tmate_status_message_client(c, message); } @@ -72,7 +73,7 @@ void __tmate_status_message(const char *fmt, va_list ap) free(message); } -void printflike1 tmate_status_message(const char *fmt, ...) +void printflike(1, 2) tmate_status_message(const char *fmt, ...) { va_list ap; diff --git a/tmate-session.c b/tmate-session.c index 40ac2d7a..802c9a9c 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -15,18 +15,16 @@ struct tmate_session tmate_session; -static struct evdns_base *ev_dnsbase; -static struct event ev_dns_retry; static void lookup_and_connect(void); -static void on_dns_retry(evutil_socket_t fd, short what, void *arg) +static void on_dns_retry(__unused evutil_socket_t fd, __unused short what, + __unused void *arg) { lookup_and_connect(); } static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) { - struct tmate_ssh_client *client; struct evutil_addrinfo *ai; struct timeval tv; const char *host = ptr; @@ -39,8 +37,9 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) tv.tv_sec = TMATE_DNS_RETRY_TIMEOUT; tv.tv_usec = 0; - evtimer_assign(&ev_dns_retry, ev_base, on_dns_retry, NULL); - evtimer_add(&ev_dns_retry, &tv); + evtimer_assign(&tmate_session.ev_dns_retry, tmate_session.ev_base, + on_dns_retry, NULL); + evtimer_add(&tmate_session.ev_dns_retry, &tv); return; } @@ -72,8 +71,8 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) /* * XXX For some reason, freeing the DNS resolver makes MacOSX flip out... * not sure what's going on... - * evdns_base_free(ev_dnsbase, 0); - * ev_dnsbase = NULL; + * evdns_base_free(tmate_session.ev_dnsbase, 0); + * tmate_session.ev_dnsbase = NULL; */ } @@ -82,9 +81,9 @@ static void lookup_and_connect(void) struct evutil_addrinfo hints; const char *tmate_server_host; - if (!ev_dnsbase) - ev_dnsbase = evdns_base_new(ev_base, 1); - if (!ev_dnsbase) + if (!tmate_session.ev_dnsbase) + tmate_session.ev_dnsbase = evdns_base_new(tmate_session.ev_base, 1); + if (!tmate_session.ev_dnsbase) tmate_fatal("Cannot initialize the DNS lookup service"); memset(&hints, 0, sizeof(hints)); @@ -93,21 +92,23 @@ static void lookup_and_connect(void) hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - tmate_server_host = options_get_string(&global_s_options, + tmate_server_host = options_get_string(global_s_options, "tmate-server-host"); tmate_info("Looking up %s...", tmate_server_host); - (void)evdns_getaddrinfo(ev_dnsbase, tmate_server_host, NULL, - &hints, dns_cb, tmate_server_host); + (void)evdns_getaddrinfo(tmate_session.ev_dnsbase, tmate_server_host, NULL, + &hints, dns_cb, (void *)tmate_server_host); } static void ssh_log_function(int priority, const char *function, - const char *buffer, void *userdata) + const char *buffer, __unused void *userdata) { tmate_debug("[%d] [%s] %s", priority, function, buffer); } -void tmate_session_init(void) +void tmate_session_init(struct event_base *base) { + tmate_session.ev_base = base; + ssh_set_log_callback(ssh_log_function); tmate_catch_sigsegv(); diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 8e8db6a1..1fd474a4 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -5,15 +5,16 @@ #include #include "tmate.h" +#include "window-copy.h" static void consume_channel(struct tmate_ssh_client *client); static void flush_input_stream(struct tmate_ssh_client *client); static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); static void __on_session_event(evutil_socket_t fd, short what, void *arg); -static void printflike2 kill_session(struct tmate_ssh_client *client, - const char *fmt, ...); -static void printflike2 reconnect_session(struct tmate_ssh_client *client, +static void printflike(2, 3) kill_session(struct tmate_ssh_client *client, const char *fmt, ...); +static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...); static void on_session_event(struct tmate_ssh_client *client); static void register_session_fd_event(struct tmate_ssh_client *client) @@ -23,7 +24,8 @@ static void register_session_fd_event(struct tmate_ssh_client *client) setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); - event_assign(&client->ev_ssh, ev_base, ssh_get_fd(client->session), + event_assign(&client->ev_ssh, client->tmate_session->ev_base, + ssh_get_fd(client->session), EV_READ | EV_PERSIST, __on_session_event, client); event_add(&client->ev_ssh, NULL); } @@ -34,7 +36,7 @@ static void register_input_stream_event(struct tmate_ssh_client *client) struct tmate_encoder *encoder = &client->tmate_session->encoder; if (!event_initialized(&encoder->ev_readable)) { - event_assign(&encoder->ev_readable, ev_base, -1, + event_assign(&encoder->ev_readable, client->tmate_session->ev_base, -1, EV_READ | EV_PERSIST, __flush_input_stream, client); event_add(&encoder->ev_readable, NULL); client->has_encoder = 1; @@ -81,7 +83,7 @@ static char *get_identity(void) { char *identity; - identity = options_get_string(&global_s_options, "tmate-identity"); + identity = options_get_string(global_s_options, "tmate-identity"); if (!strlen(identity)) return NULL; @@ -93,8 +95,8 @@ static char *get_identity(void) return identity; } -static int passphrase_callback(const char *prompt, char *buf, size_t len, - int echo, int verify, void *userdata) +static int passphrase_callback(__unused const char *prompt, char *buf, size_t len, + __unused int echo, __unused int verify, void *userdata) { struct tmate_ssh_client *client = userdata; @@ -138,7 +140,7 @@ static void request_passphrase(struct tmate_ssh_client *client) } window_pane_set_mode(wp, &window_copy_mode); - window_copy_init_from_pane(wp); + window_copy_init_from_pane(wp, 0); data = wp->modedata; data->inputtype = WINDOW_COPY_PASSWORD; @@ -146,7 +148,7 @@ static void request_passphrase(struct tmate_ssh_client *client) mode_key_init(&data->mdata, &mode_key_tree_vi_edit); - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); data->password_cb = on_passphrase_read; @@ -161,11 +163,11 @@ static void on_session_event(struct tmate_ssh_client *client) unsigned char *hash; ssize_t hash_len; char *hash_str; - char *server_hash_str; + const char *server_hash_str; int match; - int verbosity = SSH_LOG_NOLOG + debug_level; - int port = options_get_number(&global_s_options, "tmate-server-port"); + int verbosity = SSH_LOG_NOLOG + log_get_level(); + int port = options_get_number(global_s_options, "tmate-server-port"); ssh_session session = client->session; ssh_channel channel = client->channel; @@ -238,16 +240,12 @@ static void on_session_event(struct tmate_ssh_client *client) key_type = ssh_key_type(pubkey); switch (key_type) { - case SSH_KEYTYPE_DSS: - server_hash_str = options_get_string(&global_s_options, - "tmate-server-dsa-fingerprint"); - break; case SSH_KEYTYPE_RSA: - server_hash_str = options_get_string(&global_s_options, + server_hash_str = options_get_string(global_s_options, "tmate-server-rsa-fingerprint"); break; case SSH_KEYTYPE_ECDSA: - server_hash_str = options_get_string(&global_s_options, + server_hash_str = options_get_string(global_s_options, "tmate-server-ecdsa-fingerprint"); break; default: @@ -372,12 +370,12 @@ static void flush_input_stream(struct tmate_ssh_client *client) } } -static void __flush_input_stream(evutil_socket_t fd, short what, void *arg) +static void __flush_input_stream(__unused evutil_socket_t fd, __unused short what, void *arg) { flush_input_stream(arg); } -static void __on_session_event(evutil_socket_t fd, short what, void *arg) +static void __on_session_event(__unused evutil_socket_t fd, __unused short what, void *arg) { on_session_event(arg); } @@ -414,8 +412,8 @@ static void __kill_session(struct tmate_ssh_client *client, client->state = SSH_NONE; } -static void printflike2 kill_session(struct tmate_ssh_client *client, - const char *fmt, ...) +static void printflike(2, 3) kill_session(struct tmate_ssh_client *client, + const char *fmt, ...) { va_list ap; @@ -437,15 +435,15 @@ static void connect_session(struct tmate_ssh_client *client) } } -static void on_reconnect_timer(evutil_socket_t fd, short what, void *arg) +static void on_reconnect_timer(__unused evutil_socket_t fd, __unused short what, void *arg) { connect_session(arg); } -static void printflike2 reconnect_session(struct tmate_ssh_client *client, - const char *fmt, ...) +static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...) { - struct timeval tv; + /* struct timeval tv; */ va_list ap; #if 1 @@ -486,7 +484,7 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->ev_ssh.ev_flags = 0; - evtimer_assign(&client->ev_ssh_reconnect, ev_base, + evtimer_assign(&client->ev_ssh_reconnect, session->ev_base, on_reconnect_timer, client); connect_session(client); diff --git a/tmate.h b/tmate.h index 344015d1..77c603cb 100644 --- a/tmate.h +++ b/tmate.h @@ -9,10 +9,10 @@ #include "tmux.h" -#define tmate_debug(...) log_debug("[tmate] " __VA_ARGS__) -#define tmate_warn(...) log_warn("[tmate] " __VA_ARGS__) -#define tmate_info(...) log_info("[tmate] " __VA_ARGS__) -#define tmate_fatal(...) log_fatal("[tmate] " __VA_ARGS__) +#define tmate_debug(...) log_debug("[tmate] D " __VA_ARGS__) +#define tmate_warn(...) log_debug("[tmate] W " __VA_ARGS__) +#define tmate_info(...) log_debug("[tmate] I " __VA_ARGS__) +#define tmate_fatal(...) fatal("[tmate]" __VA_ARGS__) /* tmate-encoder.c */ @@ -124,6 +124,10 @@ extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *ses /* tmate-session.c */ struct tmate_session { + struct event_base *ev_base; + struct evdns_base *ev_dnsbase; + struct event ev_dns_retry; + struct tmate_encoder encoder; struct tmate_decoder decoder; @@ -138,7 +142,7 @@ struct tmate_session { }; extern struct tmate_session tmate_session; -extern void tmate_session_init(void); +extern void tmate_session_init(struct event_base *base); extern void tmate_session_start(void); /* tmate-debug.c */ @@ -148,7 +152,7 @@ extern void tmate_catch_sigsegv(void); /* tmate-msg.c */ extern void __tmate_status_message(const char *fmt, va_list ap); -extern void printflike1 tmate_status_message(const char *fmt, ...); +extern void printflike(1, 2) tmate_status_message(const char *fmt, ...); /* tmate-env.c */ diff --git a/tmux.1 b/tmux.1 index 05dfac61..f3c8066d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $Id$ +.\" $OpenBSD$ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 28lCquvV +.Op Fl 2CluvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -98,12 +98,10 @@ The options are as follows: Force .Nm to assume the terminal supports 256 colours. -.It Fl 8 -Like -.Fl 2 , -but indicates that the terminal supports 88 colours. .It Fl C -Start in control mode. +Start in control mode (see the +.Sx CONTROL MODE +section). Given twice .Xo ( Fl CC ) Xc disables echo. @@ -126,7 +124,7 @@ Specify an alternative configuration file. By default, .Nm loads the system configuration file from -.Pa /etc/tmux.conf , +.Pa @SYSCONFDIR@/tmux.conf , if present, then looks for a user configuration file at .Pa ~/.tmux.conf . .Pp @@ -145,11 +143,11 @@ session created, and continues to process the rest of the configuration file. .It Fl L Ar socket-name .Nm stores the server socket in a directory under +.Ev TMUX_TMPDIR +or .Pa /tmp -(or -.Ev TMPDIR -if set); -the default socket is named +if it is unset. +The default socket is named .Em default . This option allows a different socket name to be specified, allowing several independent @@ -164,15 +162,12 @@ If the socket is accidentally removed, the .Dv SIGUSR1 signal may be sent to the .Nm -server process to recreate it. +server process to recreate it (note that this will fail if any parent +directories are missing). .It Fl l Behave as a login shell. This flag currently has no effect and is for compatibility with other shells when using tmux as a login shell. -.It Fl q -Set the -.Ic quiet -server option to prevent the server sending various informational messages. .It Fl S Ar socket-path Specify a full alternative path to the server socket. If @@ -195,13 +190,11 @@ flag explicitly informs .Nm that UTF-8 is supported. .Pp -If the server is started from a client passed -.Fl u -or where UTF-8 is detected, the -.Ic utf8 -and -.Ic status-utf8 -options are enabled in the global window and session options respectively. +Note that +.Nm +itself always accepts UTF-8; this controls whether it will send UTF-8 +characters to the terminal it is running (if not, they are replaced by +.Ql _ ) . .It Fl v Request verbose logging. This option may be specified multiple times for increasing verbosity. @@ -256,6 +249,10 @@ Split the current pane into two, left and right. Kill the current window. .It ' Prompt for a window index to select. +.It \&( +Switch the attached client to the previous session. +.It \&) +Switch the attached client to the next session. .It , Rename the current window. .It - @@ -276,6 +273,8 @@ Choose which buffer to paste interactively from a list. List all key bindings. .It D Choose a client to detach. +.It L +Switch the attached client back to the last session. .It \&[ Enter copy mode to copy text or view the history. .It \&] @@ -300,16 +299,22 @@ Change to the previous window. Briefly display pane indexes. .It r Force redraw of the attached client. +.It m +Mark the current pane (see +.Ic select-pane +.Fl m ) . +.It M +Clear the marked pane. .It s Select a new session for the attached client interactively. -.It L -Switch the attached client back to the last session. .It t Show the time. .It w Choose the current window interactively. .It x Kill the current pane. +.It z +Toggle zoom state of the current pane. .It { Swap the current pane with the previous pane. .It } @@ -327,6 +332,8 @@ pane. .It M-1 to M-5 Arrange panes in one of the five preset layouts: even-horizontal, even-vertical, main-horizontal, main-vertical, or tiled. +.It Space +Arrange the current window in the next preset layout. .It M-n Move to the next window with a bell or activity marker. .It M-o @@ -351,6 +358,8 @@ This section contains a list of the commands supported by .Nm . Most commands accept the optional .Fl t +(and sometimes +.Fl s ) argument with one of .Ar target-client , .Ar target-session @@ -358,6 +367,7 @@ argument with one of or .Ar target-pane . These specify the client, session, window or pane which a command should affect. +.Pp .Ar target-client is the name of the .Xr pty 4 @@ -367,32 +377,53 @@ or .Pa ttyp1 for the client attached to .Pa /dev/ttyp1 . -If no client is specified, the current client is chosen, if possible, or an -error is reported. +If no client is specified, +.Nm +attempts to work out the client currently in use; if that fails, an error is +reported. Clients may be listed with the .Ic list-clients command. .Pp .Ar target-session -is the session id prefixed with a $, the name of a session (as listed by the +is tried as, in order: +.Bl -enum -offset Ds +.It +A session ID prefixed with a $. +.It +An exact name of a session (as listed by the .Ic list-sessions -command), or the name of a client with the same syntax as -.Ar target-client , -in which case the session attached to the client is used. -When looking for the session name, -.Nm -initially searches for an exact match; if none is found, the session names -are checked for any for which -.Ar target-session -is a prefix or for which it matches as an +command). +.It +The start of a session name, for example +.Ql mysess +would match a session named +.Ql mysession . +.It +An .Xr fnmatch 3 -pattern. -If a single match is found, it is used as the target session; multiple matches +pattern which is matched against the session name. +.El +.Pp +If the session name is prefixed with an +.Ql = , +only an exact match is accepted (so +.Ql =mysess +will only match exactly +.Ql mysess , +not +.Ql mysession ) . +.Pp +If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no current session is available, the most recently used is chosen. .Pp .Ar target-window +(or +.Ar src-window +or +.Ar dst-window ) specifies a window in the form .Em session Ns \&: Ns Em window . .Em session @@ -400,12 +431,32 @@ follows the same rules as for .Ar target-session , and .Em window -is looked for in order: as a window index, for example mysession:1; -as a window ID, such as @1; -as an exact window name, such as mysession:mywindow; then as an +is looked for in order as: +.Bl -enum -offset Ds +.It +A special token, listed below. +.It +A window index, for example +.Ql mysession:1 +is window 1 in session +.Ql mysession . +.It +A window ID, such as @1. +.It +An exact window name, such as +.Ql mysession:mywindow . +.It +The start of a window name, such as +.Ql mysession:mywin . +.It +As an .Xr fnmatch 3 -pattern or the start of a window name, such as mysession:mywin* or -mysession:mywin. +pattern matched against the window name. +.El +.Pp +Like sessions, a +.Ql = +prefix will do an exact match only. An empty window name specifies the next unused index if appropriate (for example the .Ic new-window @@ -415,52 +466,51 @@ commands) otherwise the current window in .Em session is chosen. -The special character -.Ql \&! -uses the last (previously current) window, -.Ql ^ -selects the highest numbered window, -.Ql $ -selects the lowest numbered window, and -.Ql + -and -.Ql - -select the next window or the previous window by number. -When the argument does not contain a colon, -.Nm -first attempts to parse it as window; if that fails, an attempt is made to -match a session. +.Pp +The following special tokens are available to indicate particular windows. +Each has a single-character alternative form. +.Bl -column "XXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{start}" Ta "^" Ta "The lowest-numbered window" +.It Li "{end}" Ta "$" Ta "The highest-numbered window" +.It Li "{last}" Ta "!" Ta "The last (previously current) window" +.It Li "{next}" Ta "+" Ta "The next window by number" +.It Li "{previous}" Ta "-" Ta "The previous window by number" +.El .Pp .Ar target-pane -takes a similar form to +(or +.Ar src-pane +or +.Ar dst-pane ) +may be a pane ID or takes a similar form to .Ar target-window -but with the optional addition of a period followed by a pane index, for -example: mysession:mywindow.1. +but with the optional addition of a period followed by a pane index or pane ID, +for example: +.Ql mysession:mywindow.1 . If the pane index is omitted, the currently active pane in the specified window is used. -If neither a colon nor period appears, -.Nm -first attempts to use the argument as a pane index; if that fails, it is looked -up as for -.Ar target-window . -A -.Ql + -or -.Ql - -indicate the next or previous pane index, respectively. -One of the strings -.Em top , -.Em bottom , -.Em left , -.Em right , -.Em top-left , -.Em top-right , -.Em bottom-left -or -.Em bottom-right -may be used instead of a pane index. +The following special tokens are available for the pane index: +.Bl -column "XXXXXXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{last}" Ta "!" Ta "The last (previously active) pane" +.It Li "{next}" Ta "+" Ta "The next pane by number" +.It Li "{previous}" Ta "-" Ta "The previous pane by number" +.It Li "{top}" Ta "" Ta "The top pane" +.It Li "{bottom}" Ta "" Ta "The bottom pane" +.It Li "{left}" Ta "" Ta "The leftmost pane" +.It Li "{right}" Ta "" Ta "The rightmost pane" +.It Li "{top-left}" Ta "" Ta "The top-left pane" +.It Li "{top-right}" Ta "" Ta "The top-right pane" +.It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" +.It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" +.It Li "{up-of}" Ta "" Ta "The pane above the active pane" +.It Li "{down-of}" Ta "" Ta "The pane below the active pane" +.It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane" +.It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane" +.El .Pp -The special characters +The tokens .Ql + and .Ql - @@ -469,30 +519,91 @@ may be followed by an offset, for example: select-window -t:+2 .Ed .Pp -When dealing with a session that doesn't contain sequential window indexes, -they will be correctly skipped. +In addition, +.Em target-session , +.Em target-window +or +.Em target-pane +may consist entirely of the token +.Ql {mouse} +(alternative form +.Ql = ) +to specify the most recent mouse event +(see the +.Sx MOUSE SUPPORT +section) +or +.Ql {marked} +(alternative form +.Ql ~ ) +to specify the marked pane (see +.Ic select-pane +.Fl m ) . .Pp +Sessions, window and panes are each numbered with a unique ID; session IDs are +prefixed with a +.Ql $ , +windows with a +.Ql @ , +and panes with a +.Ql % . +These are unique and are unchanged for the life of the session, window or pane +in the .Nm -also gives each pane created in a server an identifier consisting of a -.Ql % -and a number, starting from zero. -A pane's identifier is unique for the life of the -.Nm -server and is passed to the child process of the pane in the +server. +The pane ID is passed to the child process of the pane in the .Ev TMUX_PANE environment variable. -It may be used alone to target a pane or the window containing it. +IDs may be displayed using the +.Ql session_id , +.Ql window_id , +or +.Ql pane_id +formats (see the +.Sx FORMATS +section) and the +.Ic display-message , +.Ic list-sessions , +.Ic list-windows +or +.Ic list-panes +commands. .Pp .Ar shell-command arguments are .Xr sh 1 commands. -These must be passed as a single item, which typically means quoting them, for -example: +This may be a single argument passed to the shell, for example: .Bd -literal -offset indent new-window 'vi /etc/passwd' .Ed .Pp +Will run: +.Bd -literal -offset indent +/bin/sh -c 'vi /etc/passwd' +.Ed +.Pp +Additionally, the +.Ic new-window , +.Ic new-session , +.Ic split-window , +.Ic respawn-window +and +.Ic respawn-pane +commands allow +.Ar shell-command +to be given as multiple arguments and executed directly (without +.Ql sh -c ) . +This can avoid issues with shell quoting. +For example: +.Bd -literal -offset indent +$ tmux new-window vi /etc/passwd +.Ed +.Pp +Will run +.Xr vi 1 +directly without invoking the shell. +.Pp .Ar command .Op Ar arguments refers to a @@ -568,7 +679,8 @@ section. The following commands are available to manage clients and sessions: .Bl -tag -width Ds .It Xo Ic attach-session -.Op Fl dr +.Op Fl dEr +.Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) @@ -602,9 +714,18 @@ needs to select the most recently used session, it will prefer the most recently used .Em unattached session. +.Pp +.Fl c +will set the session working directory (used for new windows) to +.Ar working-directory . +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. .It Xo Ic detach-client -.Op Fl P -.Op Fl a +.Op Fl aP .Op Fl s Ar target-session .Op Fl t Ar target-client .Xc @@ -629,14 +750,19 @@ If it does exist, exit with 0. Kill the .Nm server and clients and destroy all sessions. -.It Ic kill-session -.Op Fl a +.It Xo Ic kill-session +.Op Fl aC .Op Fl t Ar target-session +.Xc Destroy the given session, closing any windows linked to it and no other sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. +The +.Fl C +flag clears alerts (bell, activity, or silence) in all windows linked to the +session. .It Xo Ic list-clients .Op Fl F Ar format .Op Fl t Ar target-session @@ -675,7 +801,8 @@ command. Lock all clients attached to .Ar target-session . .It Xo Ic new-session -.Op Fl AdDP +.Op Fl AdDEP +.Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name @@ -715,7 +842,7 @@ behave like .Ic attach-session if .Ar session-name -already exists; in the case, +already exists; in this case, .Fl D behaves like .Fl d @@ -749,6 +876,12 @@ By default, it uses the format .Ql #{session_name}: but a different format may be specified with .Fl F . +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client @@ -768,15 +901,23 @@ is specified, only update the client's status bar. Rename the session to .Ar new-name . .It Xo Ic show-messages +.Op Fl JT .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) +Show client messages or server information. Any messages displayed on the status line are saved in a per-client message log, up to a maximum of the limit set by the .Ar message-limit -session option for the session attached to that client. -This command displays the log for +server option. +With +.Fl t , +display the log for .Ar target-client . +.Fl J +and +.Fl T +show debugging information about jobs and terminals. .It Ic source-file Ar path .D1 (alias: Ic source ) Execute commands from @@ -794,9 +935,10 @@ Suspend a client by sending .Dv SIGTSTP (tty stop). .It Xo Ic switch-client -.Op Fl lnpr +.Op Fl Elnpr .Op Fl c Ar target-client .Op Fl t Ar target-session +.Op Fl T Ar key-table .Xc .D1 (alias: Ic switchc ) Switch the current session for client @@ -814,6 +956,28 @@ respectively. toggles whether a client is read-only (see the .Ic attach-session command). +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. +.Pp +.Fl T +sets the client's key table; the next key from the client will be interpreted from +.Ar key-table . +This may be used to configure multiple prefix keys, or to bind commands to +sequences of keys. +For example, to make typing +.Ql abc +run the +.Ic list-keys +command: +.Bd -literal -offset indent +bind-key -Ttable2 c list-keys +bind-key -Ttable1 b switch-client -Ttable2 +bind-key -Troot a switch-client -Ttable1 +.Ed .El .Sh WINDOWS AND PANES A @@ -840,10 +1004,12 @@ option). The following keys are supported as appropriate for the mode: .Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" +.It Li "Append selection" Ta "A" Ta "" .It Li "Back to indentation" Ta "^" Ta "M-m" .It Li "Bottom of history" Ta "G" Ta "M-<" .It Li "Clear selection" Ta "Escape" Ta "C-g" .It Li "Copy selection" Ta "Enter" Ta "M-w" +.It Li "Copy to named buffer" Ta \&" Ta "" .It Li "Cursor down" Ta "j" Ta "Down" .It Li "Cursor left" Ta "h" Ta "Left" .It Li "Cursor right" Ta "l" Ta "Right" @@ -857,21 +1023,22 @@ The following keys are supported as appropriate for the mode: .It Li "Go to line" Ta ":" Ta "g" .It Li "Half page down" Ta "C-d" Ta "M-Down" .It Li "Half page up" Ta "C-u" Ta "M-Up" -.It Li "Jump forward" Ta "f" Ta "f" -.It Li "Jump to forward" Ta "t" Ta "" -.It Li "Jump backward" Ta "F" Ta "F" -.It Li "Jump to backward" Ta "T" Ta "" .It Li "Jump again" Ta ";" Ta ";" .It Li "Jump again in reverse" Ta "," Ta "," +.It Li "Jump backward" Ta "F" Ta "F" +.It Li "Jump forward" Ta "f" Ta "f" +.It Li "Jump to backward" Ta "T" Ta "" +.It Li "Jump to forward" Ta "t" Ta "" .It Li "Next page" Ta "C-f" Ta "Page down" .It Li "Next space" Ta "W" Ta "" .It Li "Next space, end of word" Ta "E" Ta "" .It Li "Next word" Ta "w" Ta "" .It Li "Next word end" Ta "e" Ta "M-f" +.It Li "Other end of selection" Ta "o" Ta "" .It Li "Paste buffer" Ta "p" Ta "C-y" .It Li "Previous page" Ta "C-b" Ta "Page up" -.It Li "Previous word" Ta "b" Ta "M-b" .It Li "Previous space" Ta "B" Ta "" +.It Li "Previous word" Ta "b" Ta "M-b" .It Li "Quit mode" Ta "q" Ta "Escape" .It Li "Rectangle toggle" Ta "v" Ta "R" .It Li "Scroll down" Ta "C-Down or C-e" Ta "C-Down" @@ -880,6 +1047,7 @@ The following keys are supported as appropriate for the mode: .It Li "Search again in reverse" Ta "N" Ta "N" .It Li "Search backward" Ta "?" Ta "C-r" .It Li "Search forward" Ta "/" Ta "C-s" +.It Li "Select line" Ta "V" Ta "" .It Li "Start of line" Ta "0" Ta "C-a" .It Li "Start selection" Ta "Space" Ta "C-Space" .It Li "Top of history" Ta "g" Ta "M->" @@ -921,9 +1089,6 @@ in emacs mode, and .Ql 10w in vi. .Pp -When copying the selection, the repeat count indicates the buffer index to -replace, if used. -.Pp Mode key bindings are defined in a set of named tables: .Em vi-edit and @@ -945,15 +1110,27 @@ command and keys modified or removed with .Ic bind-key and .Ic unbind-key . -One command accepts an argument, -.Ic copy-pipe , -which copies the selection and pipes it to a command. +If +.Ic append-selection , +.Ic copy-selection , +or +.Ic start-named-buffer +are given the +.Fl x +flag, +.Nm +will not exit copy mode after copying. +.Ic copy-pipe +copies the selection and pipes it to a command. For example the following will bind +.Ql C-w +not to exit after copying and .Ql C-q to copy the selection into .Pa /tmp as well as the paste buffer: .Bd -literal -offset indent +bind-key -temacs-copy C-w copy-selection -x bind-key -temacs-copy C-q copy-pipe "cat >/tmp/out" .Ed .Pp @@ -965,13 +1142,26 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl u +.Op Fl Meu .Op Fl t Ar target-pane .Xc Enter copy mode. The .Fl u option scrolls one page up. +.Fl M +begins a mouse drag (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . +.Fl e +specifies that scrolling to the bottom of the history (to the visible screen) +should exit copy mode. +While in copy mode, pressing a key other than those used for scrolling will +disable this behaviour. +This is intended to allow fast scrolling through a pane's history, for +example with: +.Bd -literal -offset indent +bind PageUp copy-mode -eu +.Ed .El .Pp Each window displayed by @@ -1063,12 +1253,14 @@ Commands related to windows and panes are as follows: .It Xo Ic break-pane .Op Fl dP .Op Fl F Ar format -.Op Fl t Ar target-pane +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane .Xc .D1 (alias: Ic breakp ) Break -.Ar target-pane -off from its containing window to make it the only pane in a new window. +.Ar src-pane +off from its containing window to make it the only pane in +.Ar dst-window . If .Fl d is given, the new window does not become the current window. @@ -1081,7 +1273,7 @@ but a different format may be specified with .Fl F . .It Xo Ic capture-pane .Op Fl aepPq -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line .Op Fl t Ar target-pane @@ -1116,6 +1308,12 @@ and .Fl E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. +.Ql - +to +.Fl S +is the start of the history and to +.Fl E +the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client @@ -1142,32 +1340,6 @@ flag, see the section. This command works only if at least one client is attached. .It Xo -.Ic choose-list -.Op Fl l Ar items -.Op Fl t Ar target-window -.Op Ar template -.Xc -Put a window into list choice mode, allowing -.Ar items -to be selected. -.Ar items -can be a comma-separated list to display more than one item. -If an item has spaces, that entry must be quoted. -After an item is chosen, -.Ql %% -is replaced by the chosen item in the -.Ar template -and the result is executed as a command. -If -.Ar template -is not given, "run-shell '%%'" is used. -.Ar items -also accepts format specifiers. -For the meaning of this see the -.Sx FORMATS -section. -This command works only if at least one client is attached. -.It Xo .Ic choose-session .Op Fl F Ar format .Op Fl t Ar target-window @@ -1280,7 +1452,7 @@ flag, see the section. This command works only if at least one client is attached. .It Ic display-panes Op Fl t Ar target-client -.D1 (alias: Ic displayp) +.D1 (alias: Ic displayp ) Display a visible indicator of each pane shown by .Ar target-client . See the @@ -1347,6 +1519,13 @@ option causes .Ar src-pane to be joined to left of or above .Ar dst-pane . +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic kill-pane .Op Fl a .Op Fl t Ar target-pane @@ -1370,9 +1549,16 @@ The .Fl a option kills all but the window given with .Fl t . -.It Ic last-pane Op Fl t Ar target-window +.It Xo Ic last-pane +.Op Fl de +.Op Fl t Ar target-window +.Xc .D1 (alias: Ic lastp ) Select the last (previously selected) pane. +.Fl e +enables or +.Fl d +disables input to the pane. .It Ic last-window Op Fl t Ar target-session .D1 (alias: Ic last ) Select the last (previously selected) window. @@ -1380,7 +1566,7 @@ If no .Ar target-session is specified, select the last window of the current session. .It Xo Ic link-window -.Op Fl dk +.Op Fl adk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -1394,6 +1580,10 @@ If is specified and no such window exists, the .Ar src-window is linked there. +With +.Fl a , +the window is moved to the next index up (following windows +are moved if necessary). If .Fl k is given and @@ -1459,7 +1649,7 @@ and .Ar dst-pane may belong to the same window. .It Xo Ic move-window -.Op Fl rdk +.Op Fl ardk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -1512,13 +1702,6 @@ is not specified, the value of the option is used. .Fl c specifies the working directory in which the new window is created. -It may have an absolute path or one of the following values (or a subdirectory): -.Bl -column "XXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXX" -offset indent -.It Li "Empty string" Ta "Current pane's directory" -.It Li "~" Ta "User's home directory" -.It Li "-" Ta "Where session was started" -.It Li "." Ta "Where server was started" -.El .Pp When the shell command completes, the window closes. See the @@ -1609,7 +1792,7 @@ Rename the current window, or the window at if specified, to .Ar new-name . .It Xo Ic resize-pane -.Op Fl DLRUZ +.Op Fl DLMRUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height @@ -1638,6 +1821,10 @@ With .Fl Z , the active pane is toggled between zoomed (occupying the whole of the window) and unzoomed (its normal position in the layout). +.Pp +.Fl M +begins mouse resizing (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . .It Xo Ic respawn-pane .Op Fl k .Op Fl t Ar target-pane @@ -1678,7 +1865,7 @@ lower) with .Fl U or downward (numerically higher). .It Xo Ic select-layout -.Op Fl np +.Op Fl nop .Op Fl t Ar target-window .Op Ar layout-name .Xc @@ -1695,15 +1882,20 @@ are equivalent to the and .Ic previous-layout commands. +.Fl o +applies the last set layout if possible (undoes the most recent layout change). .It Xo Ic select-pane -.Op Fl lDLRU +.Op Fl DdegLlMmRU +.Op Fl P Ar style .Op Fl t Ar target-pane .Xc .D1 (alias: Ic selectp ) Make pane .Ar target-pane the active pane in window -.Ar target-window . +.Ar target-window , +or set its style (with +.Fl P ) . If one of .Fl D , .Fl L , @@ -1716,6 +1908,40 @@ target pane is used. is the same as using the .Ic last-pane command. +.Fl e +enables or +.Fl d +disables input to the pane. +.Pp +.Fl m +and +.Fl M +are used to set and clear the +.Em marked pane . +There is one marked pane at a time, setting a new marked pane clears the last. +The marked pane is the default target for +.Fl s +to +.Ic join-pane , +.Ic swap-pane +and +.Ic swap-window . +.Pp +Each pane has a style: by default the +.Ic window-style +and +.Ic window-active-style +options are used, +.Ic select-pane +.Fl P +sets the style for a single pane. +For example, to set the pane 1 background to red: +.Bd -literal -offset indent +select-pane -t:.1 -P 'bg=red' +.Ed +.Pp +.Fl g +shows the current pane style. .It Xo Ic select-window .Op Fl lnpT .Op Fl t Ar target-window @@ -1739,7 +1965,7 @@ is given and the selected window is already the current window, the command behaves like .Ic last-window . .It Xo Ic split-window -.Op Fl dhvP +.Op Fl bdhvP .Op Fl c Ar start-directory .Oo Fl l .Ar size | @@ -1763,6 +1989,10 @@ and .Fl p options specify the size of the new pane in lines (for vertical split) or in cells (for horizontal split), or as a percentage, respectively. +The +.Fl b +option causes the new pane to be created to the left of or above +.Ar target-pane . All other options have the same meaning as for the .Ic new-window command. @@ -1785,6 +2015,13 @@ swaps with the next pane (after it numerically). instructs .Nm not to change the active pane. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic swap-window .Op Fl d .Op Fl s Ar src-window @@ -1796,6 +2033,15 @@ This is similar to except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . +.Pp +Like +.Ic swap-pane , +if +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the window containing the marked pane is used rather than the current window. .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window @@ -1839,7 +2085,7 @@ In addition, the following special key names are accepted: .Em Escape , .Em F1 to -.Em F20 , +.Em F12 , .Em Home , .Em IC (Insert), @@ -1862,7 +2108,8 @@ Commands related to key bindings are as follows: .Bl -tag -width Ds .It Xo Ic bind-key .Op Fl cnr -.Op Fl t Ar key-table +.Op Fl t Ar mode-table +.Op Fl T Ar key-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) @@ -1870,16 +2117,40 @@ Bind key .Ar key to .Ar command . -By default (without -.Fl t ) -the primary key bindings are modified (those normally activated with the prefix -key); in this case, if -.Fl n -is specified, it is not necessary to use the prefix key, -.Ar command +Keys are bound in a key table. +By default (without -T), the key is bound in +the +.Em prefix +key table. +This table is used for keys pressed after the prefix key (for example, +by default +.Ql c is bound to -.Ar key -alone. +.Ic new-window +in the +.Em prefix +table, so +.Ql C-b c +creates a new window). +The +.Em root +table is used for keys pressed without the prefix key: binding +.Ql c +to +.Ic new-window +in the +.Em root +table (not recommended) means a plain +.Ql c +will create a new window. +.Fl n +is an alias +for +.Fl T Ar root . +Keys may also be bound in custom key tables and the +.Ic switch-client +.Fl T +command used to switch to them from a key binding. The .Fl r flag indicates this key may repeat, see the @@ -1891,25 +2162,37 @@ If is present, .Ar key is bound in -.Ar key-table : +.Ar mode-table : the binding for command mode with .Fl c or for normal mode without. +See the +.Sx WINDOWS AND PANES +section and the +.Ic list-keys +command for information on mode key bindings. +.Pp To view the default bindings and possible commands, see the .Ic list-keys command. -.It Ic list-keys Op Fl t Ar key-table +.It Xo Ic list-keys +.Op Fl t Ar mode-table +.Op Fl T Ar key-table +.Xc .D1 (alias: Ic lsk ) List all key bindings. Without -.Fl t -the primary key bindings - those executed when preceded by the prefix key - -are printed. +.Fl T +all key tables are printed. +With +.Fl T +only +.Ar key-table . .Pp With .Fl t , the key bindings in -.Ar key-table +.Ar mode-table are listed; this may be one of: .Em vi-edit , .Em emacs-edit , @@ -1919,7 +2202,7 @@ are listed; this may be one of: or .Em emacs-copy . .It Xo Ic send-keys -.Op Fl lR +.Op Fl lMR .Op Fl t Ar target-pane .Ar key Ar ... .Xc @@ -1940,6 +2223,10 @@ All arguments are sent sequentially from first to last. The .Fl R flag causes the terminal state to be reset. +.Pp +.Fl M +passes through a mouse event (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT ) . .It Xo Ic send-prefix .Op Fl 2 .Op Fl t Ar target-pane @@ -1949,32 +2236,23 @@ Send the prefix key, or with the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key .Op Fl acn -.Op Fl t Ar key-table +.Op Fl t Ar mode-table +.Op Fl T Ar key-table .Ar key .Xc .D1 (alias: Ic unbind ) Unbind the command bound to .Ar key . -Without +.Fl c , +.Fl n , +.Fl T +and .Fl t -the primary key bindings are modified; in this case, if -.Fl n -is specified, the command bound to -.Ar key -without a prefix (if any) is removed. +are the same as for +.Ic bind-key . If .Fl a is present, all key bindings are removed. -.Pp -If -.Fl t -is present, -.Ar key -in -.Ar key-table -is unbound: the binding for command mode with -.Fl c -or for normal mode without. .El .Sh OPTIONS The appearance and behaviour of @@ -2028,7 +2306,7 @@ also supports user options which are prefixed with a User options may have any name, so long as they are prefixed with .Ql \&@ , and be set to any string. -For example +For example: .Bd -literal -offset indent $ tmux setw -q @foo "abc123" $ tmux showw -v @foo @@ -2051,30 +2329,47 @@ command), a server option with .Fl s , otherwise a session option. -.Pp If .Fl g -is specified, the global session or window option is set. -With -.Fl a , -and if the option expects a string, -.Ar value -is appended to the existing setting. +is given, the global session or window option is set. The .Fl u flag unsets an option, so a session inherits the option from the global -options. -It is not possible to unset a global option. +options (or with +.Fl g , +restores a global option to the default). .Pp The .Fl o -flag prevents setting an option that is already set. -.Pp -The +flag prevents setting an option that is already set and the .Fl q -flag suppresses the informational message (as if the -.Ic quiet -server option was set). +flag suppresses errors about unknown or ambiguous options. +.Pp +With +.Fl a , +and if the option expects a string or a style, +.Ar value +is appended to the existing setting. +For example: +.Bd -literal -offset indent +set -g status-left "foo" +set -ag status-left "bar" +.Ed +.Pp +Will result in +.Ql foobar . +And: +.Bd -literal -offset indent +set -g status-style "bg=red" +set -ag status-style "fg=blue" +.Ed +.Pp +Will result in a red background +.Em and +blue foreground. +Without +.Fl a , +the result would be the default background and a blue foreground. .Pp Available window options are listed under .Ic set-window-option . @@ -2089,6 +2384,19 @@ Available server options are: Set the number of buffers; as new buffers are added to the top of the stack, old ones are removed from the bottom if necessary to maintain this maximum length. +.It Ic default-terminal Ar terminal +Set the default terminal for new windows created in this session - the +default value of the +.Ev TERM +environment variable. +For +.Nm +to work correctly, this +.Em must +be set to +.Ql screen , +.Ql tmux +or a derivative of them. .It Ic escape-time Ar time Set the time in milliseconds for which .Nm @@ -2099,12 +2407,22 @@ The default is 500 milliseconds. .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. -.It Xo Ic quiet +.It Xo Ic focus-events .Op Ic on | off .Xc -Enable or disable the display of various informational messages (see also the -.Fl q -command line flag). +When enabled, focus events are requested from the terminal if supported and +passed through to applications running in +.Nm . +Attached clients should be detached and attached again after changing this +option. +.It Ic history-file Ar path +If not empty, a file to which +.Nm +will write command prompt history on exit and load it from on start. +.It Ic message-limit Ar number +Set the number of error or information messages to save in the message log for +each client. +The default is 100. .It Xo Ic set-clipboard .Op Ic on | off .Xc @@ -2127,6 +2445,42 @@ disallowedWindowOps: 20,21,SetXprop Or changing this property from the .Xr xterm 1 interactive menu when required. +.It Ic terminal-overrides Ar string +Contains a list of entries which override terminal descriptions read using +.Xr terminfo 5 . +.Ar string +is a comma-separated list of items each a colon-separated string made up of a +terminal type pattern (matched using +.Xr fnmatch 3 ) +and a set of +.Em name=value +entries. +.Pp +For example, to set the +.Ql clear +.Xr terminfo 5 +entry to +.Ql \ee[H\ee[2J +for all terminal types and the +.Ql dch1 +entry to +.Ql \ee[P +for the +.Ql rxvt +terminal type, the option could be set to the string: +.Bd -literal -offset indent +"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" +.Ed +.Pp +The terminal entry value is passed through +.Xr strunvis 3 +before interpretation. +The default value forcibly corrects the +.Ql colors +entry for terminals which support 256 colours: +.Bd -literal -offset indent +"*256col*:colors=256,xterm*:XT" +.Ed .El .Pp Available session options are: @@ -2143,16 +2497,18 @@ Set the base index from which an unused index should be searched when a new window is created. The default is zero. .It Xo Ic bell-action -.Op Ic any | none | current +.Op Ic any | none | current | other .Xc Set action on window bell. .Ic any means a bell in any window linked to a session causes a bell in the current window of that session, .Ic none -means all bells are ignored and +means all bells are ignored, .Ic current -means only bells in windows other than the current window are ignored. +means only bells in windows other than the current window are ignored and +.Ic other +means bells in the current window are ignored but not those in other windows. .It Xo Ic bell-on-alert .Op Ic on | off .Xc @@ -2170,15 +2526,6 @@ The default is an empty string, which instructs to create a login shell using the value of the .Ic default-shell option. -.It Ic default-path Ar path -Set the default working directory for new panes. -If empty (the default), the working directory is determined from the process -running in the active pane, from the command line environment or from the -working directory where the session was created. -Otherwise the same options are available as for the -.Fl c -flag to -.Ic new-window . .It Ic default-shell Ar path Specify the default shell. This is used as the login shell for new windows when the @@ -2195,18 +2542,6 @@ or This option should be configured when .Nm is used as a login shell. -.It Ic default-terminal Ar terminal -Set the default terminal for new windows created in this session - the -default value of the -.Ev TERM -environment variable. -For -.Nm -to work correctly, this -.Em must -be set to -.Ql screen -or a derivative of it. .It Xo Ic destroy-unattached .Op Ic on | off .Xc @@ -2234,20 +2569,24 @@ command appear. .It Ic display-time Ar time Set the amount of time for which status line messages and other on-screen indicators are displayed. +If set to 0, messages and indicators are displayed until a key is pressed. .Ar time is in milliseconds. .It Ic history-limit Ar lines Set the maximum number of lines held in window history. This setting applies only to new windows - existing window histories are not resized and retain the limit at the point they were created. +.It Ic key-table Ar key-table +Set the default key table to +.Ar key-table +instead of +.Em root . .It Ic lock-after-time Ar number Lock the session (like the .Ic lock-session command) after .Ar number -seconds of inactivity, or the entire server (all sessions) if the -.Ic lock-server -option is set. +seconds of inactivity. The default is not to lock (set to 0). .It Ic lock-command Ar shell-command Command to run when locking each client. @@ -2255,39 +2594,18 @@ The default is to run .Xr lock 1 with .Fl np . -.It Xo Ic lock-server -.Op Ic on | off -.Xc -If this option is -.Ic on -(the default), -instead of each session locking individually as each has been -idle for -.Ic lock-after-time , -the entire server will lock after -.Em all -sessions would have locked. -This has no effect as a session option; it must be set as a global option. -.It Ic message-attr Ar attributes -Set status line message attributes, where -.Ar attributes -is either -.Ic none -or a comma-delimited list of one or more of: -.Ic bright -(or -.Ic bold ) , -.Ic dim , -.Ic underscore , -.Ic blink , -.Ic reverse , -.Ic hidden , -or -.Ic italics . -.It Ic message-bg Ar colour -Set status line message background colour, where -.Ar colour -is one of: +.It Ic message-command-style Ar style +Set status line message command style, where +.Ar style +is a comma-separated list of characteristics to be specified. +.Pp +These may be +.Ql bg=colour +to set the background colour, +.Ql fg=colour +to set the foreground colour, and a list of attributes as specified below. +.Pp +The colour is one of: .Ic black , .Ic red , .Ic green , @@ -2308,51 +2626,66 @@ from the 256-colour set, or a hexadecimal RGB string such as .Ql #ffffff , which chooses the closest match from the default 256-colour set. -.It Ic message-command-attr Ar attributes -Set status line message attributes when in command mode. -.It Ic message-command-bg Ar colour -Set status line message background colour when in command mode. -.It Ic message-command-fg Ar colour -Set status line message foreground colour when in command mode. -.It Ic message-fg Ar colour -Set status line message foreground colour. -.It Ic message-limit Ar number -Set the number of error or information messages to save in the message log for -each client. -The default is 20. -.It Xo Ic mouse-resize-pane +.Pp +The attributes is either +.Ic none +or a comma-delimited list of one or more of: +.Ic bright +(or +.Ic bold ) , +.Ic dim , +.Ic underscore , +.Ic blink , +.Ic reverse , +.Ic hidden , +or +.Ic italics , +to turn an attribute on, or an attribute prefixed with +.Ql no +to turn one off. +.Pp +Examples are: +.Bd -literal -offset indent +fg=yellow,bold,underscore,blink +bg=black,fg=default,noreverse +.Ed +.Pp +With the +.Fl a +flag to the +.Ic set-option +command the new style is added otherwise the existing style is replaced. +.It Ic message-style Ar style +Set status line message style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.It Xo Ic mouse .Op Ic on | off .Xc If on, .Nm -captures the mouse and allows panes to be resized by dragging on their borders. -.It Xo Ic mouse-select-pane -.Op Ic on | off -.Xc -If on, -.Nm -captures the mouse and when a window is split into multiple panes the mouse may -be used to select the current pane. -The mouse click is also passed through to the application as normal. -.It Xo Ic mouse-select-window -.Op Ic on | off -.Xc -If on, clicking the mouse on a window name in the status line will select that -window. -.It Xo Ic mouse-utf8 -.Op Ic on | off -.Xc -If enabled, request mouse input as UTF-8 on UTF-8 terminals. -.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. -.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. +captures the mouse and allows mouse events to be bound as key bindings. +See the +.Sx MOUSE SUPPORT +section for details. .It Ic prefix Ar key Set the key accepted as a prefix key. +In addition to the standard keys described under +.Sx KEY BINDINGS , +.Ic prefix +can be set to the special key +.Ql None +to set no prefix. .It Ic prefix2 Ar key Set a secondary key accepted as a prefix key. +Like +.Ic prefix , +.Ic prefix2 +can be set to +.Ql None . .It Xo Ic renumber-windows .Op Ic on | off .Xc @@ -2397,29 +2730,21 @@ and .Xr terminfo 5 entries if they exist. .Nm -automatically sets these to the \ee]2;...\e007 sequence if -the terminal appears to be an xterm. +automatically sets these to the \ee]0;...\e007 sequence if +the terminal appears to be +.Xr xterm 1 . This option is off by default. -Note that elinks -will only attempt to set the window title if the STY environment -variable is set. .It Ic set-titles-string Ar string String used to set the window title if .Ic set-titles is on. -Character sequences are replaced as for the -.Ic status-left -option. +Formats are expanded, see the +.Sx FORMATS +section. .It Xo Ic status .Op Ic on | off .Xc Show or hide the status line. -.It Ic status-attr Ar attributes -Set status line attributes. -.It Ic status-bg Ar colour -Set status line background colour. -.It Ic status-fg Ar colour -Set status line foreground colour. .It Ic status-interval Ar interval Update the status bar every .Ar interval @@ -2445,86 +2770,47 @@ environment variables are set and contain the string .It Ic status-left Ar string Display .Ar string -to the left of the status bar. +(by default the session name) to the left of the status bar. .Ar string will be passed through .Xr strftime 3 -before being used. -By default, the session name is shown. -.Ar string -may contain any of the following special character sequences: +and formats (see +.Sx FORMATS ) +will be expanded. +It may also contain any of the following special character sequences: .Bl -column "Character pair" "Replaced with" -offset indent .It Sy "Character pair" Ta Sy "Replaced with" -.It Li "#(shell-command)" Ta "First line of the command's output" .It Li "#[attributes]" Ta "Colour or attribute change" -.It Li "#H" Ta "Hostname of local host" -.It Li "#h" Ta "Hostname of local host without the domain name" -.It Li "#F" Ta "Current window flag" -.It Li "#I" Ta "Current window index" -.It Li "#D" Ta "Current pane unique identifier" -.It Li "#P" Ta "Current pane index" -.It Li "#S" Ta "Session name" -.It Li "#T" Ta "Current pane title" -.It Li "#W" Ta "Current window name" .It Li "##" Ta "A literal" Ql # .El .Pp -The #(shell-command) form executes -.Ql shell-command -and inserts the first line of its output. -Note that shell commands are only executed once at the interval specified by -the -.Ic status-interval -option: if the status line is redrawn in the meantime, the previous result is -used. -Shell commands are executed with the -.Nm -global environment set (see the -.Sx ENVIRONMENT -section). -.Pp For details on how the names and titles can be set see the .Sx "NAMES AND TITLES" section. +For a list of allowed attributes see the +.Ic message-command-style +option. .Pp -#[attributes] allows a comma-separated list of attributes to be specified, -these may be -.Ql fg=colour -to set the foreground colour, -.Ql bg=colour -to set the background colour, the name of one of the attributes (listed under -the -.Ic message-attr -option) to turn an attribute on, or an attribute prefixed with -.Ql no -to turn one off, for example -.Ic nobright . Examples are: .Bd -literal -offset indent #(sysctl vm.loadavg) #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp -Where appropriate, special character sequences may be prefixed with a number to -specify the maximum length, for example -.Ql #24T . -.Pp -By default, UTF-8 in -.Ar string -is not interpreted, to enable UTF-8, use the -.Ic status-utf8 -option. -.It Ic status-left-attr Ar attributes -Set the attribute of the left part of the status line. -.It Ic status-left-bg Ar colour -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. +The default is +.Ql "[#S] " . .It Ic status-left-length Ar length Set the maximum .Ar length of the left component of the status bar. The default is 10. +.It Ic status-left-style Ar style +Set the style of the left part of the status line. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .It Xo Ic status-position .Op Ic top | bottom .Xc @@ -2539,68 +2825,27 @@ As with .Ic status-left , .Ar string will be passed to -.Xr strftime 3 , -character pairs are replaced, and UTF-8 is dependent on the -.Ic status-utf8 -option. -.It Ic status-right-attr Ar attributes -Set the attribute of the right part of the status line. -.It Ic status-right-bg Ar colour -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. +.Xr strftime 3 +and character pairs are replaced. .It Ic status-right-length Ar length Set the maximum .Ar length of the right component of the status bar. The default is 40. -.It Xo Ic status-utf8 -.Op Ic on | off -.Xc -Instruct -.Nm -to treat top-bit-set characters in the -.Ic status-left -and -.Ic status-right -strings as UTF-8; notably, this is important for wide characters. -This option defaults to off. -.It Ic terminal-overrides Ar string -Contains a list of entries which override terminal descriptions read using -.Xr terminfo 5 . -.Ar string -is a comma-separated list of items each a colon-separated string made up of a -terminal type pattern (matched using -.Xr fnmatch 3 ) -and a set of -.Em name=value -entries. -.Pp -For example, to set the -.Ql clear -.Xr terminfo 5 -entry to -.Ql \ee[H\ee[2J -for all terminal types and the -.Ql dch1 -entry to -.Ql \ee[P -for the -.Ql rxvt -terminal type, the option could be set to the string: -.Bd -literal -offset indent -"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" -.Ed -.Pp -The terminal entry value is passed through -.Xr strunvis 3 -before interpretation. -The default value forcibly corrects the -.Ql colors -entry for terminals which support 88 or 256 colours: -.Bd -literal -offset indent -"*88col*:colors=88,*256col*:colors=256,xterm*:XT" -.Ed +.It Ic status-right-style Ar style +Set the style of the right part of the status line. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.It Ic status-style Ar style +Set status line style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .It Ic update-environment Ar variables Set a space-separated string containing a list of environment variables to be copied into the session environment when a new session is created or an @@ -2629,15 +2874,6 @@ through to the terminal (which normally makes a sound). Also see the .Ic bell-action option. -.It Xo Ic visual-content -.Op Ic on | off -.Xc -Like -.Ic visual-activity , -display a message when content is present in a window -for which the -.Ic monitor-content -window option is enabled. .It Xo Ic visual-silence .Op Ic on | off .Xc @@ -2652,7 +2888,7 @@ The default is .Ql \ -_@ . .El .It Xo Ic set-window-option -.Op Fl agqu +.Op Fl agoqu .Op Fl t Ar target-window .Ar option Ar value .Xc @@ -2661,6 +2897,7 @@ Set a window option. The .Fl a , .Fl g , +.Fl o , .Fl q and .Fl u @@ -2713,8 +2950,8 @@ The default is on. Control automatic window renaming. When this setting is enabled, .Nm -will attempt - on supported platforms - to rename the window to reflect the -command currently running in it. +will rename the window automatically using the format specified by +.Ic automatic-rename-format . This flag is automatically disabled for an individual window when a name is specified at creation with .Ic new-window @@ -2728,23 +2965,12 @@ It may be switched off globally with: set-window-option -g automatic-rename off .Ed .Pp -.It Ic c0-change-interval Ar interval -.It Ic c0-change-trigger Ar trigger -These two options configure a simple form of rate limiting for a pane. -If -.Nm -sees more than -.Ar trigger -C0 sequences that modify the screen (for example, carriage returns, linefeeds -or backspaces) in one millisecond, it will stop updating the pane immediately and -instead redraw it entirely every -.Ar interval -milliseconds. -This helps to prevent fast output (such as -.Xr yes 1 -overwhelming the terminal). -The default is a trigger of 250 and an interval of 100. -A trigger of zero disables the rate limiting. +.It Ic automatic-rename-format Ar format +The format (see +.Sx FORMATS ) +used when the +.Ic automatic-rename +option is enabled. .Pp .It Ic clock-mode-colour Ar colour Set clock colour. @@ -2772,15 +2998,6 @@ or .Ic main-vertical layouts. .Pp -.It Ic mode-attr Ar attributes -Set window modes attributes. -.Pp -.It Ic mode-bg Ar colour -Set window modes background colour. -.Pp -.It Ic mode-fg Ar colour -Set window modes foreground colour. -.Pp .It Xo Ic mode-keys .Op Ic vi | emacs .Xc @@ -2794,17 +3011,13 @@ or contains .Ql vi . .Pp -.It Xo Ic mode-mouse -.Op Ic on | off | copy-mode -.Xc -Mouse state in modes. -If on, the mouse may be used to enter copy mode and copy a selection by -dragging, to enter copy mode and scroll with the mouse wheel, or to select an -option in choice mode. -If set to -.Em copy-mode , -the mouse behaves as set to on, but cannot be used to enter copy -mode. +.It Ic mode-style Ar style +Set window modes style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp .It Xo Ic monitor-activity .Op Ic on | off @@ -2812,14 +3025,6 @@ mode. Monitor for activity in the window. Windows with activity are highlighted in the status line. .Pp -.It Ic monitor-content Ar match-string -Monitor content in the window. -When -.Xr fnmatch 3 -pattern -.Ar match-string -appears in the window, it is highlighted in the status line. -.Pp .It Xo Ic monitor-silence .Op Ic interval .Xc @@ -2849,11 +3054,29 @@ but set the width of other panes in the .Ic main-vertical layout. .Pp +.It Ic pane-active-border-style Ar style +Set the pane border style for the currently active pane. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +Attributes are ignored. +.Pp .It Ic pane-base-index Ar index Like .Ic base-index , but set the starting index for pane numbers. .Pp +.It Ic pane-border-style Ar style +Set the pane border style for panes aside from the active pane. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +Attributes are ignored. +.Pp .It Xo Ic remain-on-exit .Op Ic on | off .Xc @@ -2869,71 +3092,42 @@ command. Duplicate input to any pane to all other panes in the same window (only for panes that are not in any special mode). .Pp -.It Xo Ic utf8 -.Op Ic on | off -.Xc -Instructs -.Nm -to expect UTF-8 sequences to appear in this window. +.It Ic window-active-style Ar style +Set the style for the window's active pane. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp -.It Ic window-status-bell-attr Ar attributes -Set status line attributes for windows which have a bell alert. +.It Ic window-status-activity-style Ar style +Set status line style for windows with an activity alert. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp -.It Ic window-status-bell-bg Ar colour -Set status line background colour for windows with a bell alert. -.Pp -.It Ic window-status-bell-fg Ar colour -Set status line foreground colour for windows with a bell alert. -.Pp -.It Ic window-status-content-attr Ar attributes -Set status line attributes for windows which have a content alert. -.Pp -.It Ic window-status-content-bg Ar colour -Set status line background colour for windows with a content alert. -.Pp -.It Ic window-status-content-fg Ar colour -Set status line foreground colour for windows with a content alert. -.Pp -.It Ic window-status-activity-attr Ar attributes -Set status line attributes for windows which have an activity (or silence) alert. -.Pp -.It Ic window-status-activity-bg Ar colour -Set status line background colour for windows with an activity alert. -.Pp -.It Ic window-status-activity-fg Ar colour -Set status line foreground colour for windows with an activity alert. -.Pp -.It Ic window-status-attr Ar attributes -Set status line attributes for a single window. -.Pp -.It Ic window-status-bg Ar colour -Set status line background colour for a single window. -.Pp -.It Ic window-status-current-attr Ar attributes -Set status line attributes for the currently active window. -.Pp -.It Ic window-status-current-bg Ar colour -Set status line background colour for the currently active window. -.Pp -.It Ic window-status-current-fg Ar colour -Set status line foreground colour for the currently active window. +.It Ic window-status-bell-style Ar style +Set status line style for windows with a bell alert. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp .It Ic window-status-current-format Ar string Like .Ar window-status-format , but is the format used when the window is the current window. .Pp -.It Ic window-status-last-attr Ar attributes -Set status line attributes for the last active window. -.Pp -.It Ic window-status-last-bg Ar colour -Set status line background colour for the last active window. -.Pp -.It Ic window-status-last-fg Ar colour -Set status line foreground colour for the last active window. -.Pp -.It Ic window-status-fg Ar colour -Set status line foreground colour for a single window. +.It Ic window-status-current-style Ar style +Set status line style for the currently active window. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. .Pp .It Ic window-status-format Ar string Set the format in which the window is displayed in the status line window list. @@ -2943,10 +3137,34 @@ option for details of special character sequences available. The default is .Ql #I:#W#F . .Pp +.It Ic window-status-last-style Ar style +Set status line style for the last active window. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Ic window-status-separator Ar string Sets the separator drawn between windows in the status line. The default is a single space character. .Pp +.It Ic window-status-style Ar style +Set status line style for a single window. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp +.It Ic window-style Ar style +Set the default window style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Xo Ic xterm-keys .Op Ic on | off .Xc @@ -3002,6 +3220,110 @@ is used. .Fl v shows only the option value, not the name. .El +.Sh HOOKS +.Nm +allows commands to run on various triggers, called +.Em hooks . +Each hook has a +.Em name . +The following hooks are available: +.Bl -tag -width "XXXXXXXXXXXXXXXX" +.It client-attached +Run when a client is attached. +.It client-detached +Run when a client is detached +.It client-resized +Run when a client is resized. +.It pane-died +Run when the program running in a pane exits, but +.Ic remain-on-exit +is on so the pane has not closed. +.It pane-exited +Run when the program running in a pane exits. +.El +.Pp +Hooks are managed with these commands: +.Bl -tag -width Ds +.It Xo Ic set-hook +.Op Fl g +.Op Fl t Ar target-session +.Ar hook-name +.Ar command +.Xc +Sets hook +.Ar hook-name +to +.Ar command . +If +.Fl g +is given, +.Em hook-name +is added to the global list of hooks, otherwise it is added to the session +hooks (for +.Ar target-session +with +.Fl t ) . +Like options, session hooks inherit from the global ones. +.It Xo Ic show-hooks +.Op Fl g +.Op Fl t Ar target-session +.Xc +Shows the global list of hooks with +.Fl g , +otherwise the session hooks. +.El +.Sh MOUSE SUPPORT +If the +.Ic mouse +option is on (the default is off), +.Nm +allows mouse events to be bound as keys. +The name of each key is made up of a mouse event (such as +.Ql MouseUp1 ) +and a location suffix (one of +.Ql Pane +for the contents of a pane, +.Ql Border +for a pane border or +.Ql Status +for the status line). +The following mouse events are available: +.Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent +.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" +.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" +.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" +.It Li "WheelUp" Ta "WheelDown" Ta "" +.El +.Pp +Each should be suffixed with a location, for example +.Ql MouseDown1Status . +.Pp +The special token +.Ql {mouse} +or +.Ql = +may be used as +.Ar target-window +or +.Ar target-pane +in commands bound to mouse key bindings. +It resolves to the window or pane over which the mouse event took place +(for example, the window in the status line over which button 1 was released for a +.Ql MouseUp1Status +binding, or the pane over which the wheel was scrolled for a +.Ql WheelDownPane +binding). +.Pp +The +.Ic send-keys +.Fl M +flag may be used to forward a mouse event to a pane. +.Pp +The default key bindings allow the mouse to be used to select and resize panes, +to copy text and to change window using the status line. +These take effect if the +.Ic mouse +option is turned on. .Sh FORMATS Certain commands accept the .Fl F @@ -3009,18 +3331,23 @@ flag with a .Ar format argument. This is a string which controls the output format of the command. -Special character sequences are replaced as documented under the -.Ic status-left -option and an additional long form is accepted. Replacement variables are enclosed in .Ql #{ and .Ql } , for example -.Ql #{session_name} -is equivalent to -.Ql #S . -Conditionals are also accepted by prefixing with +.Ql #{session_name} . +The possible variables are listed in the table below, or the name of a +.Nm +option may be used for an option's value. +Some variables have a shorter alias such as +.Ql #S , +and +.Ql ## +is replaced by a single +.Ql # . +.Pp +Conditionals are available by prefixing with .Ql \&? and separating two alternatives with a comma; if the specified variable exists and is not zero, the first alternative @@ -3031,85 +3358,161 @@ will include the string .Ql attached if the session is attached and the string .Ql not attached -if it is unattached. +if it is unattached, or +.Ql #{?automatic-rename,yes,no} +will include +.Ql yes +if +.Ic automatic-rename +is enabled, or +.Ql no +if not. +.Pp +A limit may be placed on the length of the resultant string by prefixing it +by an +.Ql = , +a number and a colon, so +.Ql #{=10:pane_title} +will include at most the first 10 characters of the pane title. +Prefixing a time variable with +.Ql t: +will convert it to a string, so if +.Ql #{window_activity} +gives +.Ql 1445765102 , +.Ql #{t:window_activity} +gives +.Ql Sun Oct 25 09:25:02 2015 . +The +.Ql b: +and +.Ql d: +prefixes are +.Xr basename 3 +and +.Xr dirname 3 +of the variable respectively. +A prefix of the form +.Ql s/foo/bar/: +will substitute +.Ql foo +with +.Ql bar +throughout. +.Pp +In addition, the first line of a shell command's output may be inserted using +.Ql #() . +For example, +.Ql #(uptime) +will insert the system's uptime. +When constructing formats, +.Nm +does not wait for +.Ql #() +commands to finish; instead, the previous result from running the same command is used, +or a placeholder if the command has not been run before. +Commands are executed with the +.Nm +global environment set (see the +.Sx ENVIRONMENT +section). .Pp The following variables are available, where appropriate: -.Bl -column "session_created_string" "Replaced with" -offset indent -.It Sy "Variable name" Ta Sy "Replaced with" -.It Li "alternate_on" Ta "If pane is in alternate screen" -.It Li "alternate_saved_x" Ta "Saved cursor X in alternate screen" -.It Li "alternate_saved_y" Ta "Saved cursor Y in alternate screen" -.It Li "buffer_sample" Ta "First 50 characters from the specified buffer" -.It Li "buffer_size" Ta "Size of the specified buffer in bytes" -.It Li "client_activity" Ta "Integer time client last had activity" -.It Li "client_activity_string" Ta "String time client last had activity" -.It Li "client_created" Ta "Integer time client created" -.It Li "client_created_string" Ta "String time client created" -.It Li "client_cwd" Ta "Working directory of client" -.It Li "client_height" Ta "Height of client" -.It Li "client_last_session" Ta "Name of the client's last session" -.It Li "client_prefix" Ta "1 if prefix key has been pressed" -.It Li "client_readonly" Ta "1 if client is readonly" -.It Li "client_session" Ta "Name of the client's session" -.It Li "client_termname" Ta "Terminal name of client" -.It Li "client_tty" Ta "Pseudo terminal of client" -.It Li "client_utf8" Ta "1 if client supports utf8" -.It Li "client_width" Ta "Width of client" -.It Li "cursor_flag" Ta "Pane cursor flag" -.It Li "cursor_x" Ta "Cursor X position in pane" -.It Li "cursor_y" Ta "Cursor Y position in pane" -.It Li "history_bytes" Ta "Number of bytes in window history" -.It Li "history_limit" Ta "Maximum window history lines" -.It Li "history_size" Ta "Size of history in bytes" -.It Li "host" Ta "Hostname of local host" -.It Li "insert_flag" Ta "Pane insert flag" -.It Li "keypad_cursor_flag" Ta "Pane keypad cursor flag" -.It Li "keypad_flag" Ta "Pane keypad flag" -.It Li "line" Ta "Line number in the list" -.It Li "mouse_any_flag" Ta "Pane mouse any flag" -.It Li "mouse_button_flag" Ta "Pane mouse button flag" -.It Li "mouse_standard_flag" Ta "Pane mouse standard flag" -.It Li "mouse_utf8_flag" Ta "Pane mouse UTF-8 flag" -.It Li "pane_active" Ta "1 if active pane" -.It Li "pane_current_command" Ta "Current command if available" -.It Li "pane_current_path" Ta "Current path if available" -.It Li "pane_dead" Ta "1 if pane is dead" -.It Li "pane_height" Ta "Height of pane" -.It Li "pane_id" Ta "Unique pane ID" -.It Li "pane_in_mode" Ta "If pane is in a mode" -.It Li "pane_index" Ta "Index of pane" -.It Li "pane_pid" Ta "PID of first process in pane" -.It Li "pane_start_command" Ta "Command pane started with" -.It Li "pane_start_path" Ta "Path pane started with" -.It Li "pane_tabs" Ta "Pane tab positions" -.It Li "pane_title" Ta "Title of pane" -.It Li "pane_tty" Ta "Pseudo terminal of pane" -.It Li "pane_width" Ta "Width of pane" -.It Li "saved_cursor_x" Ta "Saved cursor X in pane" -.It Li "saved_cursor_y" Ta "Saved cursor Y in pane" -.It Li "scroll_region_lower" Ta "Bottom of scroll region in pane" -.It Li "scroll_region_upper" Ta "Top of scroll region in pane" -.It Li "session_attached" Ta "1 if session attached" -.It Li "session_created" Ta "Integer time session created" -.It Li "session_created_string" Ta "String time session created" -.It Li "session_group" Ta "Number of session group" -.It Li "session_grouped" Ta "1 if session in a group" -.It Li "session_height" Ta "Height of session" -.It Li "session_id" Ta "Unique session ID" -.It Li "session_name" Ta "Name of session" -.It Li "session_width" Ta "Width of session" -.It Li "session_windows" Ta "Number of windows in session" -.It Li "window_active" Ta "1 if window active" -.It Li "window_find_matches" Ta "Matched data from the find-window command if available" -.It Li "window_flags" Ta "Window flags" -.It Li "window_height" Ta "Height of window" -.It Li "window_id" Ta "Unique window ID" -.It Li "window_index" Ta "Index of window" -.It Li "window_layout" Ta "Window layout description" -.It Li "window_name" Ta "Name of window" -.It Li "window_panes" Ta "Number of panes in window" -.It Li "window_width" Ta "Width of window" -.It Li "wrap_flag" Ta "Pane wrap flag" +.Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" +.It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" +.It Li "alternate_on" Ta "" Ta "If pane is in alternate screen" +.It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" +.It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" +.It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" +.It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" +.It Li "client_activity" Ta "" Ta "Integer time client last had activity" +.It Li "client_created" Ta "" Ta "Integer time client created" +.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" +.It Li "client_height" Ta "" Ta "Height of client" +.It Li "client_key_table" Ta "" Ta "Current key table" +.It Li "client_last_session" Ta "" Ta "Name of the client's last session" +.It Li "client_pid" Ta "" Ta "PID of client process" +.It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" +.It Li "client_readonly" Ta "" Ta "1 if client is readonly" +.It Li "client_session" Ta "" Ta "Name of the client's session" +.It Li "client_termname" Ta "" Ta "Terminal name of client" +.It Li "client_tty" Ta "" Ta "Pseudo terminal of client" +.It Li "client_utf8" Ta "" Ta "1 if client supports utf8" +.It Li "client_width" Ta "" Ta "Width of client" +.It Li "command_name" Ta "" Ta "Name of command in use, if any" +.It Li "cursor_flag" Ta "" Ta "Pane cursor flag" +.It Li "cursor_x" Ta "" Ta "Cursor X position in pane" +.It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" +.It Li "history_bytes" Ta "" Ta "Number of bytes in window history" +.It Li "history_limit" Ta "" Ta "Maximum window history lines" +.It Li "history_size" Ta "" Ta "Size of history in bytes" +.It Li "host" Ta "#H" Ta "Hostname of local host" +.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" +.It Li "insert_flag" Ta "" Ta "Pane insert flag" +.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" +.It Li "keypad_flag" Ta "" Ta "Pane keypad flag" +.It Li "line" Ta "" Ta "Line number in the list" +.It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" +.It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" +.It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" +.It Li "pane_active" Ta "" Ta "1 if active pane" +.It Li "pane_bottom" Ta "" Ta "Bottom of pane" +.It Li "pane_current_command" Ta "" Ta "Current command if available" +.It Li "pane_current_path" Ta "" Ta "Current path if available" +.It Li "pane_dead" Ta "" Ta "1 if pane is dead" +.It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" +.It Li "pane_height" Ta "" Ta "Height of pane" +.It Li "pane_id" Ta "#D" Ta "Unique pane ID" +.It Li "pane_in_mode" Ta "" Ta "If pane is in a mode" +.It Li "pane_input_off" Ta "" Ta "If input to pane is disabled" +.It Li "pane_index" Ta "#P" Ta "Index of pane" +.It Li "pane_left" Ta "" Ta "Left of pane" +.It Li "pane_pid" Ta "" Ta "PID of first process in pane" +.It Li "pane_right" Ta "" Ta "Right of pane" +.It Li "pane_start_command" Ta "" Ta "Command pane started with" +.It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" +.It Li "pane_tabs" Ta "" Ta "Pane tab positions" +.It Li "pane_title" Ta "#T" Ta "Title of pane" +.It Li "pane_top" Ta "" Ta "Top of pane" +.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" +.It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "pid" Ta "" Ta "Server PID" +.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" +.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" +.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" +.It Li "session_attached" Ta "" Ta "Number of clients session is attached to" +.It Li "session_activity" Ta "" Ta "Integer time of session last activity" +.It Li "session_created" Ta "" Ta "Integer time session created" +.It Li "session_last_attached" Ta "" Ta "Integer time session last attached" +.It Li "session_group" Ta "" Ta "Number of session group" +.It Li "session_grouped" Ta "" Ta "1 if session in a group" +.It Li "session_height" Ta "" Ta "Height of session" +.It Li "session_id" Ta "" Ta "Unique session ID" +.It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" +.It Li "session_name" Ta "#S" Ta "Name of session" +.It Li "session_width" Ta "" Ta "Width of session" +.It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "socket_path" Ta "" "Server socket path" +.It Li "start_time" Ta "" Ta "Server start time" +.It Li "window_activity" Ta "" Ta "Integer time of window last activity" +.It Li "window_active" Ta "" Ta "1 if window active" +.It Li "window_bell_flag" Ta "" Ta "1 if window has bell" +.It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" +.It Li "window_flags" Ta "#F" Ta "Window flags" +.It Li "window_height" Ta "" Ta "Height of window" +.It Li "window_id" Ta "" Ta "Unique window ID" +.It Li "window_index" Ta "#I" Ta "Index of window" +.It Li "window_last_flag" Ta "" Ta "1 if window is the last used" +.It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" +.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" +.It Li "window_name" Ta "#W" Ta "Name of window" +.It Li "window_panes" Ta "" Ta "Number of panes in window" +.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" +.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes" +.It Li "window_width" Ta "" Ta "Width of window" +.It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" +.It Li "wrap_flag" Ta "" Ta "Pane wrap flag" .El .Sh NAMES AND TITLES .Nm @@ -3212,7 +3615,7 @@ flag unsets a variable. indicates the variable is to be removed from the environment before starting a new process. .It Xo Ic show-environment -.Op Fl g +.Op Fl gs .Op Fl t Ar target-session .Op Ar variable .Xc @@ -3226,6 +3629,9 @@ If is omitted, all variables are shown. Variables removed from the environment are prefixed with .Ql - . +If +.Fl s +is used, the output is formatted as a set of Bourne shell commands. .El .Sh STATUS LINE .Nm @@ -3260,31 +3666,23 @@ The flag is one of the following symbols appended to the window name: .It Li "-" Ta "Marks the last window (previously selected)." .It Li "#" Ta "Window is monitored and activity has been detected." .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 "The window has been silent for the monitor-silence interval." +.It Li "M" Ta "The window contains the marked pane." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp The # symbol relates to the .Ic monitor-activity -and + to the -.Ic monitor-content -window options. +window option. The window name is printed in inverted colours if an alert (bell, activity or -content) is present. +silence) is present. .Pp The colour and attributes of the status line may be configured, the entire status line using the -.Ic status-attr , -.Ic status-fg -and -.Ic status-bg -session options and individual windows using the -.Ic window-status-attr , -.Ic window-status-fg -and -.Ic window-status-bg -window options. +.Ic status-style +session option and individual windows using the +.Ic window-status-style +window option. .Pp The status line is automatically refreshed at interval if it has changed, the interval may be controlled with the @@ -3391,19 +3789,40 @@ is given, otherwise the active pane for the session attached to .El .Sh BUFFERS .Nm -maintains a stack of +maintains a set of named .Em paste buffers . -Up to the value of the +Each buffer may be either explicitly or automatically named. +Explicitly named buffers are named when created with the +.Ic set-buffer +or +.Ic load-buffer +commands, or by renaming an automatically named buffer with +.Ic set-buffer +.Fl n . +Automatically named buffers are given a name such as +.Ql buffer0001 , +.Ql buffer0002 +and so on. +When the .Ic buffer-limit -option are kept; when a new buffer is added, the buffer at the bottom of the -stack is removed. +option is reached, the oldest automatically named buffer is deleted. +Explicitly named are not subject to +.Ic buffer-limit +and may be deleted with +.Ic delete-buffer +command. +.Pp Buffers may be added using .Ic copy-mode or the .Ic set-buffer -command, and pasted into a window using the +and +.Ic load-buffer +commands, and pasted into a window using the .Ic paste-buffer command. +If a buffer command is used and no buffer is specified, the most +recently added automatically named buffer is assumed. .Pp A configurable history buffer is also maintained for each window. By default, up to 2000 lines are kept; this can be altered with the @@ -3424,7 +3843,7 @@ Put a window into buffer choice mode, where a buffer may be chosen interactively from a list. After a buffer is selected, .Ql %% -is replaced by the buffer index in +is replaced by the buffer name in .Ar template and the result executed as a command. If @@ -3439,11 +3858,11 @@ This command works only if at least one client is attached. .It Ic clear-history Op Fl t Ar target-pane .D1 (alias: Ic clearhist ) Remove and free the history for the specified pane. -.It Ic delete-buffer Op Fl b Ar buffer-index +.It Ic delete-buffer Op Fl b Ar buffer-name .D1 (alias: Ic deleteb ) -Delete the buffer at -.Ar buffer-index , -or the top buffer if not specified. +Delete the buffer named +.Ar buffer-name , +or the most recently added automatically named buffer if not specified. .It Xo Ic list-buffers .Op Fl F Ar format .Xc @@ -3455,7 +3874,7 @@ flag, see the .Sx FORMATS section. .It Xo Ic load-buffer -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic loadb ) @@ -3463,7 +3882,7 @@ Load the contents of the specified paste buffer from .Ar path . .It Xo Ic paste-buffer .Op Fl dpr -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Op Fl s Ar separator .Op Fl t Ar target-pane .Xc @@ -3472,7 +3891,7 @@ Insert the contents of a paste buffer into the specified pane. If not specified, paste into the current one. With .Fl d , -also delete the paste buffer from the stack. +also delete the paste buffer. When output, any linefeed (LF) characters in the paste buffer are replaced with a separator, by default carriage return (CR). A custom separator may be specified using the @@ -3487,7 +3906,7 @@ is specified, paste bracket control codes are inserted around the buffer if the application has requested bracketed paste mode. .It Xo Ic save-buffer .Op Fl a -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic saveb ) @@ -3497,14 +3916,23 @@ The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer -.Op Fl b Ar buffer-index +.Op Fl a +.Op Fl b Ar buffer-name +.Op Fl n Ar new-buffer-name .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . +The +.Fl a +option appends to rather than overwriting the buffer. +The +.Fl n +option renames the buffer to +.Ar new-buffer-name . .It Xo Ic show-buffer -.Op Fl b Ar buffer-index +.Op Fl b Ar buffer-name .Xc .D1 (alias: Ic showb ) Display the contents of the specified buffer. @@ -3515,7 +3943,7 @@ Miscellaneous commands are as follows: .It Ic clock-mode Op Fl t Ar target-pane Display a large clock. .It Xo Ic if-shell -.Op Fl b +.Op Fl bF .Op Fl t Ar target-pane .Ar shell-command command .Op Ar command @@ -3528,7 +3956,9 @@ if returns success or the second .Ar command otherwise. -Before being executed, shell-command is expanded using the rules specified in the +Before being executed, +.Ar shell-command +is expanded using the rules specified in the .Sx FORMATS section, including those relevant to .Ar target-pane . @@ -3536,13 +3966,20 @@ With .Fl b , .Ar shell-command is run in the background. +.Pp +If +.Fl F +is given, +.Ar shell-command +is not executed but considered success if neither empty nor zero (after formats +are expanded). .It Ic lock-server .D1 (alias: Ic lock ) Lock each client individually by running the command specified by the .Ic lock-command option. .It Xo Ic run-shell -.Fl b +.Op Fl b .Op Fl t Ar target-pane .Ar shell-command .Xc @@ -3562,11 +3999,8 @@ specified by .Fl t or the current pane if omitted). If the command doesn't return success, the exit status is also displayed. -.It Ic server-info -.D1 (alias: Ic info ) -Show server information and terminal details. .It Xo Ic wait-for -.Fl LSU +.Op Fl L | S | U .Ar channel .Xc .D1 (alias: Ic wait ) @@ -3588,7 +4022,7 @@ This command only works from outside understands some extensions to .Xr terminfo 5 : .Bl -tag -width Ds -.It Em Cc , Cr +.It Em Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; the second takes no arguments and restores the default cursor colour. @@ -3598,8 +4032,8 @@ to change the cursor colour from inside .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed -.It Em Cs , Csr -Change the cursor style. +.It Em \&Ss , Se +Set or reset the cursor style. If set, a sequence such as this may be used to change the cursor to an underline: .Bd -literal -offset indent @@ -3607,10 +4041,8 @@ $ printf '\e033[4 q' .Ed .Pp If -.Em Csr -is set, it will be used to reset the cursor style instead -of -.Em Cs . +.Em Se +is not set, \&Ss with argument 0 will be used to reset the cursor style instead. .It Em \&Ms This sequence can be used by .Nm @@ -3668,12 +4100,16 @@ or an error occurred. If present, .Ar reason describes why the client exited. -.It Ic %layout-change Ar window-id Ar window-layout +.It Ic %layout-change Ar window-id Ar window-layout Ar window-visible-layout Ar window-flags The layout of a window with ID .Ar window-id changed. The new layout is .Ar window-layout . +The window's visible layout is +.Ar window-visible-layout +and the window flags are +.Ar window-flags . .It Ic %output Ar pane-id Ar value A window pane produced output. .Ar value @@ -3707,12 +4143,12 @@ was renamed to .Ar name . .El .Sh FILES -.Bl -tag -width "/etc/tmux.confXXX" -compact +.Bl -tag -width "@SYSCONFDIR@/tmux.confXXX" -compact .It Pa ~/.tmux.conf Default .Nm configuration file. -.It Pa /etc/tmux.conf +.It Pa @SYSCONFDIR@/tmux.conf System-wide configuration file. .El .Sh EXAMPLES @@ -3788,7 +4224,7 @@ bind-key C-a send-prefix Turning the status line off, or changing its colour: .Bd -literal -offset indent set-option -g status off -set-option -g status-bg blue +set-option -g status-style bg=blue .Ed .Pp Setting other options, such as the default command, @@ -3807,4 +4243,4 @@ bind-key S command-prompt "new-window -n %1 'ssh %1'" .Sh SEE ALSO .Xr pty 4 .Sh AUTHORS -.An Nicholas Marriott Aq nicm@users.sourceforge.net +.An Nicholas Marriott Aq Mt nicm@users.sourceforge.net diff --git a/tmux.c b/tmux.c index b1b4f525..528990a2 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -19,12 +19,15 @@ #include #include +#include #include #include #include +#include #include #include #include +#include #include #include "tmux.h" @@ -33,26 +36,17 @@ extern char *malloc_options; #endif -struct options global_options; /* server options */ -struct options global_s_options; /* session options */ -struct options global_w_options; /* window options */ -struct environ global_environ; +struct options *global_options; /* server options */ +struct options *global_s_options; /* session options */ +struct options *global_w_options; /* window options */ +struct environ *global_environ; +struct hooks *global_hooks; -struct event_base *ev_base; - -char *cfg_file, *tmate_cfg_file; -char *shell_cmd; -int debug_level; -time_t start_time; -char socket_path[MAXPATHLEN]; -int login_shell; -char *environ_path; -pid_t environ_pid = -1; -int environ_session_id = -1; +struct timeval start_time; +const char *socket_path; __dead void usage(void); -void parseenvironment(void); -char *makesocketpath(const char *); +static char *make_label(const char *); #ifndef HAVE___PROGNAME char *__progname = (char *) "tmux"; @@ -62,24 +56,12 @@ __dead void usage(void) { fprintf(stderr, - "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", __progname); exit(1); } -void -logfile(const char *name) -{ - char *path; - - if (debug_level > 0) { - xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); - log_open(debug_level, path); - free(path); - } -} - const char * getshell(void) { @@ -126,81 +108,48 @@ areshell(const char *shell) return (0); } -const char* -get_full_path(const char *wd, const char *path) +static char * +make_label(const char *label) { - static char newpath[MAXPATHLEN]; - char oldpath[MAXPATHLEN]; + char *base, resolved[PATH_MAX], *path, *s; + struct stat sb; + u_int uid; + int saved_errno; - if (getcwd(oldpath, sizeof oldpath) == NULL) - return (NULL); - if (chdir(wd) != 0) - return (NULL); - if (realpath(path, newpath) != 0) - return (NULL); - chdir(oldpath); - return (newpath); -} - -void -parseenvironment(void) -{ - char *env, path[256]; - long pid; - int id; - - if ((env = getenv("TMUX")) == NULL) - return; - - if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3) - return; - environ_path = xstrdup(path); - environ_pid = pid; - environ_session_id = id; -} - -char * -makesocketpath(const char *label) -{ - char base[MAXPATHLEN], realbase[MAXPATHLEN], *path, *s; - struct stat sb; - u_int uid; - int do_random_label = 1; + if (label == NULL) + label = "default"; uid = getuid(); - if ((s = getenv("TMPDIR")) == NULL || *s == '\0') - xsnprintf(base, sizeof base, "%s/tmate-%u", _PATH_TMP, uid); + + if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') + xasprintf(&base, "%s/tmux-%u", s, uid); else - xsnprintf(base, sizeof base, "%s/tmate-%u", s, uid); + xasprintf(&base, "%s/tmux-%u", _PATH_TMP, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) - return (NULL); + goto fail; if (lstat(base, &sb) != 0) - return (NULL); + goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; - return (NULL); + goto fail; } - if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { + if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { errno = EACCES; - return (NULL); + goto fail; } - if (realpath(base, realbase) == NULL) - strlcpy(realbase, base, sizeof realbase); - - if (!label) { - do_random_label = 1; - label = "XXXXXX"; - } - - xasprintf(&path, "%s/%s", realbase, label); - - if (do_random_label) - mktemp(path); - + if (realpath(base, resolved) == NULL) + strlcpy(resolved, base, sizeof resolved); + xasprintf(&path, "%s/%s", resolved, label); return (path); + +fail: + saved_errno = errno; + free(base); + errno = saved_errno; + return (NULL); } void @@ -217,93 +166,90 @@ setblocking(int fd, int state) } } -__dead void -shell_exec(const char *shell, const char *shellcmd) +const char * +find_home(void) { - const char *shellname, *ptr; - char *argv0; + struct passwd *pw; + static const char *home; - ptr = strrchr(shell, '/'); - if (ptr != NULL && *(ptr + 1) != '\0') - shellname = ptr + 1; - else - shellname = shell; - if (login_shell) - xasprintf(&argv0, "-%s", shellname); - else - xasprintf(&argv0, "%s", shellname); - setenv("SHELL", shell, 1); + if (home != NULL) + return (home); - setblocking(STDIN_FILENO, 1); - setblocking(STDOUT_FILENO, 1); - setblocking(STDERR_FILENO, 1); - closefrom(STDERR_FILENO + 1); + home = getenv("HOME"); + if (home == NULL || *home == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + home = pw->pw_dir; + else + home = NULL; + } - execl(shell, argv0, "-c", shellcmd, (char *) NULL); - fatal("execl failed"); + return (home); } int main(int argc, char **argv) { - struct passwd *pw; - char *s, *path, *label, *home, **var; - int opt, flags, quiet, keys; + char *path, *label, **var, tmp[PATH_MAX], *shellcmd = NULL; + const char *s; + int opt, flags, keys; #if defined(DEBUG) && defined(__OpenBSD__) malloc_options = (char *) "AFGJPX"; #endif - flags = IDENTIFY_256COLOURS | IDENTIFY_UTF8; - quiet = 0; + setlocale(LC_TIME, ""); + tzset(); + + if (**argv == '-') + flags = CLIENT_LOGIN; + else + flags = 0; + +#ifdef TMATE + flags |= CLIENT_256COLOURS | CLIENT_UTF8; +#endif + label = path = NULL; - login_shell = (**argv == '-'); - while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUvV")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { switch (opt) { case '2': - flags |= IDENTIFY_256COLOURS; - flags &= ~IDENTIFY_88COLOURS; - break; - case '8': - flags |= IDENTIFY_88COLOURS; - flags &= ~IDENTIFY_256COLOURS; + flags |= CLIENT_256COLOURS; break; case 'c': - free(shell_cmd); - shell_cmd = xstrdup(optarg); + free(shellcmd); + shellcmd = xstrdup(optarg); break; case 'C': - if (flags & IDENTIFY_CONTROL) - flags |= IDENTIFY_TERMIOS; + if (flags & CLIENT_CONTROL) + flags |= CLIENT_CONTROLCONTROL; else - flags |= IDENTIFY_CONTROL; + flags |= CLIENT_CONTROL; break; case 'V': printf("%s %s\n", __progname, VERSION); exit(0); case 'f': - free(cfg_file); - cfg_file = xstrdup(optarg); + set_cfg_file(optarg); break; case 'l': - login_shell = 1; + flags |= CLIENT_LOGIN; break; case 'L': free(label); label = xstrdup(optarg); break; case 'q': - quiet = 1; break; case 'S': free(path); path = xstrdup(optarg); break; case 'u': - flags |= IDENTIFY_UTF8; + flags |= CLIENT_UTF8; break; case 'v': - debug_level++; + log_add_level(); break; default: usage(); @@ -312,47 +258,54 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (shell_cmd != NULL && argc != 0) + if (shellcmd != NULL && argc != 0) usage(); - if (!(flags & IDENTIFY_UTF8)) { - /* - * If the user has set whichever of LC_ALL, LC_CTYPE or LANG - * exist (in that order) to contain UTF-8, it is a safe - * assumption that either they are using a UTF-8 terminal, or - * if not they know that output from UTF-8-capable programs may - * be wrong. - */ - if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { - if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') - s = getenv("LANG"); - } - if (s != NULL && (strcasestr(s, "UTF-8") != NULL || - strcasestr(s, "UTF8") != NULL)) - flags |= IDENTIFY_UTF8; +#ifdef __OpenBSD__ + if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " + "recvfd proc exec tty ps", NULL) != 0) + err(1, "pledge"); +#endif + + /* + * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. + * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain + * UTF-8, it is a safe assumption that either they are using a UTF-8 + * terminal, or if not they know that output from UTF-8-capable + * programs may be wrong. + */ + if (getenv("TMUX") != NULL) + flags |= CLIENT_UTF8; + else { + s = getenv("LC_ALL"); + if (s == NULL || *s == '\0') + s = getenv("LC_CTYPE"); + if (s == NULL || *s == '\0') + s = getenv("LANG"); + if (s == NULL || *s == '\0') + s = ""; + if (strcasestr(s, "UTF-8") != NULL || + strcasestr(s, "UTF8") != NULL) + flags |= CLIENT_UTF8; } - environ_init(&global_environ); + global_hooks = hooks_create(NULL); + + global_environ = environ_create(); for (var = environ; *var != NULL; var++) - environ_put(&global_environ, *var); + environ_put(global_environ, *var); + if (getcwd(tmp, sizeof tmp) != NULL) + environ_set(global_environ, "PWD", "%s", tmp); - options_init(&global_options, NULL); - options_table_populate_tree(server_options_table, &global_options); - options_set_number(&global_options, "quiet", quiet); + global_options = options_create(NULL); + options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); - options_init(&global_s_options, NULL); - options_table_populate_tree(session_options_table, &global_s_options); - options_set_string(&global_s_options, "default-shell", "%s", getshell()); + global_s_options = options_create(NULL); + options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); + options_set_string(global_s_options, "default-shell", "%s", getshell()); - options_init(&global_w_options, NULL); - options_table_populate_tree(window_options_table, &global_w_options); - - /* Enable UTF-8 if the first client is on UTF-8 terminal. */ - if (flags & IDENTIFY_UTF8) { - options_set_number(&global_s_options, "status-utf8", 1); - options_set_number(&global_s_options, "mouse-utf8", 1); - options_set_number(&global_w_options, "utf8", 1); - } + global_w_options = options_create(NULL); + options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { @@ -362,62 +315,29 @@ main(int argc, char **argv) keys = MODEKEY_VI; else keys = MODEKEY_EMACS; - options_set_number(&global_s_options, "status-keys", keys); - options_set_number(&global_w_options, "mode-keys", keys); - } - - /* Locate the configuration file. */ - home = getenv("HOME"); - if (home == NULL || *home == '\0') { - pw = getpwuid(getuid()); - if (pw != NULL) - home = pw->pw_dir; - } - - if (cfg_file == NULL) { - xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); - if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { - free(cfg_file); - cfg_file = NULL; - } - } - - xasprintf(&tmate_cfg_file, "%s/%s", home, DEFAULT_TMATE_CFG); - if (access(tmate_cfg_file, R_OK) != 0 && errno == ENOENT) { - free(tmate_cfg_file); - tmate_cfg_file = NULL; + options_set_number(global_s_options, "status-keys", keys); + options_set_number(global_w_options, "mode-keys", keys); } /* - * Figure out the socket path. If specified on the command-line with -S - * or -L, use it, otherwise try $TMUX or assume -L default. + * If socket is specified on the command-line with -S or -L, it is + * used. Otherwise, $TMUX is checked and if that fails "default" is + * used. */ - parseenvironment(); - if (path == NULL) { - /* If no -L, use the environment. */ - if (label == NULL) { - if (environ_path != NULL) - path = xstrdup(environ_path); - } - - /* -L or default set. */ - if (!path) { - if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket\n"); - exit(1); - } + if (path == NULL && label == NULL) { + s = getenv("TMUX"); + if (s != NULL && *s != '\0' && *s != ',') { + path = xstrdup(s); + path[strcspn (path, ",")] = '\0'; } } + if (path == NULL && (path = make_label(label)) == NULL) { + fprintf(stderr, "can't create socket: %s\n", strerror(errno)); + exit(1); + } + socket_path = path; free(label); - strlcpy(socket_path, path, sizeof socket_path); - free(path); - -#ifdef HAVE_SETPROCTITLE - /* Set process title. */ - setproctitle("%s (%s)", __progname, socket_path); -#endif /* Pass control to the client. */ - ev_base = osdep_event_init(); - exit(client_main(argc, argv, flags)); + exit(client_main(event_init(), argc, argv, flags, shellcmd)); } diff --git a/tmux.h b/tmux.h index fa335c6a..24a7db59 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -21,33 +21,38 @@ #define TMATE -#define PROTOCOL_VERSION 7 +#define PROTOCOL_VERSION 8 -#include #include #include #include #include -#include #include #include #include -#include "array.h" +#ifdef HAVE_UTEMPTER +#include +#endif #include "compat.h" +#include "xmalloc.h" extern char *__progname; extern char **environ; -/* Default configuration files. */ -#define DEFAULT_CFG ".tmux.conf" -#define DEFAULT_TMATE_CFG ".tmate.conf" -#define SYSTEM_CFG "/etc/tmux.conf" +struct client; +struct environ; +struct input_ctx; +struct mouse_event; +struct options; +struct session; +struct tmuxpeer; +struct tmuxproc; -/* Default prompt history length. */ -#define PROMPT_HISTORY 100 +/* Default global configuration file. */ +#define TMUX_CONF "/etc/tmux.conf" /* * Minimum layout cell size, NOT including separator line. The scroll region @@ -55,136 +60,86 @@ extern char **environ; */ #define PANE_MINIMUM 2 -/* Automatic name refresh interval, in milliseconds. */ -#define NAME_INTERVAL 500 +/* Automatic name refresh interval, in microseconds. Must be < 1 second. */ +#define NAME_INTERVAL 500000 /* - * Maximum sizes of strings in message data. Don't forget to bump - * PROTOCOL_VERSION if any of these change! + * READ_SIZE is the maximum size of data to hold from a pty (the event high + * watermark). READ_BACKOFF is the amount of data waiting to be output to a tty + * before pty reads will be backed off. READ_TIME is how long to back off + * before the next read (in microseconds) if a tty is above READ_BACKOFF. */ -#define COMMAND_LENGTH 2048 /* packed argv size */ -#define TERMINAL_LENGTH 128 /* length of TERM environment variable */ -#define ENVIRON_LENGTH 1024 /* environment variable length */ - -/* - * UTF-8 data size. This must be big enough to hold combined characters as well - * as single. - */ -#define UTF8_SIZE 9 - -/* Fatal errors. */ -#define fatal(msg) log_fatal("%s: %s", __func__, msg); -#define fatalx(msg) log_fatalx("%s: %s", __func__, msg); - -/* Definition to shut gcc up about unused arguments. */ -#define unused __attribute__ ((unused)) +#define READ_SIZE 1024 +#define READ_BACKOFF 512 +#define READ_TIME 100 /* Attribute to make gcc check printf-like arguments. */ -#define printflike1 __attribute__ ((format (printf, 1, 2))) -#define printflike2 __attribute__ ((format (printf, 2, 3))) -#define printflike3 __attribute__ ((format (printf, 3, 4))) -#define printflike4 __attribute__ ((format (printf, 4, 5))) -#define printflike5 __attribute__ ((format (printf, 5, 6))) +#define printflike(a, b) __attribute__ ((format (printf, a, b))) /* Number of items in array. */ #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif -/* Default template for choose-buffer. */ -#define CHOOSE_BUFFER_TEMPLATE \ - "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" - -/* Default template for choose-client. */ -#define CHOOSE_CLIENT_TEMPLATE \ - "#{client_tty}: #{session_name} " \ - "[#{client_width}x#{client_height} #{client_termname}]" \ - "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \ - "(last used #{client_activity_string})" - -/* Default templates for choose-tree. */ -#define CHOOSE_TREE_SESSION_TEMPLATE \ - "#{session_name}: #{session_windows} windows" \ - "#{?session_grouped, (group ,}" \ - "#{session_group}#{?session_grouped,),}" \ - "#{?session_attached, (attached),}" -#define CHOOSE_TREE_WINDOW_TEMPLATE \ - "#{window_index}: #{window_name}#{window_flags} " \ - "\"#{pane_title}\"" - -/* Default template for display-message. */ -#define DISPLAY_MESSAGE_TEMPLATE \ - "[#{session_name}] #{window_index}:" \ - "#{window_name}, current pane #{pane_index} " \ - "- (%H:%M %d-%b-%y)" - -/* Default template for find-window. */ -#define FIND_WINDOW_TEMPLATE \ - "#{window_index}: #{window_name} " \ - "[#{window_width}x#{window_height}] " \ - "(#{window_panes} panes) #{window_find_matches}" - -/* Default template for list-buffers. */ -#define LIST_BUFFERS_TEMPLATE \ - "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" - -/* Default template for list-clients. */ -#define LIST_CLIENTS_TEMPLATE \ - "#{client_tty}: #{session_name} " \ - "[#{client_width}x#{client_height} #{client_termname}]" \ - "#{?client_utf8, (utf8),} #{?client_readonly, (ro),}" - -/* Default template for list-sessions. */ -#define LIST_SESSIONS_TEMPLATE \ - "#{session_name}: #{session_windows} windows " \ - "(created #{session_created_string}) " \ - "[#{session_width}x#{session_height}]" \ - "#{?session_grouped, (group ,}" \ - "#{session_group}#{?session_grouped,),}" \ - "#{?session_attached, (attached),}" - -/* Default templates for list-windows. */ -#define LIST_WINDOWS_TEMPLATE \ - "#{window_index}: #{window_name}#{window_flags} " \ - "(#{window_panes} panes) " \ - "[#{window_width}x#{window_height}] " \ - "[layout #{window_layout}] #{window_id}" \ - "#{?window_active, (active),}"; -#define LIST_WINDOWS_WITH_SESSION_TEMPLATE \ - "#{session_name}: " \ - "#{window_index}: #{window_name}#{window_flags} " \ - "(#{window_panes} panes) " \ - "[#{window_width}x#{window_height}] " - -/* Default templates for break-pane, new-window and split-window. */ -#define BREAK_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" -#define NEW_SESSION_TEMPLATE "#{session_name}:" -#define NEW_WINDOW_TEMPLATE BREAK_PANE_TEMPLATE -#define SPLIT_WINDOW_TEMPLATE BREAK_PANE_TEMPLATE - /* Bell option values. */ #define BELL_NONE 0 #define BELL_ANY 1 #define BELL_CURRENT 2 +#define BELL_OTHER 3 /* Special key codes. */ -#define KEYC_NONE 0xfff -#define KEYC_BASE 0x1000 +#define KEYC_NONE 0xffff00000000ULL +#define KEYC_UNKNOWN 0xfffe00000000ULL +#define KEYC_BASE 0x100000000000ULL /* Key modifier bits. */ -#define KEYC_ESCAPE 0x2000 -#define KEYC_CTRL 0x4000 -#define KEYC_SHIFT 0x8000 -#define KEYC_PREFIX 0x10000 +#define KEYC_ESCAPE 0x200000000000ULL +#define KEYC_CTRL 0x400000000000ULL +#define KEYC_SHIFT 0x800000000000ULL /* Mask to obtain key w/o modifiers. */ -#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX) +#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) -/* Other key codes. */ -enum key_code { - /* Mouse key. */ - KEYC_MOUSE = KEYC_BASE, +/* Is this a mouse key? */ +#define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ + ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) + +/* Mouse key codes. */ +#define KEYC_MOUSE_KEY(name) \ + KEYC_ ## name ## _PANE, \ + KEYC_ ## name ## _STATUS, \ + KEYC_ ## name ## _BORDER +#define KEYC_MOUSE_STRING(name, s) \ + { #s "Pane", KEYC_ ## name ## _PANE }, \ + { #s "Status", KEYC_ ## name ## _STATUS }, \ + { #s "Border", KEYC_ ## name ## _BORDER } + +/* + * A single key. This can be ASCII or Unicode or one of the keys starting at + * KEYC_BASE. + */ +typedef unsigned long long key_code; + +/* Special key codes. */ +enum { + /* Focus events. */ + KEYC_FOCUS_IN = KEYC_BASE, + KEYC_FOCUS_OUT, + + /* Mouse keys. */ + KEYC_MOUSE, /* unclassified mouse event */ + KEYC_MOUSE_KEY(MOUSEDOWN1), + KEYC_MOUSE_KEY(MOUSEDOWN2), + KEYC_MOUSE_KEY(MOUSEDOWN3), + KEYC_MOUSE_KEY(MOUSEUP1), + KEYC_MOUSE_KEY(MOUSEUP2), + KEYC_MOUSE_KEY(MOUSEUP3), + KEYC_MOUSE_KEY(MOUSEDRAG1), + KEYC_MOUSE_KEY(MOUSEDRAG2), + KEYC_MOUSE_KEY(MOUSEDRAG3), + KEYC_MOUSE_KEY(WHEELUP), + KEYC_MOUSE_KEY(WHEELDOWN), /* Backspace key. */ KEYC_BSPACE, @@ -202,14 +157,6 @@ enum key_code { KEYC_F10, KEYC_F11, KEYC_F12, - KEYC_F13, - KEYC_F14, - KEYC_F15, - KEYC_F16, - KEYC_F17, - KEYC_F18, - KEYC_F19, - KEYC_F20, KEYC_IC, KEYC_DC, KEYC_HOME, @@ -241,27 +188,23 @@ enum key_code { KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, - - KEYC_FOCUS_IN, - KEYC_FOCUS_OUT, }; /* Termcap codes. */ enum tty_code_code { TTYC_AX = 0, TTYC_ACSC, /* acs_chars, ac */ + TTYC_BCE, /* back_color_erase, ut */ TTYC_BEL, /* bell, bl */ TTYC_BLINK, /* enter_blink_mode, mb */ TTYC_BOLD, /* enter_bold_mode, md */ - TTYC_CC, /* set colour cursor, Cc */ TTYC_CIVIS, /* cursor_invisible, vi */ TTYC_CLEAR, /* clear_screen, cl */ TTYC_CNORM, /* cursor_normal, ve */ TTYC_COLORS, /* max_colors, Co */ TTYC_CR, /* restore cursor colour, Cr */ - TTYC_CS1, /* set cursor style, Cs */ + TTYC_CS, /* set cursor colour, Cs */ TTYC_CSR, /* change_scroll_region, cs */ - TTYC_CSR1, /* reset cursor style, Csr */ TTYC_CUB, /* parm_left_cursor, LE */ TTYC_CUB1, /* cursor_left, le */ TTYC_CUD, /* parm_down_cursor, DO */ @@ -271,6 +214,7 @@ enum tty_code_code { TTYC_CUP, /* cursor_address, cm */ TTYC_CUU, /* parm_up_cursor, UP */ TTYC_CUU1, /* cursor_up, up */ + TTYC_CVVIS, /* cursor_visible, vs */ TTYC_DCH, /* parm_dch, DC */ TTYC_DCH1, /* delete_character, dc */ TTYC_DIM, /* enter_dim_mode, mh */ @@ -317,26 +261,69 @@ enum tty_code_code { TTYC_KEND5, TTYC_KEND6, TTYC_KEND7, - TTYC_KF1, /* key_f1, k1 */ - TTYC_KF10, /* key_f10, k; */ - TTYC_KF11, /* key_f11, F1 */ - TTYC_KF12, /* key_f12, F2 */ - TTYC_KF13, /* key_f13, F3 */ - TTYC_KF14, /* key_f14, F4 */ - TTYC_KF15, /* key_f15, F5 */ - TTYC_KF16, /* key_f16, F6 */ - TTYC_KF17, /* key_f17, F7 */ - TTYC_KF18, /* key_f18, F8 */ - TTYC_KF19, /* key_f19, F9 */ - TTYC_KF2, /* key_f2, k2 */ - TTYC_KF20, /* key_f20, F10 */ - TTYC_KF3, /* key_f3, k3 */ - TTYC_KF4, /* key_f4, k4 */ - TTYC_KF5, /* key_f5, k5 */ - TTYC_KF6, /* key_f6, k6 */ - TTYC_KF7, /* key_f7, k7 */ - TTYC_KF8, /* key_f8, k8 */ - TTYC_KF9, /* key_f9, k9 */ + TTYC_KF1, + TTYC_KF10, + TTYC_KF11, + TTYC_KF12, + TTYC_KF13, + TTYC_KF14, + TTYC_KF15, + TTYC_KF16, + TTYC_KF17, + TTYC_KF18, + TTYC_KF19, + TTYC_KF2, + TTYC_KF20, + TTYC_KF21, + TTYC_KF22, + TTYC_KF23, + TTYC_KF24, + TTYC_KF25, + TTYC_KF26, + TTYC_KF27, + TTYC_KF28, + TTYC_KF29, + TTYC_KF3, + TTYC_KF30, + TTYC_KF31, + TTYC_KF32, + TTYC_KF33, + TTYC_KF34, + TTYC_KF35, + TTYC_KF36, + TTYC_KF37, + TTYC_KF38, + TTYC_KF39, + TTYC_KF4, + TTYC_KF40, + TTYC_KF41, + TTYC_KF42, + TTYC_KF43, + TTYC_KF44, + TTYC_KF45, + TTYC_KF46, + TTYC_KF47, + TTYC_KF48, + TTYC_KF49, + TTYC_KF5, + TTYC_KF50, + TTYC_KF51, + TTYC_KF52, + TTYC_KF53, + TTYC_KF54, + TTYC_KF55, + TTYC_KF56, + TTYC_KF57, + TTYC_KF58, + TTYC_KF59, + TTYC_KF6, + TTYC_KF60, + TTYC_KF61, + TTYC_KF62, + TTYC_KF63, + TTYC_KF7, + TTYC_KF8, + TTYC_KF9, TTYC_KHOM2, TTYC_KHOM3, TTYC_KHOM4, @@ -391,6 +378,7 @@ enum tty_code_code { TTYC_RMACS, /* exit_alt_charset_mode */ TTYC_RMCUP, /* exit_ca_mode, te */ TTYC_RMKX, /* keypad_local, ke */ + TTYC_SE, /* reset cursor style, Se */ TTYC_SETAB, /* set_a_background, AB */ TTYC_SETAF, /* set_a_foreground, AF */ TTYC_SGR0, /* exit_attribute_mode, me */ @@ -400,64 +388,44 @@ enum tty_code_code { TTYC_SMKX, /* keypad_xmit, ks */ TTYC_SMSO, /* enter_standout_mode, so */ TTYC_SMUL, /* enter_underline_mode, us */ + TTYC_SS, /* set cursor style, Ss */ TTYC_TSL, /* to_status_line, tsl */ TTYC_VPA, /* row_address, cv */ TTYC_XENL, /* eat_newline_glitch, xn */ TTYC_XT, /* xterm(1)-compatible title, XT */ }; -#define NTTYCODE (TTYC_XT + 1) - -/* Termcap types. */ -enum tty_code_type { - TTYCODE_NONE = 0, - TTYCODE_STRING, - TTYCODE_NUMBER, - TTYCODE_FLAG, -}; - -/* Termcap code. */ -struct tty_code { - enum tty_code_type type; - union { - char *string; - int number; - int flag; - } value; -}; - -/* Entry in terminal code table. */ -struct tty_term_code_entry { - enum tty_code_code code; - enum tty_code_type type; - const char *name; -}; - -/* List of error causes. */ -ARRAY_DECL(causelist, char *); /* Message codes. */ enum msgtype { - MSG_COMMAND, + MSG_VERSION = 12, + + MSG_IDENTIFY_FLAGS = 100, + MSG_IDENTIFY_TERM, + MSG_IDENTIFY_TTYNAME, + MSG_IDENTIFY_OLDCWD, /* unused */ + MSG_IDENTIFY_STDIN, + MSG_IDENTIFY_ENVIRON, + MSG_IDENTIFY_DONE, + MSG_IDENTIFY_CLIENTPID, + MSG_IDENTIFY_CWD, + + MSG_COMMAND = 200, MSG_DETACH, - MSG_ERROR, + MSG_DETACHKILL, MSG_EXIT, MSG_EXITED, MSG_EXITING, - MSG_IDENTIFY, - MSG_STDIN, + MSG_LOCK, MSG_READY, MSG_RESIZE, - MSG_SHUTDOWN, - MSG_SUSPEND, - MSG_VERSION, - MSG_WAKEUP, - MSG_ENVIRON, - MSG_UNLOCK, - MSG_LOCK, MSG_SHELL, + MSG_SHUTDOWN, MSG_STDERR, + MSG_STDIN, MSG_STDOUT, - MSG_DETACHKILL + MSG_SUSPEND, + MSG_UNLOCK, + MSG_WAKEUP, }; /* @@ -466,41 +434,8 @@ enum msgtype { * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { - pid_t pid; /* from $TMUX or -1 */ - int session_id; /* from $TMUX or -1 */ - - int argc; - char argv[COMMAND_LENGTH]; -}; - -struct msg_identify_data { - char cwd[MAXPATHLEN]; - - char term[TERMINAL_LENGTH]; - -#define IDENTIFY_UTF8 0x1 -#define IDENTIFY_256COLOURS 0x2 -#define IDENTIFY_88COLOURS 0x4 -#define IDENTIFY_CONTROL 0x8 -#define IDENTIFY_TERMIOS 0x10 - int flags; -}; - -struct msg_lock_data { - char cmd[COMMAND_LENGTH]; -}; - -struct msg_environ_data { - char var[ENVIRON_LENGTH]; -}; - -struct msg_shell_data { - char shell[MAXPATHLEN]; -}; - -struct msg_exit_data { - int retcode; -}; + int argc; +}; /* followed by packed argv */ struct msg_stdin_data { ssize_t size; @@ -548,18 +483,25 @@ enum mode_key_cmd { MODEKEYEDIT_SWITCHMODEAPPEND, MODEKEYEDIT_SWITCHMODEAPPENDLINE, MODEKEYEDIT_SWITCHMODEBEGINLINE, + MODEKEYEDIT_SWITCHMODECHANGELINE, + MODEKEYEDIT_SWITCHMODESUBSTITUTE, + MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, MODEKEYEDIT_TRANSPOSECHARS, /* Menu (choice) keys. */ MODEKEYCHOICE_BACKSPACE, + MODEKEYCHOICE_BOTTOMLINE, MODEKEYCHOICE_CANCEL, MODEKEYCHOICE_CHOOSE, MODEKEYCHOICE_DOWN, + MODEKEYCHOICE_ENDOFLIST, MODEKEYCHOICE_PAGEDOWN, MODEKEYCHOICE_PAGEUP, MODEKEYCHOICE_SCROLLDOWN, MODEKEYCHOICE_SCROLLUP, MODEKEYCHOICE_STARTNUMBERPREFIX, + MODEKEYCHOICE_STARTOFLIST, + MODEKEYCHOICE_TOPLINE, MODEKEYCHOICE_TREE_COLLAPSE, MODEKEYCHOICE_TREE_COLLAPSE_ALL, MODEKEYCHOICE_TREE_EXPAND, @@ -568,6 +510,7 @@ enum mode_key_cmd { MODEKEYCHOICE_UP, /* Copy keys. */ + MODEKEYCOPY_APPENDSELECTION, MODEKEYCOPY_BACKTOINDENTATION, MODEKEYCOPY_BOTTOMLINE, MODEKEYCOPY_CANCEL, @@ -596,6 +539,7 @@ enum mode_key_cmd { MODEKEYCOPY_NEXTSPACEEND, MODEKEYCOPY_NEXTWORD, MODEKEYCOPY_NEXTWORDEND, + MODEKEYCOPY_OTHEREND, MODEKEYCOPY_PREVIOUSPAGE, MODEKEYCOPY_PREVIOUSSPACE, MODEKEYCOPY_PREVIOUSWORD, @@ -608,6 +552,7 @@ enum mode_key_cmd { MODEKEYCOPY_SEARCHREVERSE, MODEKEYCOPY_SEARCHUP, MODEKEYCOPY_SELECTLINE, + MODEKEYCOPY_STARTNAMEDBUFFER, MODEKEYCOPY_STARTNUMBERPREFIX, MODEKEYCOPY_STARTOFLINE, MODEKEYCOPY_STARTSELECTION, @@ -615,20 +560,6 @@ enum mode_key_cmd { MODEKEYCOPY_UP, }; -/* Entry in the default mode key tables. */ -struct mode_key_entry { - int key; - - /* - * Editing mode for vi: 0 is edit mode, keys not in the table are - * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table - * are returned as MODEKEY_NONE. This is also matched on, allowing some - * keys to be bound in edit mode. - */ - int mode; - enum mode_key_cmd cmd; -}; - /* Data required while mode keys are in use. */ struct mode_key_data { struct mode_key_tree *tree; @@ -639,7 +570,7 @@ struct mode_key_data { /* Binding between a key and a command. */ struct mode_key_binding { - int key; + key_code key; int mode; enum mode_key_cmd cmd; @@ -656,6 +587,7 @@ struct mode_key_cmdstr { }; /* Named mode key table description. */ +struct mode_key_entry; struct mode_key_table { const char *name; const struct mode_key_cmdstr *cmdstr; @@ -671,34 +603,33 @@ struct mode_key_table { #define MODE_WRAP 0x10 /* whether lines wrap */ #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 -#define MODE_MOUSE_ANY 0x80 +#define MODE_BLINKING 0x80 #define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 -#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY) +#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON) -/* A single UTF-8 character. */ +/* + * A single UTF-8 character. UTF8_SIZE must be big enough to hold at least one + * combining character as well. +*/ +#define UTF8_SIZE 9 struct utf8_data { u_char data[UTF8_SIZE]; - size_t have; - size_t size; + u_char have; + u_char size; - u_int width; + u_char width; /* 0xff if invalid */ +} __packed; +enum utf8_state { + UTF8_MORE, + UTF8_DONE, + UTF8_ERROR }; -/* Grid output. */ -#if defined(DEBUG) && \ - ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ - (defined(__GNUC__) && __GNUC__ >= 3)) -#define GRID_DEBUG(gd, fmt, ...) log_debug2("%s: (sx=%u, sy=%u, hsize=%u) " \ - fmt, __func__, (gd)->sx, (gd)->sy, (gd)->hsize, ## __VA_ARGS__) -#else -#define GRID_DEBUG(...) -#endif - /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 #define GRID_ATTR_DIM 0x2 @@ -713,66 +644,93 @@ struct utf8_data { #define GRID_FLAG_FG256 0x1 #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 +#define GRID_FLAG_EXTENDED 0x8 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 /* Grid cell data. */ struct grid_cell { - u_char attr; - u_char flags; - u_char fg; - u_char bg; + u_char flags; + u_char attr; + u_char fg; + u_char bg; + struct utf8_data data; - u_char xstate; /* top 4 bits width, bottom 4 bits size */ - u_char xdata[UTF8_SIZE]; +}; +struct grid_cell_entry { + u_char flags; + union { + u_int offset; + struct { + u_char attr; + u_char fg; + u_char bg; + u_char data; + } data; + }; } __packed; /* Grid line. */ struct grid_line { - u_int cellsize; - struct grid_cell *celldata; + u_int cellsize; + struct grid_cell_entry *celldata; - int flags; + u_int extdsize; + struct grid_cell *extddata; + + int flags; } __packed; /* Entire grid of cells. */ struct grid { - int flags; -#define GRID_HISTORY 0x1 /* scroll lines into history */ + int flags; +#define GRID_HISTORY 0x1 /* scroll lines into history */ - u_int sx; - u_int sy; + u_int sx; + u_int sy; - u_int hsize; - u_int hlimit; + u_int hsize; + u_int hlimit; - struct grid_line *linedata; + struct grid_line *linedata; +}; + +/* Hook data structures. */ +struct hook { + const char *name; + + struct cmd_q *cmdq; + struct cmd_list *cmdlist; + + RB_ENTRY(hook) entry; }; /* Option data structures. */ struct options_entry { - char *name; + const char *name; enum { OPTIONS_STRING, OPTIONS_NUMBER, - OPTIONS_DATA, + OPTIONS_STYLE } type; - char *str; - long long num; + char *str; + long long num; + struct grid_cell style; RB_ENTRY(options_entry) entry; }; -struct options { - RB_HEAD(options_tree, options_entry) tree; - struct options *parent; -}; - /* Scheduled job. */ struct job { + enum { + JOB_RUNNING, + JOB_DEAD, + JOB_CLOSED + } state; + char *cmd; pid_t pid; int status; @@ -792,6 +750,13 @@ LIST_HEAD(joblist, job); struct screen_sel { int flag; int rectflag; + enum { + LINE_SEL_NONE, + LINE_SEL_LEFT_RIGHT, + LINE_SEL_RIGHT_LEFT, + } lineflag; + + int modekeys; u_int sx; u_int sy; @@ -836,59 +801,16 @@ struct screen_write_ctx { #define screen_hsize(s) ((s)->grid->hsize) #define screen_hlimit(s) ((s)->grid->hlimit) -/* Input parser context. */ -struct input_ctx { - struct window_pane *wp; - struct screen_write_ctx ctx; - - struct grid_cell cell; - - struct grid_cell old_cell; - u_int old_cx; - u_int old_cy; - - u_char interm_buf[4]; - size_t interm_len; - - u_char param_buf[64]; - size_t param_len; - - u_char input_buf[256]; - size_t input_len; - - int param_list[24]; /* -1 not present */ - u_int param_list_len; - - struct utf8_data utf8data; - - int ch; - int flags; -#define INPUT_DISCARD 0x1 - - const struct input_state *state; - - /* - * All input received since we were last in the ground state. Sent to - * control clients on connection. - */ - struct evbuffer *since_ground; -}; - /* * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ -struct session; -struct window; -struct mouse_event; struct window_mode { struct screen *(*init)(struct window_pane *); void (*free)(struct window_pane *); void (*resize)(struct window_pane *, u_int, u_int); - void (*key)(struct window_pane *, struct session *, int); - void (*mouse)(struct window_pane *, - struct session *, struct mouse_event *); - void (*timer)(struct window_pane *); + void (*key)(struct window_pane *, struct client *, struct session *, + key_code, struct mouse_event *); }; /* Structures for choose mode. */ @@ -907,23 +829,16 @@ struct window_choose_data { struct winlink *wl; int pane_id; - char *ft_template; + char *ft_template; struct format_tree *ft; char *command; }; -struct window_choose_mode_item { - struct window_choose_data *wcd; - char *name; - int pos; - int state; -#define TREE_EXPANDED 0x1 -}; - /* Child window structure. */ struct window_pane { u_int id; + u_int active_point; struct window *window; @@ -941,28 +856,34 @@ struct window_pane { #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 #define PANE_RESIZE 0x8 +#define PANE_FOCUSPUSH 0x10 +#define PANE_INPUTOFF 0x20 +#define PANE_CHANGED 0x40 - char *cmd; + int argc; + char **argv; char *shell; - char *cwd; + const char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; - - u_int changes; - struct event changes_timer; - u_int changes_redraw; + int status; int fd; struct bufferevent *event; + struct event timer; - struct input_ctx ictx; + struct input_ctx *ictx; + + struct grid_cell colgc; int pipe_fd; struct bufferevent *pipe_event; size_t pipe_off; +#ifdef TMATE size_t tmate_off; +#endif struct screen *screen; struct screen base; @@ -985,9 +906,14 @@ RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ struct window { u_int id; + char *name; - struct event name_timer; - struct timeval silence_timer; + struct event name_event; + struct timeval name_time; + + struct event alerts_timer; + + struct timeval activity_time; struct window_pane *active; struct window_pane *last; @@ -996,6 +922,7 @@ struct window { int lastlayout; struct layout_cell *layout_root; struct layout_cell *saved_layout_root; + char *old_layout; u_int sx; u_int sy; @@ -1005,13 +932,18 @@ struct window { #define WINDOW_ACTIVITY 0x2 #define WINDOW_REDRAW 0x4 #define WINDOW_SILENCE 0x8 -#define WINDOW_ZOOMED 0x10 +#define WINDOW_ZOOMED 0x1000 +#define WINDOW_FORCEWIDTH 0x2000 +#define WINDOW_FORCEHEIGHT 0x4000 +#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) - struct options options; + struct options *options; u_int references; + + RB_ENTRY(window) entry; }; -ARRAY_DECL(windows, struct window *); +RB_HEAD(windows, window); /* Entry on local window list. */ struct winlink { @@ -1022,13 +954,11 @@ struct winlink { struct grid_cell status_cell; char *status_text; - int flags; + int flags; #define WINLINK_BELL 0x1 #define WINLINK_ACTIVITY 0x2 -#define WINLINK_CONTENT 0x4 -#define WINLINK_SILENCE 0x8 -#define WINLINK_ALERTFLAGS \ - (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_CONTENT|WINLINK_SILENCE) +#define WINLINK_SILENCE 0x4 +#define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE) RB_ENTRY(winlink) entry; TAILQ_ENTRY(winlink) sentry; @@ -1064,13 +994,6 @@ struct layout_cell { TAILQ_ENTRY(layout_cell) entry; }; -/* Paste buffer. */ -struct paste_buffer { - char *data; - size_t size; -}; -ARRAY_DECL(paste_stack, struct paste_buffer *); - /* Environment variable. */ struct environ_entry { char *name; @@ -1078,7 +1001,6 @@ struct environ_entry { RB_ENTRY(environ_entry) entry; }; -RB_HEAD(environ, environ_entry); /* Client session. */ struct session_group { @@ -1092,12 +1014,15 @@ struct session { u_int id; char *name; - char *cwd; + const char *cwd; struct timeval creation_time; + struct timeval last_attached_time; struct timeval activity_time; struct timeval last_activity_time; + struct event lock_timer; + u_int sx; u_int sy; @@ -1105,14 +1030,18 @@ struct session { struct winlink_stack lastw; struct winlinks windows; - struct options options; + struct hooks *hooks; + struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ +#define SESSION_PASTING 0x2 int flags; + u_int attached; + struct termios *tio; - struct environ environ; + struct environ *environ; int references; @@ -1120,12 +1049,52 @@ struct session { RB_ENTRY(session) entry; }; RB_HEAD(sessions, session); -ARRAY_DECL(sessionslist, struct session *); + +/* Mouse button masks. */ +#define MOUSE_MASK_BUTTONS 3 +#define MOUSE_MASK_SHIFT 4 +#define MOUSE_MASK_META 8 +#define MOUSE_MASK_CTRL 16 +#define MOUSE_MASK_DRAG 32 +#define MOUSE_MASK_WHEEL 64 + +/* Mouse wheel states. */ +#define MOUSE_WHEEL_UP 0 +#define MOUSE_WHEEL_DOWN 64 + +/* Mouse helpers. */ +#define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS) +#define MOUSE_WHEEL(b) ((b) & MOUSE_MASK_WHEEL) +#define MOUSE_DRAG(b) ((b) & MOUSE_MASK_DRAG) +#define MOUSE_RELEASE(b) (((b) & MOUSE_MASK_BUTTONS) == 3) + +/* Mouse input. */ +struct mouse_event { + int valid; + + key_code key; + int statusat; + + u_int x; + u_int y; + u_int b; + + u_int lx; + u_int ly; + u_int lb; + + int s; + int w; + int wp; + + u_int sgr_type; + u_int sgr_b; +}; /* TTY information. */ struct tty_key { char ch; - int key; + key_code key; struct tty_key *left; struct tty_key *right; @@ -1133,76 +1102,26 @@ struct tty_key { struct tty_key *next; }; +struct tty_code; struct tty_term { char *name; u_int references; char acs[UCHAR_MAX + 1][2]; - struct tty_code codes[NTTYCODE]; + struct tty_code *codes; #define TERM_256COLOURS 0x1 -#define TERM_88COLOURS 0x2 -#define TERM_EARLYWRAP 0x4 +#define TERM_EARLYWRAP 0x2 int flags; LIST_ENTRY(tty_term) entry; }; LIST_HEAD(tty_terms, tty_term); -/* Mouse wheel states. */ -#define MOUSE_WHEEL_UP 0 -#define MOUSE_WHEEL_DOWN 1 - -/* Mouse events. */ -#define MOUSE_EVENT_DOWN (1 << 0) -#define MOUSE_EVENT_DRAG (1 << 1) -#define MOUSE_EVENT_UP (1 << 2) -#define MOUSE_EVENT_CLICK (1 << 3) -#define MOUSE_EVENT_WHEEL (1 << 4) - -/* Mouse flags. */ -#define MOUSE_RESIZE_PANE (1 << 0) - -/* - * Mouse input. When sent by xterm: - * - * - buttons are in the bottom two bits: 0 = b1; 1 = b2; 2 = b3; 3 = released - * - bits 3, 4 and 5 are for keys - * - bit 6 is set for dragging - * - bit 7 for buttons 4 and 5 - * - * With the SGR 1006 extension the released button becomes known. Store these - * in separate fields and store the value converted to the old format in xb. - */ -struct mouse_event { - u_int xb; - - u_int x; - u_int lx; - u_int sx; - - u_int y; - u_int ly; - u_int sy; - - u_int sgr; /* whether the input arrived in SGR format */ - u_int sgr_xb; /* only for SGR: the unmangled button */ - u_int sgr_rel; /* only for SGR: if it is a release event */ - - u_int button; - u_int clicks; - - int wheel; - int event; - int flags; -}; - struct tty { struct client *client; - char *path; - u_int class; u_int sx; u_int sy; @@ -1223,8 +1142,6 @@ struct tty { int fd; struct bufferevent *event; - int log_fd; - struct termios tio; struct grid_cell cell; @@ -1235,17 +1152,23 @@ struct tty { #define TTY_UTF8 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 +#define TTY_FOCUS 0x40 int flags; int term_flags; struct mouse_event mouse; + int mouse_drag_flag; + void (*mouse_drag_update)(struct client *, + struct mouse_event *); + void (*mouse_drag_release)(struct client *, + struct mouse_event *); struct event key_timer; struct tty_key *key_tree; }; -/* TTY command context and function pointer. */ +/* TTY command context. */ struct tty_ctx { struct window_pane *wp; @@ -1275,74 +1198,78 @@ struct tty_ctx { /* Saved message entry. */ struct message_entry { - char *msg; - time_t msg_time; + char *msg; + u_int msg_num; + time_t msg_time; + TAILQ_ENTRY(message_entry) entry; }; -/* Status output data from a job. */ -struct status_out { - char *cmd; - char *out; - - RB_ENTRY(status_out) entry; -}; -RB_HEAD(status_out_tree, status_out); - /* Client connection. */ struct client { - struct imsgbuf ibuf; + struct tmuxpeer *peer; + + pid_t pid; + int fd; struct event event; - int retcode; + int retval; struct timeval creation_time; struct timeval activity_time; - struct environ environ; + struct environ *environ; char *title; - char *cwd; + const char *cwd; + char *term; + char *ttyname; struct tty tty; void (*stdin_callback)(struct client *, int, void *); void *stdin_callback_data; struct evbuffer *stdin_data; - int stdin_closed; + int stdin_closed; struct evbuffer *stdout_data; struct evbuffer *stderr_data; struct event repeat_timer; - struct status_out_tree status_old; - struct status_out_tree status_new; - struct timeval status_timer; + struct event status_timer; struct screen status; #define CLIENT_TERMINAL 0x1 -#define CLIENT_PREFIX 0x2 +#define CLIENT_LOGIN 0x2 #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 -#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ +#define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 -#define CLIENT_BAD 0x80 +/* 0x80 unused */ #define CLIENT_IDENTIFY 0x100 #define CLIENT_DEAD 0x200 #define CLIENT_BORDERS 0x400 #define CLIENT_READONLY 0x800 #define CLIENT_REDRAWWINDOW 0x1000 #define CLIENT_CONTROL 0x2000 -#define CLIENT_FOCUSED 0x4000 +#define CLIENT_CONTROLCONTROL 0x4000 +#define CLIENT_FOCUSED 0x8000 +#define CLIENT_UTF8 0x10000 +#define CLIENT_256COLOURS 0x20000 +#define CLIENT_IDENTIFIED 0x40000 +#define CLIENT_STATUSFORCE 0x80000 #ifdef TMATE +/* TODO investigate if we can merge with CLIENT_STATUSFORCE */ #define CLIENT_FORCE_STATUS 0x800000 #endif int flags; + struct key_table *keytable; struct event identify_timer; char *message_string; struct event message_timer; - ARRAY_DECL(, struct message_entry) message_log; + u_int message_next; + TAILQ_HEAD(, message_entry) message_log; char *prompt_string; char *prompt_buffer; @@ -1350,7 +1277,7 @@ struct client { int (*prompt_callbackfn)(void *, const char *); void (*prompt_freefn)(void *); void *prompt_data; - u_int prompt_hindex; + u_int prompt_hindex; #define PROMPT_SINGLE 0x1 int prompt_flags; @@ -1364,16 +1291,51 @@ struct client { struct cmd_q *cmdq; int references; + + TAILQ_ENTRY(client) entry; }; -ARRAY_DECL(clients, struct client *); +TAILQ_HEAD(clients, client); -/* Parsed arguments. */ +/* Parsed arguments structures. */ +struct args_entry; +RB_HEAD(args_tree, args_entry); struct args { - bitstr_t *flags; - char *values[SCHAR_MAX]; /* XXX This is awfully big. */ + struct args_tree tree; + int argc; + char **argv; +}; - int argc; - char **argv; +/* Command find structures. */ +enum cmd_find_type { + CMD_FIND_PANE, + CMD_FIND_WINDOW, + CMD_FIND_SESSION, +}; +struct cmd_find_state { + struct cmd_q *cmdq; + int flags; + struct cmd_find_state *current; + + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + int idx; +}; + +/* Command find flags. */ +#define CMD_FIND_PREFER_UNATTACHED 0x1 +#define CMD_FIND_QUIET 0x2 +#define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 +#define CMD_FIND_EXACT_SESSION 0x10 +#define CMD_FIND_EXACT_WINDOW 0x20 + +/* Context for command being executed. */ +struct cmd_state { + struct client *c; + struct cmd_find_state tflag; + struct cmd_find_state sflag; }; /* Command and list of commands. */ @@ -1384,11 +1346,15 @@ struct cmd { char *file; u_int line; +#define CMD_CONTROL 0x1 + int flags; + TAILQ_ENTRY(cmd) qentry; }; + struct cmd_list { - int references; - TAILQ_HEAD(, cmd) list; + int references; + TAILQ_HEAD(, cmd) list; }; /* Command return values. */ @@ -1402,6 +1368,9 @@ enum cmd_retval { /* Command queue entry. */ struct cmd_q_item { struct cmd_list *cmdlist; + + struct mouse_event mouse; + TAILQ_ENTRY(cmd_q_item) qentry; }; TAILQ_HEAD(cmd_q_items, cmd_q_item); @@ -1409,7 +1378,10 @@ TAILQ_HEAD(cmd_q_items, cmd_q_item); /* Command queue. */ struct cmd_q { int references; - int dead; + int flags; +#define CMD_Q_DEAD 0x1 +#define CMD_Q_REENTRY 0x2 +#define CMD_Q_NOHOOKS 0x4 struct client *client; int client_exit; @@ -1417,6 +1389,10 @@ struct cmd_q { struct cmd_q_items queue; struct cmd_q_item *item; struct cmd *cmd; + struct cmd_q *parent; + + struct cmd_find_state current; + struct cmd_state state; time_t time; u_int number; @@ -1424,43 +1400,76 @@ struct cmd_q { void (*emptyfn)(struct cmd_q *); void *data; - struct msg_command_data *msgdata; + TAILQ_ENTRY(cmd_q) waitentry; +}; - TAILQ_ENTRY(cmd_q) waitentry; +/* Command -c, -t or -s flags. */ +enum cmd_entry_flag { + CMD_NONE, + + CMD_CLIENT, + CMD_CLIENT_CANFAIL, + + CMD_SESSION, + CMD_SESSION_CANFAIL, + CMD_SESSION_PREFERUNATTACHED, + CMD_SESSION_WITHPANE, + + CMD_WINDOW, + CMD_WINDOW_CANFAIL, + CMD_WINDOW_MARKED, + CMD_WINDOW_INDEX, + + CMD_PANE, + CMD_PANE_CANFAIL, + CMD_PANE_MARKED, + + CMD_MOVEW_R, }; /* Command definition. */ struct cmd_entry { - const char *name; - const char *alias; + const char *name; + const char *alias; - const char *args_template; - int args_lower; - int args_upper; + struct { + const char *template; + int lower; + int upper; + } args; + const char *usage; - const char *usage; + enum cmd_entry_flag tflag; + enum cmd_entry_flag sflag; + enum cmd_entry_flag cflag; #define CMD_STARTSERVER 0x1 -#define CMD_CANTNEST 0x2 -#define CMD_SENDENVIRON 0x4 -#define CMD_READONLY 0x8 +#define CMD_READONLY 0x2 int flags; - void (*key_binding)(struct cmd *, int); - int (*check)(struct args *); - enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); + enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; -/* Key binding. */ +/* Key binding and key table. */ struct key_binding { - int key; - struct cmd_list *cmdlist; - int can_repeat; + key_code key; + struct cmd_list *cmdlist; + int can_repeat; - RB_ENTRY(key_binding) entry; + RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); +struct key_table { + const char *name; + struct key_bindings key_bindings; + + u_int references; + + RB_ENTRY(key_table) entry; +}; +RB_HEAD(key_tables, key_table); + /* * Option table entries. The option table is the user-visible part of the * option, as opposed to the internal options (struct option) which are just @@ -1473,30 +1482,31 @@ enum options_table_type { OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_ATTRIBUTES, OPTIONS_TABLE_FLAG, - OPTIONS_TABLE_CHOICE + OPTIONS_TABLE_CHOICE, + OPTIONS_TABLE_STYLE +}; +enum options_table_scope { + OPTIONS_TABLE_NONE, + OPTIONS_TABLE_SERVER, + OPTIONS_TABLE_SESSION, + OPTIONS_TABLE_WINDOW, }; struct options_table_entry { - const char *name; - enum options_table_type type; + const char *name; + enum options_table_type type; + enum options_table_scope scope; - u_int minimum; - u_int maximum; - const char **choices; + u_int minimum; + u_int maximum; + const char **choices; - const char *default_str; - long long default_num; + const char *default_str; + long long default_num; + + const char *style; }; -/* Tree of format entries. */ -struct format_entry { - char *key; - char *value; - - RB_ENTRY(format_entry) entry; -}; -RB_HEAD(format_tree, format_entry); - /* Common command usages. */ #define CMD_TARGET_PANE_USAGE "[-t target-pane]" #define CMD_TARGET_WINDOW_USAGE "[-t target-window]" @@ -1506,55 +1516,92 @@ RB_HEAD(format_tree, format_entry); #define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" #define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" -#define CMD_BUFFER_USAGE "[-b buffer-index]" +#define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ -extern struct options global_options; -extern struct options global_s_options; -extern struct options global_w_options; -extern struct environ global_environ; -extern struct event_base *ev_base; -extern char *cfg_file, *tmate_cfg_file; -extern char *shell_cmd; -extern int debug_level; -extern time_t start_time; -extern char socket_path[MAXPATHLEN]; -extern int login_shell; -extern char *environ_path; -extern pid_t environ_pid; -extern int environ_session_id; -void logfile(const char *); +extern struct hooks *global_hooks; +extern struct options *global_options; +extern struct options *global_s_options; +extern struct options *global_w_options; +extern struct environ *global_environ; +extern struct timeval start_time; +extern const char *socket_path; const char *getshell(void); int checkshell(const char *); int areshell(const char *); -const char* get_full_path(const char *, const char *); void setblocking(int, int); -__dead void shell_exec(const char *, const char *); +const char *find_home(void); + +/* proc.c */ +struct imsg; +int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t); +int proc_send_s(struct tmuxpeer *, enum msgtype, const char *); +struct tmuxproc *proc_start(const char *, struct event_base *, int, + void (*)(int)); +void proc_loop(struct tmuxproc *, int (*)(void)); +void proc_exit(struct tmuxproc *); +struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, + void (*)(struct imsg *, void *), void *); +void proc_remove_peer(struct tmuxpeer *); +void proc_kill_peer(struct tmuxpeer *); /* cfg.c */ -extern struct cmd_q *cfg_cmd_q; extern int cfg_finished; extern int cfg_references; -extern struct causelist cfg_causes; +extern struct client *cfg_client; +void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); -void cfg_default_done(struct cmd_q *); +void set_cfg_file(const char *); +void cfg_add_cause(const char *, ...); +void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); +/* paste.c */ +struct paste_buffer; +const char *paste_buffer_name(struct paste_buffer *); +const char *paste_buffer_data(struct paste_buffer *, size_t *); +struct paste_buffer *paste_walk(struct paste_buffer *); +struct paste_buffer *paste_get_top(const char **); +struct paste_buffer *paste_get_name(const char *); +void paste_free(struct paste_buffer *); +void paste_add(char *, size_t); +int paste_rename(const char *, const char *, char **); +int paste_set(char *, size_t, const char *, char **); +char *paste_make_sample(struct paste_buffer *); + /* format.c */ -int format_cmp(struct format_entry *, struct format_entry *); -RB_PROTOTYPE(format_tree, format_entry, entry, format_cmp); -struct format_tree *format_create(void); +#define FORMAT_STATUS 0x1 +#define FORMAT_FORCE 0x2 +struct format_tree; +struct format_tree *format_create(struct cmd_q *, int); void format_free(struct format_tree *); -void printflike3 format_add( - struct format_tree *, const char *, const char *, ...); -const char *format_find(struct format_tree *, const char *); +void printflike(3, 4) format_add(struct format_tree *, const char *, + const char *, ...); +char *format_expand_time(struct format_tree *, const char *, time_t); char *format_expand(struct format_tree *, const char *); -void format_session(struct format_tree *, struct session *); -void format_client(struct format_tree *, struct client *); -void format_winlink( - struct format_tree *, struct session *, struct winlink *); -void format_window_pane(struct format_tree *, struct window_pane *); -void format_paste_buffer(struct format_tree *, struct paste_buffer *); +void format_defaults(struct format_tree *, struct client *, + struct session *, struct winlink *, struct window_pane *); +void format_defaults_window(struct format_tree *, struct window *); +void format_defaults_pane(struct format_tree *, + struct window_pane *); +void format_defaults_paste_buffer(struct format_tree *, + struct paste_buffer *); + +/* hooks.c */ +struct hook; +struct hooks *hooks_get(struct session *); +struct hooks *hooks_create(struct hooks *); +void hooks_free(struct hooks *); +struct hook *hooks_first(struct hooks *); +struct hook *hooks_next(struct hook *); +void hooks_add(struct hooks *, const char *, struct cmd_list *); +void hooks_copy(struct hooks *, struct hooks *); +void hooks_remove(struct hooks *, const char *); +struct hook *hooks_find(struct hooks *, const char *); +int printflike(4, 5) hooks_run(struct hooks *, struct client *, + struct cmd_find_state *, const char *, ...); +int printflike(4, 5) hooks_wait(struct hooks *, struct cmd_q *, + struct cmd_find_state *, const char *, ...); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -1573,7 +1620,8 @@ enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *, const struct mode_key_table *mode_key_findtable(const char *); void mode_key_init_trees(void); void mode_key_init(struct mode_key_data *, struct mode_key_tree *); -enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int, const char **); +enum mode_key_cmd mode_key_lookup(struct mode_key_data *, key_code, + const char **); /* notify.c */ void notify_enable(void); @@ -1589,55 +1637,58 @@ void notify_session_created(struct session *); void notify_session_closed(struct session *); /* options.c */ -int options_cmp(struct options_entry *, struct options_entry *); -RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); -void options_init(struct options *, struct options *); +struct options *options_create(struct options *); void options_free(struct options *); +struct options_entry *options_first(struct options *); +struct options_entry *options_next(struct options_entry *); struct options_entry *options_find1(struct options *, const char *); struct options_entry *options_find(struct options *, const char *); void options_remove(struct options *, const char *); -struct options_entry *printflike3 options_set_string( - struct options *, const char *, const char *, ...); +struct options_entry *printflike(3, 4) options_set_string(struct options *, + const char *, const char *, ...); char *options_get_string(struct options *, const char *); -struct options_entry *options_set_number( - struct options *, const char *, long long); +struct options_entry *options_set_number(struct options *, const char *, + long long); long long options_get_number(struct options *, const char *); +struct options_entry *options_set_style(struct options *, const char *, + const char *, int); +struct grid_cell *options_get_style(struct options *, const char *); /* options-table.c */ -extern const struct options_table_entry server_options_table[]; -extern const struct options_table_entry session_options_table[]; -extern const struct options_table_entry window_options_table[]; -void options_table_populate_tree(const struct options_table_entry *, - struct options *); +extern const struct options_table_entry options_table[]; +void options_table_populate_tree(enum options_table_scope, struct options *); const char *options_table_print_entry(const struct options_table_entry *, struct options_entry *, int); -int options_table_find(const char *, const struct options_table_entry **, - const struct options_table_entry **); +int options_table_find(const char *, const struct options_table_entry **); /* job.c */ extern struct joblist all_jobs; -struct job *job_run(const char *, struct session *, +struct job *job_run(const char *, struct session *, const char *, void (*)(struct job *), void (*)(void *), void *); void job_free(struct job *); void job_died(struct job *, int); /* environ.c */ -int environ_cmp(struct environ_entry *, struct environ_entry *); -RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); -void environ_init(struct environ *); +struct environ *environ_create(void); void environ_free(struct environ *); +struct environ_entry *environ_first(struct environ *); +struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); -void environ_set(struct environ *, const char *, const char *); +void printflike(3, 4) environ_set(struct environ *, const char *, const char *, + ...); +void environ_clear(struct environ *, const char *); void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); void environ_update(const char *, struct environ *, struct environ *); void environ_push(struct environ *); /* tty.c */ +void tty_create_log(void); void tty_init_termios(int, struct termios *, struct bufferevent *); void tty_raw(struct tty *, const char *); -void tty_attributes(struct tty *, const struct grid_cell *); +void tty_attributes(struct tty *, const struct grid_cell *, + const struct window_pane *); void tty_reset(struct tty *); void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); void tty_region(struct tty *, u_int, u_int); @@ -1652,21 +1703,24 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); -void tty_init(struct tty *, struct client *, int, char *); +int tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); -void tty_set_class(struct tty *, u_int); void tty_start_tty(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_force_cursor_colour(struct tty *, const char *); -void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int); -int tty_open(struct tty *, const char *, char **); +void tty_draw_pane(struct tty *, const struct window_pane *, u_int, u_int, + u_int); +void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, + u_int, u_int, u_int); +int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_write( - void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); +void tty_write(void (*)(struct tty *, const struct tty_ctx *), + struct tty_ctx *); +int tty_client_ready(struct client *, struct window_pane *wp); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_cell(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofline(struct tty *, const struct tty_ctx *); @@ -1686,185 +1740,98 @@ void tty_cmd_utf8character(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); -void tty_bell(struct tty *); /* tty-term.c */ extern struct tty_terms tty_terms; -extern const struct tty_term_code_entry tty_term_codes[NTTYCODE]; -struct tty_term *tty_term_find(char *, int, const char *, char **); +u_int tty_term_ncodes(void); +struct tty_term *tty_term_find(char *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); -const char *tty_term_string2( - struct tty_term *, enum tty_code_code, int, int); -const char *tty_term_ptr1( - struct tty_term *, enum tty_code_code, const void *); +const char *tty_term_string2(struct tty_term *, enum tty_code_code, int, + int); +const char *tty_term_ptr1(struct tty_term *, enum tty_code_code, + const void *); const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const void *, const void *); int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code); +const char *tty_term_describe(struct tty_term *, enum tty_code_code); /* tty-acs.c */ const char *tty_acs_get(struct tty *, u_char); /* tty-keys.c */ -void tty_keys_build(struct tty *); -void tty_keys_free(struct tty *); -int tty_keys_next(struct tty *); - -/* paste.c */ -struct paste_buffer *paste_walk_stack(struct paste_stack *, u_int *); -struct paste_buffer *paste_get_top(struct paste_stack *); -struct paste_buffer *paste_get_index(struct paste_stack *, u_int); -int paste_free_top(struct paste_stack *); -int paste_free_index(struct paste_stack *, u_int); -void paste_add(struct paste_stack *, char *, size_t, u_int); -int paste_replace(struct paste_stack *, u_int, char *, size_t); -char *paste_print(struct paste_buffer *, size_t); -void paste_send_pane(struct paste_buffer *, struct window_pane *, - const char *, int); - -/* clock.c */ -extern const char clock_table[14][5][5]; -void clock_draw(struct screen_write_ctx *, int, int); +void tty_keys_build(struct tty *); +void tty_keys_free(struct tty *); +key_code tty_keys_next(struct tty *); /* arguments.c */ +int args_cmp(struct args_entry *, struct args_entry *); +RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); struct args *args_create(int, ...); struct args *args_parse(const char *, int, char **); void args_free(struct args *); -size_t args_print(struct args *, char *, size_t); +char *args_print(struct args *); int args_has(struct args *, u_char); void args_set(struct args *, u_char, const char *); const char *args_get(struct args *, u_char); -long long args_strtonum( - struct args *, u_char, long long, long long, char **); +long long args_strtonum(struct args *, u_char, long long, long long, + char **); + +/* cmd-find.c */ +int cmd_find_target(struct cmd_find_state *, struct cmd_q *, + const char *, enum cmd_find_type, int); +struct client *cmd_find_client(struct cmd_q *, const char *, int); +void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, + int); +int cmd_find_valid_state(struct cmd_find_state *); +void cmd_find_copy_state(struct cmd_find_state *, + struct cmd_find_state *); +void cmd_find_log_state(const char *, struct cmd_find_state *); +int cmd_find_from_session(struct cmd_find_state *, + struct session *); +int cmd_find_from_window(struct cmd_find_state *, struct window *); +int cmd_find_from_pane(struct cmd_find_state *, + struct window_pane *); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); -char **cmd_copy_argv(int, char *const *); +char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); +char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); -size_t cmd_print(struct cmd *, char *, size_t); -struct session *cmd_current_session(struct cmd_q *, int); -struct client *cmd_current_client(struct cmd_q *); -struct client *cmd_find_client(struct cmd_q *, const char *, int); -struct session *cmd_find_session(struct cmd_q *, const char *, int); -struct winlink *cmd_find_window(struct cmd_q *, const char *, - struct session **); -int cmd_find_index(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, - struct window_pane **); +int cmd_prepare_state(struct cmd *, struct cmd_q *); +char *cmd_print(struct cmd *); +int cmd_mouse_at(struct window_pane *, struct mouse_event *, + u_int *, u_int *, int); +struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); +struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, + struct winlink **); char *cmd_template_replace(const char *, const char *, int); -const char *cmd_get_default_path(struct cmd_q *, const char *); extern const struct cmd_entry *cmd_table[]; -extern const struct cmd_entry cmd_attach_session_entry; -extern const struct cmd_entry cmd_bind_key_entry; -extern const struct cmd_entry cmd_break_pane_entry; -extern const struct cmd_entry cmd_capture_pane_entry; -extern const struct cmd_entry cmd_choose_buffer_entry; -extern const struct cmd_entry cmd_choose_client_entry; -extern const struct cmd_entry cmd_choose_list_entry; -extern const struct cmd_entry cmd_choose_session_entry; -extern const struct cmd_entry cmd_choose_tree_entry; -extern const struct cmd_entry cmd_choose_window_entry; -extern const struct cmd_entry cmd_clear_history_entry; -extern const struct cmd_entry cmd_clock_mode_entry; -extern const struct cmd_entry cmd_command_prompt_entry; -extern const struct cmd_entry cmd_confirm_before_entry; -extern const struct cmd_entry cmd_copy_mode_entry; -extern const struct cmd_entry cmd_delete_buffer_entry; -extern const struct cmd_entry cmd_detach_client_entry; -extern const struct cmd_entry cmd_display_message_entry; -extern const struct cmd_entry cmd_display_panes_entry; -extern const struct cmd_entry cmd_down_pane_entry; -extern const struct cmd_entry cmd_find_window_entry; -extern const struct cmd_entry cmd_has_session_entry; -extern const struct cmd_entry cmd_if_shell_entry; -extern const struct cmd_entry cmd_join_pane_entry; -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_session_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_link_window_entry; -extern const struct cmd_entry cmd_list_buffers_entry; -extern const struct cmd_entry cmd_list_clients_entry; -extern const struct cmd_entry cmd_list_commands_entry; -extern const struct cmd_entry cmd_list_keys_entry; -extern const struct cmd_entry cmd_list_panes_entry; -extern const struct cmd_entry cmd_list_sessions_entry; -extern const struct cmd_entry cmd_list_windows_entry; -extern const struct cmd_entry cmd_load_buffer_entry; -extern const struct cmd_entry cmd_lock_client_entry; -extern const struct cmd_entry cmd_lock_server_entry; -extern const struct cmd_entry cmd_lock_session_entry; -extern const struct cmd_entry cmd_move_pane_entry; -extern const struct cmd_entry cmd_move_window_entry; -extern const struct cmd_entry cmd_new_session_entry; -extern const struct cmd_entry cmd_new_window_entry; -extern const struct cmd_entry cmd_next_layout_entry; -extern const struct cmd_entry cmd_next_window_entry; -extern const struct cmd_entry cmd_paste_buffer_entry; -extern const struct cmd_entry cmd_pipe_pane_entry; -extern const struct cmd_entry cmd_previous_layout_entry; -extern const struct cmd_entry cmd_previous_window_entry; -extern const struct cmd_entry cmd_refresh_client_entry; -extern const struct cmd_entry cmd_rename_session_entry; -extern const struct cmd_entry cmd_rename_window_entry; -extern const struct cmd_entry cmd_resize_pane_entry; -extern const struct cmd_entry cmd_respawn_pane_entry; -extern const struct cmd_entry cmd_respawn_window_entry; -extern const struct cmd_entry cmd_rotate_window_entry; -extern const struct cmd_entry cmd_run_shell_entry; -extern const struct cmd_entry cmd_save_buffer_entry; -extern const struct cmd_entry cmd_select_layout_entry; -extern const struct cmd_entry cmd_select_pane_entry; -extern const struct cmd_entry cmd_select_window_entry; -extern const struct cmd_entry cmd_send_keys_entry; -extern const struct cmd_entry cmd_send_prefix_entry; -extern const struct cmd_entry cmd_server_info_entry; -extern const struct cmd_entry cmd_set_buffer_entry; -extern const struct cmd_entry cmd_set_environment_entry; -extern const struct cmd_entry cmd_set_option_entry; -extern const struct cmd_entry cmd_set_window_option_entry; -extern const struct cmd_entry cmd_show_buffer_entry; -extern const struct cmd_entry cmd_show_environment_entry; -extern const struct cmd_entry cmd_show_messages_entry; -extern const struct cmd_entry cmd_show_options_entry; -extern const struct cmd_entry cmd_show_window_options_entry; -extern const struct cmd_entry cmd_source_file_entry; -extern const struct cmd_entry cmd_split_window_entry; -extern const struct cmd_entry cmd_start_server_entry; -extern const struct cmd_entry cmd_suspend_client_entry; -extern const struct cmd_entry cmd_swap_pane_entry; -extern const struct cmd_entry cmd_swap_window_entry; -extern const struct cmd_entry cmd_switch_client_entry; -extern const struct cmd_entry cmd_unbind_key_entry; -extern const struct cmd_entry cmd_unlink_window_entry; -extern const struct cmd_entry cmd_up_pane_entry; -extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ -enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int); +enum cmd_retval cmd_attach_session(struct cmd_q *, int, int, const char *, + int); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); void cmd_list_free(struct cmd_list *); -size_t cmd_list_print(struct cmd_list *, char *, size_t); +char *cmd_list_print(struct cmd_list *); /* cmd-queue.c */ struct cmd_q *cmdq_new(struct client *); int cmdq_free(struct cmd_q *); -void printflike2 cmdq_print(struct cmd_q *, const char *, ...); -void printflike2 cmdq_info(struct cmd_q *, const char *, ...); -void printflike2 cmdq_error(struct cmd_q *, const char *, ...); -int cmdq_guard(struct cmd_q *, const char *); -void cmdq_run(struct cmd_q *, struct cmd_list *); -void cmdq_append(struct cmd_q *, struct cmd_list *); +void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...); +void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...); +void cmdq_guard(struct cmd_q *, const char *, int); +void cmdq_run(struct cmd_q *, struct cmd_list *, + struct mouse_event *); +void cmdq_append(struct cmd_q *, struct cmd_list *, + struct mouse_event *); int cmdq_continue(struct cmd_q *); void cmdq_flush(struct cmd_q *); @@ -1872,51 +1839,69 @@ void cmdq_flush(struct cmd_q *); int cmd_string_parse(const char *, struct cmd_list **, const char *, u_int, char **); +/* cmd-wait-for.c */ +#ifdef TMATE +void signal_waiting_clients(const char *name); +#endif +void cmd_wait_for_flush(void); + /* client.c */ -int client_main(int, char **, int); +int client_main(struct event_base *, int, char **, int, const char *); /* key-bindings.c */ -extern struct key_bindings key_bindings; -int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); -struct key_binding *key_bindings_lookup(int); -void key_bindings_add(int, int, struct cmd_list *); -void key_bindings_remove(int); -void key_bindings_clean(void); +RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); +extern struct key_tables key_tables; +int key_table_cmp(struct key_table *, struct key_table *); +int key_bindings_cmp(struct key_binding *, struct key_binding *); +struct key_table *key_bindings_get_table(const char *, int); +void key_bindings_unref_table(struct key_table *); +void key_bindings_add(const char *, key_code, int, struct cmd_list *); +void key_bindings_remove(const char *, key_code); +void key_bindings_remove_table(const char *); void key_bindings_init(void); -void key_bindings_dispatch(struct key_binding *, struct client *); +void key_bindings_dispatch(struct key_binding *, struct client *, + struct mouse_event *); /* key-string.c */ -int key_string_lookup_string(const char *); -const char *key_string_lookup_key(int); +key_code key_string_lookup_string(const char *); +const char *key_string_lookup_key(key_code); + +/* alerts.c */ +void alerts_reset_all(void); +void alerts_queue(struct window *, int); +void alerts_check_session(struct session *); /* server.c */ +extern struct tmuxproc *server_proc; extern struct clients clients; -extern struct clients dead_clients; -extern struct paste_stack global_buffers; -int server_start(int, char *); +extern struct cmd_find_state marked_pane; +void server_set_marked(struct session *, struct winlink *, + struct window_pane *); +void server_clear_marked(void); +int server_is_marked(struct session *, struct winlink *, + struct window_pane *); +int server_check_marked(void); +int server_start(struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); /* server-client.c */ -void server_client_handle_key(struct client *, int); +void server_client_set_key_table(struct client *, const char *); +const char *server_client_get_key_table(struct client *); +int server_client_check_nested(struct client *); +void server_client_handle_key(struct client *, key_code); void server_client_create(int); -int server_client_open(struct client *, struct session *, char **); +int server_client_open(struct client *, char **); +void server_client_unref(struct client *); void server_client_lost(struct client *); -void server_client_callback(int, short, void *); -void server_client_status_timer(void); +void server_client_detach(struct client *, enum msgtype); void server_client_loop(void); - -/* server-window.c */ -void server_window_loop(void); +void server_client_push_stdout(struct client *); +void server_client_push_stderr(struct client *); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); -void server_write_ready(struct client *); -int server_write_client( - struct client *, enum msgtype, const void *, size_t); -void server_write_session( - struct session *, enum msgtype, const void *, size_t); void server_redraw_client(struct client *); void server_status_client(struct client *); void server_redraw_session(struct session *); @@ -1929,43 +1914,37 @@ void server_status_window(struct window *); void server_lock(void); void server_lock_session(struct session *); void server_lock_client(struct client *); -int server_unlock(const char *); void server_kill_window(struct window *); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); void server_unlink_window(struct session *, struct winlink *); -void server_destroy_pane(struct window_pane *); +void server_destroy_pane(struct window_pane *, int); void server_destroy_session_group(struct session *); void server_destroy_session(struct session *); void server_check_unattached(void); void server_set_identify(struct client *); void server_clear_identify(struct client *); -void server_update_event(struct client *); -void server_push_stdout(struct client *); -void server_push_stderr(struct client *); int server_set_stdin_callback(struct client *, void (*)(struct client *, int, void *), void *, char **); void server_unzoom_window(struct window *); /* status.c */ -int status_out_cmp(struct status_out *, struct status_out *); -RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp); +void status_timer_start(struct client *); +void status_timer_start_all(void); int status_at_line(struct client *); -void status_free_jobs(struct status_out_tree *); -void status_update_jobs(struct client *); -void status_set_window_at(struct client *, u_int); +struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); -char *status_replace(struct client *, struct session *, - struct winlink *, struct window_pane *, const char *, time_t, int); -void printflike2 status_message_set(struct client *, const char *, ...); +void printflike(2, 3) status_message_set(struct client *, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, const char *, const char *, int (*)(void *, const char *), void (*)(void *), void *, int); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); -void status_prompt_key(struct client *, int); +void status_prompt_key(struct client *, key_code); void status_prompt_update(struct client *, const char *, const char *); +void status_prompt_load_history(void); +void status_prompt_save_history(void); /* resize.c */ void recalculate_sizes(void); @@ -1973,24 +1952,24 @@ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); +void input_reset(struct window_pane *, int); +struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ -void input_key(struct window_pane *, int); -void input_mouse(struct window_pane *, struct session *, - struct mouse_event *); +void input_key(struct window_pane *, key_code, struct mouse_event *); /* xterm-keys.c */ -char *xterm_keys_lookup(int); -int xterm_keys_find(const char *, size_t, size_t *, int *); +char *xterm_keys_lookup(key_code); +int xterm_keys_find(const char *, size_t, size_t *, key_code *); /* colour.c */ +int colour_find_rgb(u_char, u_char, u_char); void colour_set_fg(struct grid_cell *, int); void colour_set_bg(struct grid_cell *, int); const char *colour_tostring(int); int colour_fromstring(const char *); u_char colour_256to16(u_char); -u_char colour_256to88(u_char); /* attributes.c */ const char *attributes_tostring(u_char); @@ -1998,17 +1977,16 @@ int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; -extern const struct grid_cell grid_marker_cell; struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *); void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); +void grid_clear_history(struct grid *); void grid_expand_line(struct grid *, u_int, u_int); -const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int); -struct grid_cell *grid_get_cell(struct grid *, u_int, u_int); +void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int); @@ -2016,21 +1994,14 @@ void grid_move_lines(struct grid *, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int, struct grid_cell **, int, int, int); -void grid_duplicate_lines( - struct grid *, u_int, struct grid *, u_int, u_int); +void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, + u_int); u_int grid_reflow(struct grid *, struct grid *, u_int); -/* grid-cell.c */ -u_int grid_cell_width(const struct grid_cell *); -void grid_cell_get(const struct grid_cell *, struct utf8_data *); -void grid_cell_set(struct grid_cell *, const struct utf8_data *); -void grid_cell_one(struct grid_cell *, u_char); - /* grid-view.c */ -const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int); -struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int); -void grid_view_set_cell( - struct grid *, u_int, u_int, const struct grid_cell *); +void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); +void grid_view_set_cell(struct grid *, u_int, u_int, + const struct grid_cell *); void grid_view_clear_history(struct grid *); void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_view_scroll_region_up(struct grid *, u_int, u_int); @@ -2044,26 +2015,24 @@ void grid_view_delete_cells(struct grid *, u_int, u_int, u_int); char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ -void screen_write_start( - struct screen_write_ctx *, struct window_pane *, struct screen *); +void screen_write_start(struct screen_write_ctx *, struct window_pane *, + struct screen *); void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); -size_t printflike2 screen_write_cstrlen(int, const char *, ...); -void printflike5 screen_write_cnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, ...); -size_t printflike2 screen_write_strlen(int, const char *, ...); -void printflike3 screen_write_puts(struct screen_write_ctx *, +size_t printflike(1, 2) screen_write_cstrlen(const char *, ...); +void printflike(4, 5) screen_write_cnputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, ...); +size_t printflike(1, 2) screen_write_strlen(const char *, ...); +void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, struct grid_cell *, const char *, ...); -void printflike5 screen_write_nputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, ...); -void screen_write_vnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, va_list); -void screen_write_parsestyle( - struct grid_cell *, struct grid_cell *, const char *); -void screen_write_putc( - struct screen_write_ctx *, struct grid_cell *, u_char); -void screen_write_copy(struct screen_write_ctx *, - struct screen *, u_int, u_int, u_int, u_int); +void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, ...); +void screen_write_vnputs(struct screen_write_ctx *, ssize_t, + struct grid_cell *, const char *, va_list); +void screen_write_putc(struct screen_write_ctx *, struct grid_cell *, + u_char); +void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, + u_int, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); void screen_write_mode_set(struct screen_write_ctx *, int); void screen_write_mode_clear(struct screen_write_ctx *, int); @@ -2084,7 +2053,6 @@ void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); void screen_write_reverseindex(struct screen_write_ctx *); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_linefeed(struct screen_write_ctx *, int); -void screen_write_linefeedscreen(struct screen_write_ctx *, int); void screen_write_carriagereturn(struct screen_write_ctx *); void screen_write_clearendofscreen(struct screen_write_ctx *); void screen_write_clearstartofscreen(struct screen_write_ctx *); @@ -2095,7 +2063,7 @@ void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); /* screen-redraw.c */ -void screen_redraw_screen(struct client *, int, int); +void screen_redraw_screen(struct client *, int, int, int); void screen_redraw_pane(struct client *, struct window_pane *); /* screen.c */ @@ -2116,6 +2084,8 @@ void screen_reflow(struct screen *, u_int); /* window.c */ extern struct windows windows; extern struct window_pane_tree all_window_panes; +int window_cmp(struct window *, struct window *); +RB_PROTOTYPE(windows, window, entry, window_cmp); int winlink_cmp(struct winlink *, struct winlink *); RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp); int window_pane_cmp(struct window_pane *, struct window_pane *); @@ -2136,51 +2106,54 @@ struct winlink *winlink_previous_by_number(struct winlink *, struct session *, int); void winlink_stack_push(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *); -int window_index(struct window *, u_int *); +struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); +void window_update_activity(struct window *); struct window *window_create1(u_int, u_int); -struct window *window_create(const char *, const char *, const char *, - const char *, struct environ *, struct termios *, - u_int, u_int, u_int, char **); +struct window *window_create(const char *, int, char **, const char *, + const char *, const char *, struct environ *, + struct termios *, u_int, u_int, u_int, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); -void window_set_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); -void window_set_active_pane(struct window *, struct window_pane *); +int window_has_pane(struct window *, struct window_pane *); +int window_set_active_pane(struct window *, struct window_pane *); +void window_redraw_active_switch(struct window *, + struct window_pane *); struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); +void window_lost_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *); struct window_pane *window_pane_at_index(struct window *, u_int); struct window_pane *window_pane_next_by_number(struct window *, - struct window_pane *, u_int); + struct window_pane *, u_int); struct window_pane *window_pane_previous_by_number(struct window *, - struct window_pane *, u_int); + struct window_pane *, u_int); int window_pane_index(struct window_pane *, u_int *); u_int window_count_panes(struct window *); void window_destroy_panes(struct window *); +struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); -void window_pane_timer_start(struct window_pane *); -int window_pane_spawn(struct window_pane *, const char *, - const char *, const char *, struct environ *, +int window_pane_spawn(struct window_pane *, int, char **, + const char *, const char *, const char *, struct environ *, struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); void window_pane_alternate_off(struct window_pane *, struct grid_cell *, int); -int window_pane_set_mode( - struct window_pane *, const struct window_mode *); +int window_pane_set_mode(struct window_pane *, + const struct window_mode *); void window_pane_reset_mode(struct window_pane *); -void window_pane_key(struct window_pane *, struct session *, int); -void window_pane_mouse(struct window_pane *, - struct session *, struct mouse_event *); +void window_pane_key(struct window_pane *, struct client *, + struct session *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); -char *window_pane_search( - struct window_pane *, const char *, u_int *); +char *window_pane_search(struct window_pane *, const char *, + u_int *); char *window_printable_flags(struct session *, struct winlink *); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); @@ -2189,24 +2162,24 @@ struct window_pane *window_pane_find_right(struct window_pane *); void window_set_name(struct window *, const char *); void window_remove_ref(struct window *); void winlink_clear_flags(struct winlink *); -void window_mode_attrs(struct grid_cell *, struct options *); +int winlink_shuffle_up(struct session *, struct winlink *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *); void layout_free_cell(struct layout_cell *); void layout_print_cell(struct layout_cell *, const char *, u_int); -void layout_destroy_cell(struct layout_cell *, struct layout_cell **); -void layout_set_size( - struct layout_cell *, u_int, u_int, u_int, u_int); -void layout_make_leaf( - struct layout_cell *, struct window_pane *); +void layout_destroy_cell(struct layout_cell *, + struct layout_cell **); +void layout_set_size(struct layout_cell *, u_int, u_int, u_int, + u_int); +void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_offsets(struct layout_cell *); void layout_fix_panes(struct window *, u_int, u_int); u_int layout_resize_check(struct layout_cell *, enum layout_type); -void layout_resize_adjust( - struct layout_cell *, enum layout_type, int); +void layout_resize_adjust(struct layout_cell *, enum layout_type, + int); void layout_init(struct window *, struct window_pane *); void layout_free(struct window *); void layout_resize(struct window *, u_int, u_int); @@ -2214,115 +2187,34 @@ void layout_resize_pane(struct window_pane *, enum layout_type, int); void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); -void layout_resize_pane_mouse(struct client *); void layout_assign_pane(struct layout_cell *, struct window_pane *); -struct layout_cell *layout_split_pane( - struct window_pane *, enum layout_type, int, int); +struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, + int, int); void layout_close_pane(struct window_pane *); /* layout-custom.c */ -char *layout_dump(struct window *); +char *layout_dump(struct layout_cell *); int layout_parse(struct window *, const char *); /* layout-set.c */ -const char *layout_set_name(u_int); int layout_set_lookup(const char *); u_int layout_set_select(struct window *, u_int); u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); -void layout_set_active_changed(struct window *); /* window-clock.c */ extern const struct window_mode window_clock_mode; +extern const char window_clock_table[14][5][5]; /* window-copy.c */ -enum window_copy_input_type { - WINDOW_COPY_OFF, - WINDOW_COPY_NUMERICPREFIX, - WINDOW_COPY_SEARCHUP, - WINDOW_COPY_SEARCHDOWN, - WINDOW_COPY_JUMPFORWARD, - WINDOW_COPY_JUMPBACK, - WINDOW_COPY_JUMPTOFORWARD, - WINDOW_COPY_JUMPTOBACK, - WINDOW_COPY_GOTOLINE, - -#ifdef TMATE - WINDOW_COPY_PASSWORD, -#endif -}; - -/* - * Copy-mode's visible screen (the "screen" field) is filled from one of - * two sources: the original contents of the pane (used when we - * actually enter via the "copy-mode" command, to copy the contents of - * the current pane), or else a series of lines containing the output - * from an output-writing tmux command (such as any of the "show-*" or - * "list-*" commands). - * - * In either case, the full content of the copy-mode grid is pointed at - * by the "backing" field, and is copied into "screen" as needed (that - * is, when scrolling occurs). When copy-mode is backed by a pane, - * backing points directly at that pane's screen structure (&wp->base); - * when backed by a list of output-lines from a command, it points at - * a newly-allocated screen structure (which is deallocated when the - * mode ends). - */ - -#ifdef TMATE -typedef void (*copy_password_callback)(const char *password, void *private); -#endif - -struct window_copy_mode_data { - struct screen screen; - - struct screen *backing; - int backing_written; /* backing display has started */ - - struct mode_key_data mdata; - - u_int oy; - - u_int selx; - u_int sely; - - u_int rectflag; /* are we in rectangle copy mode? */ - - u_int cx; - u_int cy; - - u_int lastcx; /* position in last line with content */ - u_int lastsx; /* size of last line with content */ - - enum window_copy_input_type inputtype; - const char *inputprompt; - char *inputstr; - - int numprefix; - - enum window_copy_input_type searchtype; - char *searchstr; - - enum window_copy_input_type jumptype; - char jumpchar; - -#ifdef TMATE - copy_password_callback password_cb; - void *password_cb_private; -#endif -}; - - - extern const struct window_mode window_copy_mode; -void window_copy_init_from_pane(struct window_pane *); +void window_copy_init_from_pane(struct window_pane *, int); void window_copy_init_for_output(struct window_pane *); -void printflike2 window_copy_add(struct window_pane *, const char *, ...); +void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *); - -int window_copy_update_selection(struct window_pane *); -void window_copy_redraw_screen(struct window_pane *); +void window_copy_start_drag(struct client *, struct mouse_event *); +int window_copy_scroll_position(struct window_pane *); /* window-choose.c */ extern const struct window_mode window_choose_mode; @@ -2340,22 +2232,23 @@ struct window_choose_data *window_choose_add_window(struct window_pane *, struct window_choose_data *window_choose_add_session(struct window_pane *, struct client *, struct session *, const char *, const char *, u_int); -struct window_choose_data *window_choose_add_item(struct window_pane *, - struct client *, struct winlink *, const char *, - const char *, u_int); void window_choose_expand_all(struct window_pane *); +void window_choose_collapse_all(struct window_pane *); +void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void queue_window_name(struct window *); -char *default_window_name(struct window *); +void check_window_name(struct window *); +char *default_window_name(struct window *); +char *format_window_name(struct window *); +char *parse_window_name(const char *); /* signal.c */ -void set_signals(void(*)(int, short, void *)); +void set_signals(void(*)(int, short, void *), void *); void clear_signals(int); /* control.c */ -void control_callback(struct client *, int, void*); -void printflike2 control_write(struct client *, const char *, ...); +void control_callback(struct client *, int, void *); +void printflike(2, 3) control_write(struct client *, const char *, ...); void control_write_buffer(struct client *, struct evbuffer *); /* control-notify.c */ @@ -2372,27 +2265,29 @@ void control_notify_session_close(struct session *); /* session.c */ extern struct sessions sessions; -extern struct sessions dead_sessions; 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_by_id_str(const char *); struct session *session_find_by_id(u_int); -struct session *session_create(const char *, const char *, const char *, - struct environ *, struct termios *, int, u_int, u_int, - char **); +struct session *session_create(const char *, int, char **, const char *, + const char *, struct environ *, struct termios *, int, + u_int, u_int, char **); void session_destroy(struct session *); +void session_unref(struct session *); int session_check_name(const char *); -void session_update_activity(struct session *); +void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); -struct winlink *session_new(struct session *, - const char *, const char *, const char *, int, char **); -struct winlink *session_attach( - struct session *, struct window *, int, char **); +struct winlink *session_new(struct session *, const char *, int, char **, + const char *, const char *, int, char **); +struct winlink *session_attach(struct session *, struct window *, int, + char **); int session_detach(struct session *, struct winlink *); -struct winlink* session_has(struct session *, struct window *); +int session_has(struct session *, struct window *); +int session_is_linked(struct session *, struct window *); int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); @@ -2402,17 +2297,28 @@ struct session_group *session_group_find(struct session *); u_int session_group_index(struct session_group *); void session_group_add(struct session *, struct session *); void session_group_remove(struct session *); +u_int session_group_count(struct session_group *); void session_group_synchronize_to(struct session *); void session_group_synchronize_from(struct session *); void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ -void utf8_build(void); -int utf8_open(struct utf8_data *, u_char); -int utf8_append(struct utf8_data *, u_char); -u_int utf8_combine(const struct utf8_data *); -u_int utf8_split2(u_int, u_char *); +u_int utf8_width(u_int); +void utf8_set(struct utf8_data *, u_char); +void utf8_copy(struct utf8_data *, const struct utf8_data *); +enum utf8_state utf8_open(struct utf8_data *, u_char); +enum utf8_state utf8_append(struct utf8_data *, u_char); +u_int utf8_combine(const struct utf8_data *); +enum utf8_state utf8_split(u_int, struct utf8_data *); +u_int utf8_split2(u_int, u_char *); +int utf8_strvis(char *, const char *, size_t, int); +char *utf8_sanitize(const char *); +struct utf8_data *utf8_fromcstr(const char *); +char *utf8_tocstr(struct utf8_data *); +u_int utf8_cstrwidth(const char *); +char *utf8_trimcstr(const char *, u_int); +char *utf8_padcstr(const char *, u_int); /* osdep-*.c */ char *osdep_get_name(int, char *); @@ -2420,24 +2326,26 @@ char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); /* log.c */ -void log_open(int, const char *); -void log_close(void); -void printflike1 log_warn(const char *, ...); -void printflike1 log_warnx(const char *, ...); -void printflike1 log_info(const char *, ...); -void printflike1 log_debug(const char *, ...); -void printflike1 log_debug2(const char *, ...); -__dead void printflike1 log_fatal(const char *, ...); -__dead void printflike1 log_fatalx(const char *, ...); +void log_add_level(void); +int log_get_level(void); +void log_open(const char *); +void log_close(void); +void printflike(1, 2) log_debug(const char *, ...); +__dead void printflike(1, 2) fatal(const char *, ...); +__dead void printflike(1, 2) fatalx(const char *, ...); -/* xmalloc.c */ -char *xstrdup(const char *); -void *xcalloc(size_t, size_t); -void *xmalloc(size_t); -void *xrealloc(void *, size_t, size_t); -int printflike2 xasprintf(char **, const char *, ...); -int xvasprintf(char **, const char *, va_list); -int printflike3 xsnprintf(char *, size_t, const char *, ...); -int xvsnprintf(char *, size_t, const char *, va_list); +/* style.c */ +int style_parse(const struct grid_cell *, + struct grid_cell *, const char *); +const char *style_tostring(struct grid_cell *); +void style_update_new(struct options *, const char *, const char *); +void style_update_old(struct options *, const char *, + struct grid_cell *); +void style_apply(struct grid_cell *, struct options *, + const char *); +void style_apply_update(struct grid_cell *, struct options *, + const char *); +int style_equal(const struct grid_cell *, + const struct grid_cell *); #endif /* TMUX_H */ diff --git a/tools/check-compat.sh b/tools/check-compat.sh deleted file mode 100644 index b7603a80..00000000 --- a/tools/check-compat.sh +++ /dev/null @@ -1,5 +0,0 @@ -# $Id$ - -grep "#include" compat.h|while read line; do - grep "$line" *.[ch] compat/*.[ch] -done diff --git a/tools/cmp-cvs.sh b/tools/cmp-cvs.sh index 104ded6b..5429d769 100644 --- a/tools/cmp-cvs.sh +++ b/tools/cmp-cvs.sh @@ -1,4 +1,4 @@ -# $Id$ +#!/bin/ksh rm diff.out touch diff.out diff --git a/tools/fix-ids.sh b/tools/fix-ids.sh deleted file mode 100644 index 4621d2b4..00000000 --- a/tools/fix-ids.sh +++ /dev/null @@ -1,8 +0,0 @@ -# $Id$ - -for i in *.[ch] tmux.1; do - (head -1 $i|grep '$OpenBSD' >/dev/null) || continue - mv $i $i~ || exit - sed 's/\$OpenBSD.* \$/$\Id$/' $i~ >$i || exit - echo $i -done diff --git a/tools/fuzz.c b/tools/fuzz.c deleted file mode 100644 index 39a2a4db..00000000 --- a/tools/fuzz.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -#include -#include -#include -#include - -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); */ - } -} diff --git a/tools/putty-utf8.sh b/tools/putty-utf8.sh deleted file mode 100644 index 73cb5fb1..00000000 --- a/tools/putty-utf8.sh +++ /dev/null @@ -1 +0,0 @@ -echo -ne \\033%G\\033[?47h\\033%G\\033[?47l diff --git a/tty-acs.c b/tty-acs.c index e85888c3..5d03c3eb 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott @@ -30,38 +30,38 @@ struct tty_acs_entry { 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" } + { '+', "\342\206\222" }, /* arrow pointing right */ + { ',', "\342\206\220" }, /* arrow pointing left */ + { '-', "\342\206\221" }, /* arrow pointing up */ + { '.', "\342\206\223" }, /* arrow pointing down */ + { '0', "\342\226\256" }, /* solid square block */ + { '`', "\342\227\206" }, /* diamond */ + { 'a', "\342\226\222" }, /* checker board (stipple) */ + { 'f', "\302\260" }, /* degree symbol */ + { 'g', "\302\261" }, /* plus/minus */ + { 'h', "\342\226\222" }, /* board of squares */ + { 'i', "\342\230\203" }, /* lantern symbol */ + { 'j', "\342\224\230" }, /* lower right corner */ + { 'k', "\342\224\220" }, /* upper right corner */ + { 'l', "\342\224\214" }, /* upper left corner */ + { 'm', "\342\224\224" }, /* lower left corner */ + { 'n', "\342\224\274" }, /* large plus or crossover */ + { 'o', "\342\216\272" }, /* scan line 1 */ + { 'p', "\342\216\273" }, /* scan line 3 */ + { 'q', "\342\224\200" }, /* horizontal line */ + { 'r', "\342\216\274" }, /* scan line 7 */ + { 's', "\342\216\275" }, /* scan line 9 */ + { 't', "\342\224\234" }, /* tee pointing right */ + { 'u', "\342\224\244" }, /* tee pointing left */ + { 'v', "\342\224\264" }, /* tee pointing up */ + { 'w', "\342\224\254" }, /* tee pointing down */ + { 'x', "\342\224\202" }, /* vertical line */ + { 'y', "\342\211\244" }, /* less-than-or-equal-to */ + { 'z', "\342\211\245" }, /* greater-than-or-equal-to */ + { '{', "\317\200" }, /* greek pi */ + { '|', "\342\211\240" }, /* not-equal */ + { '}', "\302\243" }, /* UK pound sign */ + { '~', "\302\267" } /* bullet */ }; int @@ -81,7 +81,7 @@ 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 != NULL && !(tty->flags & TTY_UTF8)) { if (tty->term->acs[ch][0] == '\0') return (NULL); return (&tty->term->acs[ch][0]); diff --git a/tty-keys.c b/tty-keys.c index 3055f399..86839a17 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -33,20 +33,19 @@ * into a ternary tree. */ -void tty_keys_add1(struct tty_key **, const char *, int); -void tty_keys_add(struct tty *, const char *, int); +void tty_keys_add1(struct tty_key **, const char *, key_code); +void tty_keys_add(struct tty *, const char *, key_code); void tty_keys_free1(struct tty_key *); -struct tty_key *tty_keys_find1( - struct tty_key *, const char *, size_t, size_t *); +struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, + size_t *); struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); void tty_keys_callback(int, short, void *); int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); -int tty_keys_device(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { const char *string; - int key; + key_code key; }; const struct tty_default_key_raw tty_default_raw_keys[] = { /* @@ -113,14 +112,6 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21^", KEYC_F10|KEYC_CTRL }, { "\033[23^", KEYC_F11|KEYC_CTRL }, { "\033[24^", KEYC_F12|KEYC_CTRL }, - { "\033[25^", KEYC_F13|KEYC_CTRL }, - { "\033[26^", KEYC_F14|KEYC_CTRL }, - { "\033[28^", KEYC_F15|KEYC_CTRL }, - { "\033[29^", KEYC_F16|KEYC_CTRL }, - { "\033[31^", KEYC_F17|KEYC_CTRL }, - { "\033[32^", KEYC_F18|KEYC_CTRL }, - { "\033[33^", KEYC_F19|KEYC_CTRL }, - { "\033[34^", KEYC_F20|KEYC_CTRL }, { "\033[2^", KEYC_IC|KEYC_CTRL }, { "\033[3^", KEYC_DC|KEYC_CTRL }, { "\033[7^", KEYC_HOME|KEYC_CTRL }, @@ -140,14 +131,6 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21$", KEYC_F10|KEYC_SHIFT }, { "\033[23$", KEYC_F11|KEYC_SHIFT }, { "\033[24$", KEYC_F12|KEYC_SHIFT }, - { "\033[25$", KEYC_F13|KEYC_SHIFT }, - { "\033[26$", KEYC_F14|KEYC_SHIFT }, - { "\033[28$", KEYC_F15|KEYC_SHIFT }, - { "\033[29$", KEYC_F16|KEYC_SHIFT }, - { "\033[31$", KEYC_F17|KEYC_SHIFT }, - { "\033[32$", KEYC_F18|KEYC_SHIFT }, - { "\033[33$", KEYC_F19|KEYC_SHIFT }, - { "\033[34$", KEYC_F20|KEYC_SHIFT }, { "\033[2$", KEYC_IC|KEYC_SHIFT }, { "\033[3$", KEYC_DC|KEYC_SHIFT }, { "\033[7$", KEYC_HOME|KEYC_SHIFT }, @@ -167,14 +150,6 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, - { "\033[25@", KEYC_F13|KEYC_CTRL|KEYC_SHIFT }, - { "\033[26@", KEYC_F14|KEYC_CTRL|KEYC_SHIFT }, - { "\033[28@", KEYC_F15|KEYC_CTRL|KEYC_SHIFT }, - { "\033[29@", KEYC_F16|KEYC_CTRL|KEYC_SHIFT }, - { "\033[31@", KEYC_F17|KEYC_CTRL|KEYC_SHIFT }, - { "\033[32@", KEYC_F18|KEYC_CTRL|KEYC_SHIFT }, - { "\033[33@", KEYC_F19|KEYC_CTRL|KEYC_SHIFT }, - { "\033[34@", KEYC_F20|KEYC_CTRL|KEYC_SHIFT }, { "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT }, { "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT }, { "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT }, @@ -190,7 +165,7 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { /* Default terminfo(5) keys. */ struct tty_default_key_code { enum tty_code_code code; - int key; + key_code key; }; const struct tty_default_key_code tty_default_code_keys[] = { /* Function keys. */ @@ -206,14 +181,63 @@ const struct tty_default_key_code tty_default_code_keys[] = { { TTYC_KF10, KEYC_F10 }, { TTYC_KF11, KEYC_F11 }, { TTYC_KF12, KEYC_F12 }, - { TTYC_KF13, KEYC_F13 }, - { TTYC_KF14, KEYC_F14 }, - { TTYC_KF15, KEYC_F15 }, - { TTYC_KF16, KEYC_F16 }, - { TTYC_KF17, KEYC_F17 }, - { TTYC_KF18, KEYC_F18 }, - { TTYC_KF19, KEYC_F19 }, - { TTYC_KF20, KEYC_F20 }, + + { TTYC_KF13, KEYC_F1|KEYC_SHIFT }, + { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, + { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, + { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, + { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, + { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, + { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, + { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, + { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, + { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, + { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, + { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, + + { TTYC_KF25, KEYC_F1|KEYC_CTRL }, + { TTYC_KF26, KEYC_F2|KEYC_CTRL }, + { TTYC_KF27, KEYC_F3|KEYC_CTRL }, + { TTYC_KF28, KEYC_F4|KEYC_CTRL }, + { TTYC_KF29, KEYC_F5|KEYC_CTRL }, + { TTYC_KF30, KEYC_F6|KEYC_CTRL }, + { TTYC_KF31, KEYC_F7|KEYC_CTRL }, + { TTYC_KF32, KEYC_F8|KEYC_CTRL }, + { TTYC_KF33, KEYC_F9|KEYC_CTRL }, + { TTYC_KF34, KEYC_F10|KEYC_CTRL }, + { TTYC_KF35, KEYC_F11|KEYC_CTRL }, + { TTYC_KF36, KEYC_F12|KEYC_CTRL }, + + { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, + { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, + + { TTYC_KF49, KEYC_F1|KEYC_ESCAPE }, + { TTYC_KF50, KEYC_F2|KEYC_ESCAPE }, + { TTYC_KF51, KEYC_F3|KEYC_ESCAPE }, + { TTYC_KF52, KEYC_F4|KEYC_ESCAPE }, + { TTYC_KF53, KEYC_F5|KEYC_ESCAPE }, + { TTYC_KF54, KEYC_F6|KEYC_ESCAPE }, + { TTYC_KF55, KEYC_F7|KEYC_ESCAPE }, + { TTYC_KF56, KEYC_F8|KEYC_ESCAPE }, + { TTYC_KF57, KEYC_F9|KEYC_ESCAPE }, + { TTYC_KF58, KEYC_F10|KEYC_ESCAPE }, + { TTYC_KF59, KEYC_F11|KEYC_ESCAPE }, + { TTYC_KF60, KEYC_F12|KEYC_ESCAPE }, + + { TTYC_KF61, KEYC_F1|KEYC_ESCAPE|KEYC_SHIFT }, + { TTYC_KF62, KEYC_F2|KEYC_ESCAPE|KEYC_SHIFT }, + { TTYC_KF63, KEYC_F3|KEYC_ESCAPE|KEYC_SHIFT }, + { TTYC_KICH1, KEYC_IC }, { TTYC_KDCH1, KEYC_DC }, { TTYC_KHOME, KEYC_HOME }, @@ -293,7 +317,7 @@ const struct tty_default_key_code tty_default_code_keys[] = { /* Add key to tree. */ void -tty_keys_add(struct tty *tty, const char *s, int key) +tty_keys_add(struct tty *tty, const char *s, key_code key) { struct tty_key *tk; size_t size; @@ -301,17 +325,17 @@ tty_keys_add(struct tty *tty, const char *s, int key) keystr = key_string_lookup_key(key); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { - log_debug("new key %s: 0x%x (%s)", s, key, keystr); + log_debug("new key %s: 0x%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); } else { - log_debug("replacing key %s: 0x%x (%s)", s, key, keystr); + log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr); tk->key = key; } } /* Add next node to the tree. */ void -tty_keys_add1(struct tty_key **tkp, const char *s, int key) +tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) { struct tty_key *tk; @@ -320,7 +344,7 @@ tty_keys_add1(struct tty_key **tkp, const char *s, int key) if (tk == NULL) { tk = *tkp = xcalloc(1, sizeof *tk); tk->ch = *s; - tk->key = KEYC_NONE; + tk->key = KEYC_UNKNOWN; } /* Find the next entry. */ @@ -357,7 +381,7 @@ tty_keys_build(struct tty *tty) const char *s; if (tty->key_tree != NULL) - tty_keys_free (tty); + tty_keys_free(tty); tty->key_tree = NULL; for (i = 0; i < nitems(tty_default_raw_keys); i++) { @@ -420,7 +444,7 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) (*size)++; /* At the end of the string, return the current node. */ - if (len == 0 || (tk->next == NULL && tk->key != KEYC_NONE)) + if (len == 0 || (tk->next == NULL && tk->key != KEYC_UNKNOWN)) return (tk); /* Move into the next tree for the following character. */ @@ -440,34 +464,28 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) * Process at least one key in the buffer and invoke tty->key_callback. Return * 0 if there are no further keys, or 1 if there could be more in the buffer. */ -int +key_code tty_keys_next(struct tty *tty) { - struct tty_key *tk; - struct timeval tv; - const char *buf; - size_t len, size; - cc_t bspace; - int key, delay, expired = 0; + struct tty_key *tk; + struct timeval tv; + const char *buf; + size_t len, size; + cc_t bspace; + int delay, expired = 0; + key_code key; + struct utf8_data ud; + enum utf8_state more; + u_int i; /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); len = EVBUFFER_LENGTH(tty->event->input); + if (len == 0) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); - /* Is this device attributes response? */ - switch (tty_keys_device(tty, buf, len, &size)) { - case 0: /* yes */ - key = KEYC_NONE; - goto complete_key; - case -1: /* no, or not valid */ - break; - case 1: /* partial */ - goto partial_key; - } - /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size)) { case 0: /* yes */ @@ -475,20 +493,13 @@ tty_keys_next(struct tty *tty) goto complete_key; case -1: /* no, or not valid */ break; + case -2: /* yes, but we don't care. */ + key = KEYC_MOUSE; + goto discard_key; case 1: /* partial */ goto partial_key; } - /* Try to parse a key with an xterm-style modifier. */ - switch (xterm_keys_find(buf, len, &size, &key)) { - case 0: /* found */ - goto complete_key; - case -1: /* not found */ - break; - case 1: - goto partial_key; - } - /* Look for matching key string and return if found. */ tk = tty_keys_find(tty, buf, len, &size); if (tk != NULL) { @@ -498,6 +509,16 @@ tty_keys_next(struct tty *tty) goto complete_key; } + /* Try to parse a key with an xterm-style modifier. */ + switch (xterm_keys_find(buf, len, &size, &key)) { + case 0: /* found */ + goto complete_key; + case -1: /* not found */ + break; + case 1: + goto partial_key; + } + first_key: /* Is this a meta key? */ if (len >= 2 && buf[0] == '\033') { @@ -513,14 +534,31 @@ first_key: if (tk->next != NULL) goto partial_key; key = tk->key; - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) key |= KEYC_ESCAPE; goto complete_key; } } + /* Is this valid UTF-8? */ + if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) { + size = ud.size; + if (len < size) { + if (expired) + goto discard_key; + goto partial_key; + } + for (i = 1; i < size; i++) + more = utf8_append(&ud, (u_char)buf[i]); + if (more != UTF8_DONE) + goto discard_key; + key = utf8_combine(&ud); + log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); + goto complete_key; + } + /* No key found, take first. */ - key = (u_char) *buf; + key = (u_char)*buf; size = 1; /* @@ -548,7 +586,7 @@ partial_key: } /* Get the time period. */ - delay = options_get_number(&global_options, "escape-time"); + delay = options_get_number(global_options, "escape-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; @@ -562,7 +600,7 @@ partial_key: return (0); complete_key: - log_debug("complete key %.*s %#x", (int) size, buf, key); + log_debug("complete key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); @@ -582,15 +620,23 @@ complete_key: } /* Fire the key. */ - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) server_client_handle_key(tty->client, key); return (1); + +discard_key: + log_debug("discard key %.*s %#llx", (int)size, buf, key); + + /* Remove data from buffer. */ + evbuffer_drain(tty->event->input, size); + + return (1); } /* Key timer callback. */ void -tty_keys_callback(unused int fd, unused short events, void *data) +tty_keys_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; @@ -608,9 +654,8 @@ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; - struct utf8_data utf8data; - u_int i, value, x, y, b, sgr, sgr_b, sgr_rel; - unsigned char c; + u_int i, x, y, b, sgr_b; + u_char sgr_type, c; /* * Standard mouse sequences are \033[M followed by three characters @@ -626,7 +671,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) */ *size = 0; - x = y = b = sgr = sgr_b = sgr_rel = 0; + x = y = b = sgr_b = 0; + sgr_type = ' '; /* First two bytes are always \033[. */ if (buf[0] != '\033') @@ -639,8 +685,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (1); /* - * Third byte is M in old standard and UTF-8 extension, < in SGR - * extension. + * Third byte is M in old standard (and UTF-8 extension which we do not + * support), < in SGR extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ @@ -648,39 +694,28 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) for (i = 0; i < 3; i++) { if (len <= *size) return (1); - - if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&utf8data, buf[*size])) { - if (utf8data.size != 2) - return (-1); - (*size)++; - if (len <= *size) - return (1); - utf8_append(&utf8data, buf[*size]); - value = utf8_combine(&utf8data); - } else - value = (u_char) buf[*size]; - (*size)++; - } else { - value = (u_char) buf[*size]; - (*size)++; - } - + c = (u_char)buf[(*size)++]; if (i == 0) - b = value; + b = c; else if (i == 1) - x = value; + x = c; else - y = value; + y = c; } - log_debug("mouse input: %.*s", (int) *size, buf); + log_debug("mouse input: %.*s", (int)*size, buf); /* Check and return the mouse input. */ - if (b < 32 || x < 33 || y < 33) + if (b < 32) return (-1); b -= 32; - x -= 33; - y -= 33; + if (x >= 33) + x -= 33; + else + x = 256 - x; + if (y >= 33) + y -= 33; + else + y = 256 - y; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; @@ -707,125 +742,47 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) while (1) { if (len <= *size) return (1); - c = (u_char) buf[(*size)++]; + c = (u_char)buf[(*size)++]; if (c == 'M' || c == 'm') break; if (c < '0' || c > '9') return (-1); y = 10 * y + (c - '0'); } - log_debug("mouse input (sgr): %.*s", (int) *size, buf); + log_debug("mouse input (SGR): %.*s", (int)*size, buf); /* Check and return the mouse input. */ if (x < 1 || y < 1) return (-1); x--; y--; - sgr = 1; - sgr_rel = (c == 'm'); - - /* Figure out what b would be in old format. */ b = sgr_b; - if (sgr_rel) + + /* Type is M for press, m for release. */ + sgr_type = c; + if (sgr_type == 'm') b |= 3; + + /* + * Some terminals (like PuTTY 0.63) mistakenly send + * button-release events for scroll-wheel button-press event. + * Discard it before it reaches any program running inside + * tmux. + */ + if (sgr_type == 'm' && (sgr_b & 64)) + return (-2); } else return (-1); - /* Fill in mouse structure. */ - if (~m->event & MOUSE_EVENT_WHEEL) { - m->lx = m->x; - m->ly = m->y; - } - m->xb = b; - m->sgr = sgr; - m->sgr_xb = sgr_b; - m->sgr_rel = sgr_rel; - if (b & 64) { /* wheel button */ - b &= 3; - if (b == 0) - m->wheel = MOUSE_WHEEL_UP; - else if (b == 1) - m->wheel = MOUSE_WHEEL_DOWN; - m->event = MOUSE_EVENT_WHEEL; - } else if ((b & 3) == 3) { - if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) { - m->event = MOUSE_EVENT_CLICK; - } else - m->event = MOUSE_EVENT_DRAG; - m->event |= MOUSE_EVENT_UP; - } else { - if (b & 32) /* drag motion */ - m->event = MOUSE_EVENT_DRAG; - else { - if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y) - m->clicks = (m->clicks + 1) % 3; - else - m->clicks = 0; - m->sx = x; - m->sy = y; - m->event = MOUSE_EVENT_DOWN; - } - m->button = (b & 3); - } + /* Fill mouse event. */ + m->lx = m->x; m->x = x; + m->ly = m->y; m->y = y; - - return (0); -} - -/* - * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for - * partial. - */ -int -tty_keys_device(struct tty *tty, const char *buf, size_t len, size_t *size) -{ - u_int i, class; - char tmp[64], *endptr; - - /* - * Primary device attributes are \033[?a;b and secondary are - * \033[>a;b;c. - */ - - *size = 0; - - /* First three bytes are always \033[?. */ - if (buf[0] != '\033') - return (-1); - if (len == 1) - return (1); - if (buf[1] != '[') - return (-1); - if (len == 2) - return (1); - if (buf[2] != '>' && buf[2] != '?') - return (-1); - if (len == 3) - return (1); - - /* Copy the rest up to a 'c'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { - if (3 + i == len) - return (1); - tmp[i] = buf[3 + i]; - } - if (i == (sizeof tmp) - 1) - return (-1); - tmp[i] = '\0'; - *size = 4 + i; - - /* Only primary is of interest. */ - if (buf[2] != '?') - return (0); - - /* Convert service class. */ - class = strtoul(tmp, &endptr, 10); - if (*endptr != ';') - class = 0; - - log_debug("received service class %u", class); - tty_set_class(tty, class); + m->lb = m->b; + m->b = b; + m->sgr_type = sgr_type; + m->sgr_b = sgr_b; return (0); } diff --git a/tty-term.c b/tty-term.c index 95cb5db7..df69bd15 100644 --- a/tty-term.c +++ b/tty-term.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -18,9 +18,9 @@ #include -#ifdef HAVE_CURSES_H +#if defined(HAVE_CURSES_H) #include -#else +#elif defined(HAVE_NCURSES_H) #include #endif #include @@ -35,165 +35,237 @@ char *tty_term_strip(const char *); struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); -const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { - { TTYC_ACSC, TTYCODE_STRING, "acsc" }, - { TTYC_AX, TTYCODE_FLAG, "AX" }, - { TTYC_BEL, TTYCODE_STRING, "bel" }, - { TTYC_BLINK, TTYCODE_STRING, "blink" }, - { TTYC_BOLD, TTYCODE_STRING, "bold" }, - { TTYC_CC, TTYCODE_STRING, "Cc" }, - { TTYC_CIVIS, TTYCODE_STRING, "civis" }, - { TTYC_CLEAR, TTYCODE_STRING, "clear" }, - { TTYC_CNORM, TTYCODE_STRING, "cnorm" }, - { TTYC_COLORS, TTYCODE_NUMBER, "colors" }, - { TTYC_CR, TTYCODE_STRING, "Cr" }, - { TTYC_CS1, TTYCODE_STRING, "Cs" }, - { TTYC_CSR, TTYCODE_STRING, "csr" }, - { TTYC_CSR1, TTYCODE_STRING, "Csr" }, - { TTYC_CUB, TTYCODE_STRING, "cub" }, - { TTYC_CUB1, TTYCODE_STRING, "cub1" }, - { TTYC_CUD, TTYCODE_STRING, "cud" }, - { TTYC_CUD1, TTYCODE_STRING, "cud1" }, - { TTYC_CUF, TTYCODE_STRING, "cuf" }, - { TTYC_CUF1, TTYCODE_STRING, "cuf1" }, - { TTYC_CUP, TTYCODE_STRING, "cup" }, - { TTYC_CUU, TTYCODE_STRING, "cuu" }, - { TTYC_CUU1, TTYCODE_STRING, "cuu1" }, - { TTYC_DCH, TTYCODE_STRING, "dch" }, - { TTYC_DCH1, TTYCODE_STRING, "dch1" }, - { TTYC_DIM, TTYCODE_STRING, "dim" }, - { TTYC_DL, TTYCODE_STRING, "dl" }, - { TTYC_DL1, TTYCODE_STRING, "dl1" }, - { TTYC_E3, TTYCODE_STRING, "E3" }, - { TTYC_ECH, TTYCODE_STRING, "ech" }, - { TTYC_EL, TTYCODE_STRING, "el" }, - { TTYC_EL1, TTYCODE_STRING, "el1" }, - { TTYC_ENACS, TTYCODE_STRING, "enacs" }, - { TTYC_FSL, TTYCODE_STRING, "fsl" }, - { TTYC_HOME, TTYCODE_STRING, "home" }, - { TTYC_HPA, TTYCODE_STRING, "hpa" }, - { TTYC_ICH, TTYCODE_STRING, "ich" }, - { TTYC_ICH1, TTYCODE_STRING, "ich1" }, - { TTYC_IL, TTYCODE_STRING, "il" }, - { TTYC_IL1, TTYCODE_STRING, "il1" }, - { TTYC_INVIS, TTYCODE_STRING, "invis" }, - { TTYC_IS1, TTYCODE_STRING, "is1" }, - { TTYC_IS2, TTYCODE_STRING, "is2" }, - { TTYC_IS3, TTYCODE_STRING, "is3" }, - { TTYC_KCBT, TTYCODE_STRING, "kcbt" }, - { TTYC_KCUB1, TTYCODE_STRING, "kcub1" }, - { TTYC_KCUD1, TTYCODE_STRING, "kcud1" }, - { TTYC_KCUF1, TTYCODE_STRING, "kcuf1" }, - { TTYC_KCUU1, TTYCODE_STRING, "kcuu1" }, - { TTYC_KDC2, TTYCODE_STRING, "kDC" }, - { TTYC_KDC3, TTYCODE_STRING, "kDC3" }, - { TTYC_KDC4, TTYCODE_STRING, "kDC4" }, - { TTYC_KDC5, TTYCODE_STRING, "kDC5" }, - { TTYC_KDC6, TTYCODE_STRING, "kDC6" }, - { TTYC_KDC7, TTYCODE_STRING, "kDC7" }, - { TTYC_KDCH1, TTYCODE_STRING, "kdch1" }, - { TTYC_KDN2, TTYCODE_STRING, "kDN" }, - { TTYC_KDN3, TTYCODE_STRING, "kDN3" }, - { TTYC_KDN4, TTYCODE_STRING, "kDN4" }, - { TTYC_KDN5, TTYCODE_STRING, "kDN5" }, - { TTYC_KDN6, TTYCODE_STRING, "kDN6" }, - { TTYC_KDN7, TTYCODE_STRING, "kDN7" }, - { TTYC_KEND, TTYCODE_STRING, "kend" }, - { TTYC_KEND2, TTYCODE_STRING, "kEND" }, - { TTYC_KEND3, TTYCODE_STRING, "kEND3" }, - { TTYC_KEND4, TTYCODE_STRING, "kEND4" }, - { TTYC_KEND5, TTYCODE_STRING, "kEND5" }, - { TTYC_KEND6, TTYCODE_STRING, "kEND6" }, - { TTYC_KEND7, TTYCODE_STRING, "kEND7" }, - { TTYC_KF1, TTYCODE_STRING, "kf1" }, - { TTYC_KF10, TTYCODE_STRING, "kf10" }, - { TTYC_KF11, TTYCODE_STRING, "kf11" }, - { TTYC_KF12, TTYCODE_STRING, "kf12" }, - { TTYC_KF13, TTYCODE_STRING, "kf13" }, - { TTYC_KF14, TTYCODE_STRING, "kf14" }, - { TTYC_KF15, TTYCODE_STRING, "kf15" }, - { TTYC_KF16, TTYCODE_STRING, "kf16" }, - { TTYC_KF17, TTYCODE_STRING, "kf17" }, - { TTYC_KF18, TTYCODE_STRING, "kf18" }, - { TTYC_KF19, TTYCODE_STRING, "kf19" }, - { TTYC_KF2, TTYCODE_STRING, "kf2" }, - { TTYC_KF20, TTYCODE_STRING, "kf20" }, - { TTYC_KF3, TTYCODE_STRING, "kf3" }, - { TTYC_KF4, TTYCODE_STRING, "kf4" }, - { TTYC_KF5, TTYCODE_STRING, "kf5" }, - { TTYC_KF6, TTYCODE_STRING, "kf6" }, - { TTYC_KF7, TTYCODE_STRING, "kf7" }, - { TTYC_KF8, TTYCODE_STRING, "kf8" }, - { TTYC_KF9, TTYCODE_STRING, "kf9" }, - { TTYC_KHOM2, TTYCODE_STRING, "kHOM" }, - { TTYC_KHOM3, TTYCODE_STRING, "kHOM3" }, - { TTYC_KHOM4, TTYCODE_STRING, "kHOM4" }, - { TTYC_KHOM5, TTYCODE_STRING, "kHOM5" }, - { TTYC_KHOM6, TTYCODE_STRING, "kHOM6" }, - { TTYC_KHOM7, TTYCODE_STRING, "kHOM7" }, - { TTYC_KHOME, TTYCODE_STRING, "khome" }, - { TTYC_KIC2, TTYCODE_STRING, "kIC" }, - { TTYC_KIC3, TTYCODE_STRING, "kIC3" }, - { TTYC_KIC4, TTYCODE_STRING, "kIC4" }, - { TTYC_KIC5, TTYCODE_STRING, "kIC5" }, - { TTYC_KIC6, TTYCODE_STRING, "kIC6" }, - { TTYC_KIC7, TTYCODE_STRING, "kIC7" }, - { TTYC_KICH1, TTYCODE_STRING, "kich1" }, - { TTYC_KLFT2, TTYCODE_STRING, "kLFT" }, - { TTYC_KLFT3, TTYCODE_STRING, "kLFT3" }, - { TTYC_KLFT4, TTYCODE_STRING, "kLFT4" }, - { TTYC_KLFT5, TTYCODE_STRING, "kLFT5" }, - { TTYC_KLFT6, TTYCODE_STRING, "kLFT6" }, - { TTYC_KLFT7, TTYCODE_STRING, "kLFT7" }, - { TTYC_KMOUS, TTYCODE_STRING, "kmous" }, - { TTYC_KNP, TTYCODE_STRING, "knp" }, - { TTYC_KNXT2, TTYCODE_STRING, "kNXT" }, - { TTYC_KNXT3, TTYCODE_STRING, "kNXT3" }, - { TTYC_KNXT4, TTYCODE_STRING, "kNXT4" }, - { TTYC_KNXT5, TTYCODE_STRING, "kNXT5" }, - { TTYC_KNXT6, TTYCODE_STRING, "kNXT6" }, - { TTYC_KNXT7, TTYCODE_STRING, "kNXT7" }, - { TTYC_KPP, TTYCODE_STRING, "kpp" }, - { TTYC_KPRV2, TTYCODE_STRING, "kPRV" }, - { TTYC_KPRV3, TTYCODE_STRING, "kPRV3" }, - { TTYC_KPRV4, TTYCODE_STRING, "kPRV4" }, - { TTYC_KPRV5, TTYCODE_STRING, "kPRV5" }, - { TTYC_KPRV6, TTYCODE_STRING, "kPRV6" }, - { TTYC_KPRV7, TTYCODE_STRING, "kPRV7" }, - { TTYC_KRIT2, TTYCODE_STRING, "kRIT" }, - { TTYC_KRIT3, TTYCODE_STRING, "kRIT3" }, - { TTYC_KRIT4, TTYCODE_STRING, "kRIT4" }, - { TTYC_KRIT5, TTYCODE_STRING, "kRIT5" }, - { TTYC_KRIT6, TTYCODE_STRING, "kRIT6" }, - { TTYC_KRIT7, TTYCODE_STRING, "kRIT7" }, - { TTYC_KUP2, TTYCODE_STRING, "kUP" }, - { TTYC_KUP3, TTYCODE_STRING, "kUP3" }, - { TTYC_KUP4, TTYCODE_STRING, "kUP4" }, - { TTYC_KUP5, TTYCODE_STRING, "kUP5" }, - { TTYC_KUP6, TTYCODE_STRING, "kUP6" }, - { TTYC_KUP7, TTYCODE_STRING, "kUP7" }, - { TTYC_MS, TTYCODE_STRING, "Ms" }, - { TTYC_OP, TTYCODE_STRING, "op" }, - { TTYC_REV, TTYCODE_STRING, "rev" }, - { TTYC_RI, TTYCODE_STRING, "ri" }, - { TTYC_RMACS, TTYCODE_STRING, "rmacs" }, - { TTYC_RMCUP, TTYCODE_STRING, "rmcup" }, - { TTYC_RMKX, TTYCODE_STRING, "rmkx" }, - { TTYC_SETAB, TTYCODE_STRING, "setab" }, - { TTYC_SETAF, TTYCODE_STRING, "setaf" }, - { TTYC_SGR0, TTYCODE_STRING, "sgr0" }, - { TTYC_SITM, TTYCODE_STRING, "sitm" }, - { TTYC_SMACS, TTYCODE_STRING, "smacs" }, - { TTYC_SMCUP, TTYCODE_STRING, "smcup" }, - { TTYC_SMKX, TTYCODE_STRING, "smkx" }, - { TTYC_SMSO, TTYCODE_STRING, "smso" }, - { TTYC_SMUL, TTYCODE_STRING, "smul" }, - { TTYC_TSL, TTYCODE_STRING, "tsl" }, - { TTYC_VPA, TTYCODE_STRING, "vpa" }, - { TTYC_XENL, TTYCODE_FLAG, "xenl" }, - { TTYC_XT, TTYCODE_FLAG, "XT" }, +enum tty_code_type { + TTYCODE_NONE = 0, + TTYCODE_STRING, + TTYCODE_NUMBER, + TTYCODE_FLAG, }; +struct tty_code { + enum tty_code_type type; + union { + char *string; + int number; + int flag; + } value; +}; + +struct tty_term_code_entry { + enum tty_code_type type; + const char *name; +}; + +const struct tty_term_code_entry tty_term_codes[] = { + [TTYC_ACSC] = { TTYCODE_STRING, "acsc" }, + [TTYC_AX] = { TTYCODE_FLAG, "AX" }, + [TTYC_BCE] = { TTYCODE_FLAG, "bce" }, + [TTYC_BEL] = { TTYCODE_STRING, "bel" }, + [TTYC_BLINK] = { TTYCODE_STRING, "blink" }, + [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, + [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, + [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, + [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, + [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, + [TTYC_CR] = { TTYCODE_STRING, "Cr" }, + [TTYC_CS] = { TTYCODE_STRING, "Cs" }, + [TTYC_CSR] = { TTYCODE_STRING, "csr" }, + [TTYC_CUB] = { TTYCODE_STRING, "cub" }, + [TTYC_CUB1] = { TTYCODE_STRING, "cub1" }, + [TTYC_CUD] = { TTYCODE_STRING, "cud" }, + [TTYC_CUD1] = { TTYCODE_STRING, "cud1" }, + [TTYC_CUF] = { TTYCODE_STRING, "cuf" }, + [TTYC_CUF1] = { TTYCODE_STRING, "cuf1" }, + [TTYC_CUP] = { TTYCODE_STRING, "cup" }, + [TTYC_CUU] = { TTYCODE_STRING, "cuu" }, + [TTYC_CUU1] = { TTYCODE_STRING, "cuu1" }, + [TTYC_CVVIS] = { TTYCODE_STRING, "cvvis" }, + [TTYC_DCH] = { TTYCODE_STRING, "dch" }, + [TTYC_DCH1] = { TTYCODE_STRING, "dch1" }, + [TTYC_DIM] = { TTYCODE_STRING, "dim" }, + [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, + [TTYC_E3] = { TTYCODE_STRING, "E3" }, + [TTYC_ECH] = { TTYCODE_STRING, "ech" }, + [TTYC_EL] = { TTYCODE_STRING, "el" }, + [TTYC_EL1] = { TTYCODE_STRING, "el1" }, + [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, + [TTYC_HOME] = { TTYCODE_STRING, "home" }, + [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, + [TTYC_ICH] = { TTYCODE_STRING, "ich" }, + [TTYC_ICH1] = { TTYCODE_STRING, "ich1" }, + [TTYC_IL] = { TTYCODE_STRING, "il" }, + [TTYC_IL1] = { TTYCODE_STRING, "il1" }, + [TTYC_INVIS] = { TTYCODE_STRING, "invis" }, + [TTYC_IS1] = { TTYCODE_STRING, "is1" }, + [TTYC_IS2] = { TTYCODE_STRING, "is2" }, + [TTYC_IS3] = { TTYCODE_STRING, "is3" }, + [TTYC_KCBT] = { TTYCODE_STRING, "kcbt" }, + [TTYC_KCUB1] = { TTYCODE_STRING, "kcub1" }, + [TTYC_KCUD1] = { TTYCODE_STRING, "kcud1" }, + [TTYC_KCUF1] = { TTYCODE_STRING, "kcuf1" }, + [TTYC_KCUU1] = { TTYCODE_STRING, "kcuu1" }, + [TTYC_KDC2] = { TTYCODE_STRING, "kDC" }, + [TTYC_KDC3] = { TTYCODE_STRING, "kDC3" }, + [TTYC_KDC4] = { TTYCODE_STRING, "kDC4" }, + [TTYC_KDC5] = { TTYCODE_STRING, "kDC5" }, + [TTYC_KDC6] = { TTYCODE_STRING, "kDC6" }, + [TTYC_KDC7] = { TTYCODE_STRING, "kDC7" }, + [TTYC_KDCH1] = { TTYCODE_STRING, "kdch1" }, + [TTYC_KDN2] = { TTYCODE_STRING, "kDN" }, + [TTYC_KDN3] = { TTYCODE_STRING, "kDN3" }, + [TTYC_KDN4] = { TTYCODE_STRING, "kDN4" }, + [TTYC_KDN5] = { TTYCODE_STRING, "kDN5" }, + [TTYC_KDN6] = { TTYCODE_STRING, "kDN6" }, + [TTYC_KDN7] = { TTYCODE_STRING, "kDN7" }, + [TTYC_KEND] = { TTYCODE_STRING, "kend" }, + [TTYC_KEND2] = { TTYCODE_STRING, "kEND" }, + [TTYC_KEND3] = { TTYCODE_STRING, "kEND3" }, + [TTYC_KEND4] = { TTYCODE_STRING, "kEND4" }, + [TTYC_KEND5] = { TTYCODE_STRING, "kEND5" }, + [TTYC_KEND6] = { TTYCODE_STRING, "kEND6" }, + [TTYC_KEND7] = { TTYCODE_STRING, "kEND7" }, + [TTYC_KF1] = { TTYCODE_STRING, "kf1" }, + [TTYC_KF10] = { TTYCODE_STRING, "kf10" }, + [TTYC_KF11] = { TTYCODE_STRING, "kf11" }, + [TTYC_KF12] = { TTYCODE_STRING, "kf12" }, + [TTYC_KF13] = { TTYCODE_STRING, "kf13" }, + [TTYC_KF14] = { TTYCODE_STRING, "kf14" }, + [TTYC_KF15] = { TTYCODE_STRING, "kf15" }, + [TTYC_KF16] = { TTYCODE_STRING, "kf16" }, + [TTYC_KF17] = { TTYCODE_STRING, "kf17" }, + [TTYC_KF18] = { TTYCODE_STRING, "kf18" }, + [TTYC_KF19] = { TTYCODE_STRING, "kf19" }, + [TTYC_KF2] = { TTYCODE_STRING, "kf2" }, + [TTYC_KF20] = { TTYCODE_STRING, "kf20" }, + [TTYC_KF21] = { TTYCODE_STRING, "kf21" }, + [TTYC_KF22] = { TTYCODE_STRING, "kf22" }, + [TTYC_KF23] = { TTYCODE_STRING, "kf23" }, + [TTYC_KF24] = { TTYCODE_STRING, "kf24" }, + [TTYC_KF25] = { TTYCODE_STRING, "kf25" }, + [TTYC_KF26] = { TTYCODE_STRING, "kf26" }, + [TTYC_KF27] = { TTYCODE_STRING, "kf27" }, + [TTYC_KF28] = { TTYCODE_STRING, "kf28" }, + [TTYC_KF29] = { TTYCODE_STRING, "kf29" }, + [TTYC_KF3] = { TTYCODE_STRING, "kf3" }, + [TTYC_KF30] = { TTYCODE_STRING, "kf30" }, + [TTYC_KF31] = { TTYCODE_STRING, "kf31" }, + [TTYC_KF32] = { TTYCODE_STRING, "kf32" }, + [TTYC_KF33] = { TTYCODE_STRING, "kf33" }, + [TTYC_KF34] = { TTYCODE_STRING, "kf34" }, + [TTYC_KF35] = { TTYCODE_STRING, "kf35" }, + [TTYC_KF36] = { TTYCODE_STRING, "kf36" }, + [TTYC_KF37] = { TTYCODE_STRING, "kf37" }, + [TTYC_KF38] = { TTYCODE_STRING, "kf38" }, + [TTYC_KF39] = { TTYCODE_STRING, "kf39" }, + [TTYC_KF4] = { TTYCODE_STRING, "kf4" }, + [TTYC_KF40] = { TTYCODE_STRING, "kf40" }, + [TTYC_KF41] = { TTYCODE_STRING, "kf41" }, + [TTYC_KF42] = { TTYCODE_STRING, "kf42" }, + [TTYC_KF43] = { TTYCODE_STRING, "kf43" }, + [TTYC_KF44] = { TTYCODE_STRING, "kf44" }, + [TTYC_KF45] = { TTYCODE_STRING, "kf45" }, + [TTYC_KF46] = { TTYCODE_STRING, "kf46" }, + [TTYC_KF47] = { TTYCODE_STRING, "kf47" }, + [TTYC_KF48] = { TTYCODE_STRING, "kf48" }, + [TTYC_KF49] = { TTYCODE_STRING, "kf49" }, + [TTYC_KF5] = { TTYCODE_STRING, "kf5" }, + [TTYC_KF50] = { TTYCODE_STRING, "kf50" }, + [TTYC_KF51] = { TTYCODE_STRING, "kf51" }, + [TTYC_KF52] = { TTYCODE_STRING, "kf52" }, + [TTYC_KF53] = { TTYCODE_STRING, "kf53" }, + [TTYC_KF54] = { TTYCODE_STRING, "kf54" }, + [TTYC_KF55] = { TTYCODE_STRING, "kf55" }, + [TTYC_KF56] = { TTYCODE_STRING, "kf56" }, + [TTYC_KF57] = { TTYCODE_STRING, "kf57" }, + [TTYC_KF58] = { TTYCODE_STRING, "kf58" }, + [TTYC_KF59] = { TTYCODE_STRING, "kf59" }, + [TTYC_KF6] = { TTYCODE_STRING, "kf6" }, + [TTYC_KF60] = { TTYCODE_STRING, "kf60" }, + [TTYC_KF61] = { TTYCODE_STRING, "kf61" }, + [TTYC_KF62] = { TTYCODE_STRING, "kf62" }, + [TTYC_KF63] = { TTYCODE_STRING, "kf63" }, + [TTYC_KF7] = { TTYCODE_STRING, "kf7" }, + [TTYC_KF8] = { TTYCODE_STRING, "kf8" }, + [TTYC_KF9] = { TTYCODE_STRING, "kf9" }, + [TTYC_KHOM2] = { TTYCODE_STRING, "kHOM" }, + [TTYC_KHOM3] = { TTYCODE_STRING, "kHOM3" }, + [TTYC_KHOM4] = { TTYCODE_STRING, "kHOM4" }, + [TTYC_KHOM5] = { TTYCODE_STRING, "kHOM5" }, + [TTYC_KHOM6] = { TTYCODE_STRING, "kHOM6" }, + [TTYC_KHOM7] = { TTYCODE_STRING, "kHOM7" }, + [TTYC_KHOME] = { TTYCODE_STRING, "khome" }, + [TTYC_KIC2] = { TTYCODE_STRING, "kIC" }, + [TTYC_KIC3] = { TTYCODE_STRING, "kIC3" }, + [TTYC_KIC4] = { TTYCODE_STRING, "kIC4" }, + [TTYC_KIC5] = { TTYCODE_STRING, "kIC5" }, + [TTYC_KIC6] = { TTYCODE_STRING, "kIC6" }, + [TTYC_KIC7] = { TTYCODE_STRING, "kIC7" }, + [TTYC_KICH1] = { TTYCODE_STRING, "kich1" }, + [TTYC_KLFT2] = { TTYCODE_STRING, "kLFT" }, + [TTYC_KLFT3] = { TTYCODE_STRING, "kLFT3" }, + [TTYC_KLFT4] = { TTYCODE_STRING, "kLFT4" }, + [TTYC_KLFT5] = { TTYCODE_STRING, "kLFT5" }, + [TTYC_KLFT6] = { TTYCODE_STRING, "kLFT6" }, + [TTYC_KLFT7] = { TTYCODE_STRING, "kLFT7" }, + [TTYC_KMOUS] = { TTYCODE_STRING, "kmous" }, + [TTYC_KNP] = { TTYCODE_STRING, "knp" }, + [TTYC_KNXT2] = { TTYCODE_STRING, "kNXT" }, + [TTYC_KNXT3] = { TTYCODE_STRING, "kNXT3" }, + [TTYC_KNXT4] = { TTYCODE_STRING, "kNXT4" }, + [TTYC_KNXT5] = { TTYCODE_STRING, "kNXT5" }, + [TTYC_KNXT6] = { TTYCODE_STRING, "kNXT6" }, + [TTYC_KNXT7] = { TTYCODE_STRING, "kNXT7" }, + [TTYC_KPP] = { TTYCODE_STRING, "kpp" }, + [TTYC_KPRV2] = { TTYCODE_STRING, "kPRV" }, + [TTYC_KPRV3] = { TTYCODE_STRING, "kPRV3" }, + [TTYC_KPRV4] = { TTYCODE_STRING, "kPRV4" }, + [TTYC_KPRV5] = { TTYCODE_STRING, "kPRV5" }, + [TTYC_KPRV6] = { TTYCODE_STRING, "kPRV6" }, + [TTYC_KPRV7] = { TTYCODE_STRING, "kPRV7" }, + [TTYC_KRIT2] = { TTYCODE_STRING, "kRIT" }, + [TTYC_KRIT3] = { TTYCODE_STRING, "kRIT3" }, + [TTYC_KRIT4] = { TTYCODE_STRING, "kRIT4" }, + [TTYC_KRIT5] = { TTYCODE_STRING, "kRIT5" }, + [TTYC_KRIT6] = { TTYCODE_STRING, "kRIT6" }, + [TTYC_KRIT7] = { TTYCODE_STRING, "kRIT7" }, + [TTYC_KUP2] = { TTYCODE_STRING, "kUP" }, + [TTYC_KUP3] = { TTYCODE_STRING, "kUP3" }, + [TTYC_KUP4] = { TTYCODE_STRING, "kUP4" }, + [TTYC_KUP5] = { TTYCODE_STRING, "kUP5" }, + [TTYC_KUP6] = { TTYCODE_STRING, "kUP6" }, + [TTYC_KUP7] = { TTYCODE_STRING, "kUP7" }, + [TTYC_MS] = { TTYCODE_STRING, "Ms" }, + [TTYC_OP] = { TTYCODE_STRING, "op" }, + [TTYC_REV] = { TTYCODE_STRING, "rev" }, + [TTYC_RI] = { TTYCODE_STRING, "ri" }, + [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, + [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, + [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, + [TTYC_SE] = { TTYCODE_STRING, "Se" }, + [TTYC_SETAB] = { TTYCODE_STRING, "setab" }, + [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, + [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, + [TTYC_SITM] = { TTYCODE_STRING, "sitm" }, + [TTYC_SMACS] = { TTYCODE_STRING, "smacs" }, + [TTYC_SMCUP] = { TTYCODE_STRING, "smcup" }, + [TTYC_SMKX] = { TTYCODE_STRING, "smkx" }, + [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, + [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, + [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, + [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, + [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, + [TTYC_XT] = { TTYCODE_FLAG, "XT" }, +}; + +u_int +tty_term_ncodes(void) +{ + return (nitems(tty_term_codes)); +} + char * tty_term_strip(const char *s) { @@ -267,11 +339,11 @@ tty_term_override(struct tty_term *term, const char *overrides) log_debug("%s override: %s %s", term->name, entstr, removeflag ? "@" : val); - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; if (strcmp(entstr, ent->name) != 0) continue; - code = &term->codes[ent->code]; + code = &term->codes[i]; if (removeflag) { code->type = TTYCODE_NONE; @@ -308,7 +380,7 @@ tty_term_override(struct tty_term *term, const char *overrides) } struct tty_term * -tty_term_find(char *name, int fd, const char *overrides, char **cause) +tty_term_find(char *name, int fd, char **cause) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -330,19 +402,19 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) term->name = xstrdup(name); term->references = 1; term->flags = 0; - memset(term->codes, 0, sizeof term->codes); + term->codes = xcalloc (tty_term_ncodes(), sizeof *term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); /* Set up curses terminal. */ if (setupterm(name, fd, &error) != OK) { switch (error) { case 1: - xasprintf( - cause, "can't use hardcopy terminal: %s", name); + xasprintf(cause, "can't use hardcopy terminal: %s", + name); break; case 0: - xasprintf( - cause, "missing or unsuitable terminal: %s", name); + xasprintf(cause, "missing or unsuitable terminal: %s", + name); break; case -1: xasprintf(cause, "can't find terminfo database"); @@ -355,10 +427,10 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) } /* Fill in codes. */ - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; + code = &term->codes[i]; code->type = TTYCODE_NONE; switch (ent->type) { case TTYCODE_NONE: @@ -382,11 +454,14 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) if (n == -1) break; code->type = TTYCODE_FLAG; - code->value.number = n; + code->value.flag = n; break; } } - tty_term_override(term, overrides); + + /* Apply terminal overrides. */ + s = options_get_string(global_options, "terminal-overrides"); + tty_term_override(term, s); /* Delete curses data. */ #if !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR > 5 || \ @@ -410,11 +485,9 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) goto error; } - /* Figure out if we have 256 or 88 colours. */ + /* Figure out if we have 256. */ if (tty_term_number(term, TTYC_COLORS) == 256) term->flags |= TERM_256COLOURS; - if (tty_term_number(term, TTYC_COLORS) == 88) - term->flags |= TERM_88COLOURS; /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 @@ -467,10 +540,12 @@ tty_term_free(struct tty_term *term) LIST_REMOVE(term, entry); - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { if (term->codes[i].type == TTYCODE_STRING) free(term->codes[i].value.string); } + free(term->codes); + free(term->name); free(term); } @@ -487,11 +562,10 @@ tty_term_string(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (""); if (term->codes[code].type != TTYCODE_STRING) - log_fatalx("not a string: %d", code); + fatalx("not a string: %d", code); return (term->codes[code].value.string); } -/* No vtparm. Fucking curses. */ const char * tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) { @@ -511,7 +585,8 @@ tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a) } const char * -tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, const void *b) +tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, + const void *b) { return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0)); } @@ -522,7 +597,7 @@ tty_term_number(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_NUMBER) - log_fatalx("not a number: %d", code); + fatalx("not a number: %d", code); return (term->codes[code].value.number); } @@ -532,6 +607,38 @@ tty_term_flag(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_FLAG) - log_fatalx("not a flag: %d", code); + fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } + +const char * +tty_term_describe(struct tty_term *term, enum tty_code_code code) +{ + static char s[256]; + char out[128]; + + switch (term->codes[code].type) { + case TTYCODE_NONE: + xsnprintf(s, sizeof s, "%4u: %s: [missing]", + code, tty_term_codes[code].name); + break; + case TTYCODE_STRING: + strnvis(out, term->codes[code].value.string, sizeof out, + VIS_OCTAL|VIS_TAB|VIS_NL); + xsnprintf(s, sizeof s, "%4u: %s: (string) %s", + code, tty_term_codes[code].name, + out); + break; + case TTYCODE_NUMBER: + xsnprintf(s, sizeof s, "%4u: %s: (number) %d", + code, tty_term_codes[code].name, + term->codes[code].value.number); + break; + case TTYCODE_FLAG: + xsnprintf(s, sizeof s, "%4u: %s: (flag) %s", + code, tty_term_codes[code].name, + term->codes[code].value.flag ? "true" : "false"); + break; + } + return (s); +} diff --git a/tty.c b/tty.c index ab75d948..da5eac4d 100644 --- a/tty.c +++ b/tty.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -31,11 +31,13 @@ #include "tmux.h" +static int tty_log_fd = -1; + void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); +void tty_set_italics(struct tty *); int tty_try_256(struct tty *, u_char, const char *); -int tty_try_88(struct tty *, u_char, const char *); void tty_colours(struct tty *, const struct grid_cell *); void tty_check_fg(struct tty *, struct grid_cell *); @@ -44,11 +46,14 @@ void tty_colours_fg(struct tty *, const struct grid_cell *); void tty_colours_bg(struct tty *, const struct grid_cell *); int tty_large_region(struct tty *, const struct tty_ctx *); +int tty_fake_bce(const struct tty *, const struct window_pane *); void tty_redraw_region(struct tty *, const struct tty_ctx *); -void tty_emulate_repeat( - struct tty *, enum tty_code_code, enum tty_code_code, u_int); +void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, + u_int); void tty_repeat_space(struct tty *, u_int); -void tty_cell(struct tty *, const struct grid_cell *); +void tty_cell(struct tty *, const struct grid_cell *, + const struct window_pane *); +void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_use_acs(tty) \ (tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8)) @@ -57,12 +62,26 @@ void tty_cell(struct tty *, const struct grid_cell *); ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) void +tty_create_log(void) +{ + char name[64]; + + xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); + + tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); +} + +int tty_init(struct tty *tty, struct client *c, int fd, char *term) { char *path; + if (!isatty(fd)) + return (-1); + memset(tty, 0, sizeof *tty); - tty->log_fd = -1; if (term == NULL || *term == '\0') tty->termname = xstrdup("unknown"); @@ -72,13 +91,15 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->client = c; if ((path = ttyname(fd)) == NULL) - fatalx("ttyname failed"); + return (-1); tty->path = xstrdup(path); tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; + + return (0); } int @@ -129,20 +150,9 @@ tty_set_size(struct tty *tty, u_int sx, u_int sy) { } int -tty_open(struct tty *tty, const char *overrides, char **cause) +tty_open(struct tty *tty, char **cause) { - char out[64]; - int fd; - - if (debug_level > 3) { - xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid()); - fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - fatal("fcntl failed"); - tty->log_fd = fd; - } - - tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause); + tty->term = tty_term_find(tty->termname, tty->fd, cause); if (tty->term == NULL) { tty_close(tty); return (-1); @@ -151,8 +161,8 @@ tty_open(struct tty *tty, const char *overrides, char **cause) tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_TIMER); - tty->event = bufferevent_new( - tty->fd, tty_read_callback, NULL, tty_error_callback, tty); + tty->event = bufferevent_new(tty->fd, tty_read_callback, NULL, + tty_error_callback, tty); tty_start_tty(tty); @@ -162,7 +172,7 @@ tty_open(struct tty *tty, const char *overrides, char **cause) } void -tty_read_callback(unused struct bufferevent *bufev, void *data) +tty_read_callback(__unused struct bufferevent *bufev, void *data) { struct tty *tty = data; @@ -171,8 +181,8 @@ tty_read_callback(unused struct bufferevent *bufev, void *data) } void -tty_error_callback( - unused struct bufferevent *bufev, unused short what, unused void *data) +tty_error_callback(__unused struct bufferevent *bufev, __unused short what, + __unused void *data) { } @@ -194,7 +204,7 @@ tty_init_termios(int fd, struct termios *orig_tio, struct bufferevent *bufev) tio.c_iflag |= IGNBRK; tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| - ECHOPRT|ECHOKE|ECHOCTL|ISIG); + ECHOPRT|ECHOKE|ISIG); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr(fd, TCSANOW, &tio) == 0) @@ -218,10 +228,14 @@ tty_start_tty(struct tty *tty) tty_putcode(tty, TTYC_CNORM); if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); + tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) - tty_puts(tty, "\033[c\033[>4;1m\033[?1004h"); + if (tty_term_flag(tty->term, TTYC_XT)) { + if (options_get_number(global_options, "focus-events")) { + tty->flags |= TTY_FOCUS; + tty_puts(tty, "\033[?1004h"); + } + } tty->cx = UINT_MAX; tty->cy = UINT_MAX; @@ -234,14 +248,10 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_STARTED; tty_force_cursor_colour(tty, ""); -} -void -tty_set_class(struct tty *tty, u_int class) -{ - if (tty->class != 0) - return; - tty->class = class; + tty->mouse_drag_flag = 0; + tty->mouse_drag_update = NULL; + tty->mouse_drag_release = NULL; } void @@ -271,20 +281,26 @@ tty_stop_tty(struct tty *tty) 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_CLEAR)); - if (tty_term_has(tty->term, TTYC_CS1) && tty->cstyle != 0) { - if (tty_term_has(tty->term, TTYC_CSR1)) - tty_raw(tty, tty_term_string(tty->term, TTYC_CSR1)); + if (tty_term_has(tty->term, TTYC_SS) && tty->cstyle != 0) { + if (tty_term_has(tty->term, TTYC_SE)) + tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); else - tty_raw(tty, tty_term_string1(tty->term, TTYC_CS1, 0)); + tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } + if (tty->mode & MODE_BRACKETPASTE) + tty_raw(tty, "\033[?2004l"); tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); + tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) - tty_raw(tty, "\033[>4m\033[?1004l"); + if (tty_term_flag(tty->term, TTYC_XT)) { + if (tty->flags & TTY_FOCUS) { + tty->flags &= ~TTY_FOCUS; + tty_raw(tty, "\033[?1004l"); + } + } tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); @@ -294,11 +310,6 @@ tty_stop_tty(struct tty *tty) void tty_close(struct tty *tty) { - if (tty->log_fd != -1) { - close(tty->log_fd); - tty->log_fd = -1; - } - if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty_stop_tty(tty); @@ -324,10 +335,8 @@ tty_free(struct tty *tty) tty_close(tty); free(tty->ccolour); - if (tty->path != NULL) - free(tty->path); - if (tty->termname != NULL) - free(tty->termname); + free(tty->path); + free(tty->termname); } void @@ -380,7 +389,8 @@ tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a) } void -tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, const void *b) +tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, + const void *b) { if (a != NULL && b != NULL) tty_puts(tty, tty_term_ptr2(tty->term, code, a, b)); @@ -393,8 +403,8 @@ tty_puts(struct tty *tty, const char *s) return; bufferevent_write(tty->event, s, strlen(s)); - if (tty->log_fd != -1) - write(tty->log_fd, s, strlen(s)); + if (tty_log_fd != -1) + write(tty_log_fd, s, strlen(s)); } void @@ -425,19 +435,34 @@ tty_putc(struct tty *tty, u_char ch) tty->cx++; } - if (tty->log_fd != -1) - write(tty->log_fd, &ch, 1); + if (tty_log_fd != -1) + write(tty_log_fd, &ch, 1); } void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { bufferevent_write(tty->event, buf, len); - if (tty->log_fd != -1) - write(tty->log_fd, buf, len); + if (tty_log_fd != -1) + write(tty_log_fd, buf, len); tty->cx += width; } +void +tty_set_italics(struct tty *tty) +{ + const char *s; + + if (tty_term_has(tty->term, TTYC_SITM)) { + s = options_get_string(global_options, "default-terminal"); + if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { + tty_putcode(tty, TTYC_SITM); + return; + } + } + tty_putcode(tty, TTYC_SMSO); +} + void tty_set_title(struct tty *tty, const char *title) { @@ -456,7 +481,7 @@ tty_force_cursor_colour(struct tty *tty, const char *ccolour) if (*ccolour == '\0') tty_putcode(tty, TTYC_CR); else - tty_putcode_ptr1(tty, TTYC_CC, ccolour); + tty_putcode_ptr1(tty, TTYC_CS, ccolour); free(tty->ccolour); tty->ccolour = xstrdup(ccolour); } @@ -466,61 +491,55 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; - if (strcmp(s->ccolour, tty->ccolour)) + if (s != NULL && strcmp(s->ccolour, tty->ccolour)) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; + if (changed & MODE_BLINKING) { + if (tty_term_has(tty->term, TTYC_CVVIS)) + tty_putcode(tty, TTYC_CVVIS); + else + tty_putcode(tty, TTYC_CNORM); + changed |= MODE_CURSOR; + } if (changed & MODE_CURSOR) { if (mode & MODE_CURSOR) tty_putcode(tty, TTYC_CNORM); else tty_putcode(tty, TTYC_CIVIS); } - if (tty->cstyle != s->cstyle) { - if (tty_term_has(tty->term, TTYC_CS1)) { + if (s != NULL && tty->cstyle != s->cstyle) { + if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && - tty_term_has(tty->term, TTYC_CSR1)) - tty_putcode(tty, TTYC_CSR1); + tty_term_has(tty->term, TTYC_SE)) + tty_putcode(tty, TTYC_SE); else - tty_putcode1(tty, TTYC_CS1, s->cstyle); + tty_putcode1(tty, TTYC_SS, s->cstyle); } tty->cstyle = s->cstyle; } - if (changed & (ALL_MOUSE_MODES|MODE_MOUSE_UTF8)) { + if (changed & ALL_MOUSE_MODES) { if (mode & ALL_MOUSE_MODES) { /* - * Enable the UTF-8 (1005) extension if configured to. * Enable the SGR (1006) extension unconditionally, as * this is safe from misinterpretation. Do it in this * order, because in some terminals it's the last one * that takes effect and SGR is the preferred one. */ - if (mode & MODE_MOUSE_UTF8) - tty_puts(tty, "\033[?1005h"); - else - tty_puts(tty, "\033[?1005l"); tty_puts(tty, "\033[?1006h"); - - if (mode & MODE_MOUSE_ANY) - tty_puts(tty, "\033[?1003h"); - else if (mode & MODE_MOUSE_BUTTON) + if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); } else { - if (tty->mode & MODE_MOUSE_ANY) - tty_puts(tty, "\033[?1003l"); - else if (tty->mode & MODE_MOUSE_BUTTON) + if (tty->mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); - tty_puts(tty, "\033[?1006l"); - if (tty->mode & MODE_MOUSE_UTF8) - tty_puts(tty, "\033[?1005l"); } } if (changed & MODE_KKEYPAD) { @@ -539,8 +558,8 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } void -tty_emulate_repeat( - struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n) +tty_emulate_repeat(struct tty *tty, enum tty_code_code code, + enum tty_code_code code1, u_int n) { if (tty_term_has(tty->term, code)) tty_putcode1(tty, code, n); @@ -563,13 +582,30 @@ tty_repeat_space(struct tty *tty, u_int n) * pane. */ int -tty_large_region(unused struct tty *tty, const struct tty_ctx *ctx) +tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2); } +/* + * Return if BCE is needed but the terminal doesn't have it - it'll need to be + * emulated. + */ +int +tty_fake_bce(const struct tty *tty, const struct window_pane *wp) +{ + struct grid_cell gc; + + memcpy(&gc, &grid_default_cell, sizeof gc); + tty_default_colours(&gc, wp); + + if (gc.bg == 8 && !(gc.flags & GRID_FLAG_BG256)) + return (0); + return (!tty_term_flag(tty->term, TTYC_BCE)); +} + /* * Redraw scroll region using data from screen (already updated). Used when * CSR not supported, or window is a pane that doesn't take up the full @@ -593,23 +629,32 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { for (i = ctx->ocy; i < screen_size_y(s); i++) - tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) - tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); } } void -tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) +tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox, + u_int oy) { - const struct grid_cell *gc; - struct grid_line *gl; - struct grid_cell tmpgc; - struct utf8_data ud; - u_int i, sx; + tty_draw_line(tty, wp, wp->screen, py, ox, oy); +} - tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s); +void +tty_draw_line(struct tty *tty, const struct window_pane *wp, + struct screen *s, u_int py, u_int ox, u_int oy) +{ + struct grid_cell gc; + struct grid_line *gl; + u_int i, sx; + int flags; + + flags = tty->flags & TTY_NOCURSOR; + tty->flags |= TTY_NOCURSOR; + tty_update_mode(tty, tty->mode, s); sx = screen_size_x(s); if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) @@ -618,7 +663,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) sx = tty->sx; /* - * Don't move the cursor to the start permission if it will wrap there + * Don't move the cursor to the start position if it will wrap there * itself. */ gl = NULL; @@ -630,42 +675,52 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) tty_cursor(tty, ox, oy + py); for (i = 0; i < sx; i++) { - gc = grid_view_peek_cell(s->grid, i, py); + grid_view_get_cell(s->grid, i, py, &gc); if (screen_check_selection(s, i, py)) { - memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc); - grid_cell_get(gc, &ud); - grid_cell_set(&tmpgc, &ud); - tmpgc.flags = gc->flags & - ~(GRID_FLAG_FG256|GRID_FLAG_BG256); - tmpgc.flags |= s->sel.cell.flags & + gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); - tty_cell(tty, &tmpgc); - } else - tty_cell(tty, gc); + } + tty_cell(tty, &gc, wp); } - if (sx >= tty->sx) { - tty_update_mode(tty, tty->mode, s); - return; - } - tty_reset(tty); + if (sx < tty->sx) { + tty_attributes(tty, &grid_default_cell, wp); - tty_cursor(tty, ox + sx, oy + py); - if (sx != screen_size_x(s) && ox + screen_size_x(s) >= tty->sx && - tty_term_has(tty->term, TTYC_EL)) - tty_putcode(tty, TTYC_EL); - else - tty_repeat_space(tty, screen_size_x(s) - sx); + tty_cursor(tty, ox + sx, oy + py); + if (sx != screen_size_x(s) && + ox + screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL) && + !tty_fake_bce(tty, wp)) + tty_putcode(tty, TTYC_EL); + else + tty_repeat_space(tty, screen_size_x(s) - sx); + } + + tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; tty_update_mode(tty, tty->mode, s); } +int +tty_client_ready(struct client *c, struct window_pane *wp) +{ + if (c->session == NULL || c->tty.term == NULL) + return (0); + if (c->flags & CLIENT_SUSPENDED) + return (0); + if (c->tty.flags & TTY_FREEZE) + return (0); + if (c->session->curw->window != wp->window) + return (0); + return (1); +} + void -tty_write( - void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) +tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), + struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct client *c; - u_int i; /* wp can be NULL if updating the screen but not the terminal. */ if (wp == NULL) @@ -676,15 +731,8 @@ tty_write( if (!window_pane_visible(wp) || wp->flags & PANE_DROP) return; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL || c->tty.term == NULL) - continue; - if (c->flags & CLIENT_SUSPENDED) - continue; - if (c->tty.flags & TTY_FREEZE) - continue; - if (c->session->curw->window != wp->window) + TAILQ_FOREACH(c, &clients, entry) { + if (!tty_client_ready(c, wp)) continue; ctx->xoff = wp->xoff; @@ -702,19 +750,19 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; if (!tty_pane_full_width(tty, ctx)) { - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_term_has(tty->term, TTYC_ICH) || - tty_term_has(tty->term, TTYC_ICH1)) + if (!tty_fake_bce(tty, wp) && (tty_term_has(tty->term, TTYC_ICH) || + tty_term_has(tty->term, TTYC_ICH1))) tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); else - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); } void @@ -722,14 +770,14 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -743,11 +791,11 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { u_int i; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_term_has(tty->term, TTYC_ECH)) + if (tty_term_has(tty->term, TTYC_ECH) && !tty_fake_bce(tty, ctx->wp)) tty_putcode1(tty, TTYC_ECH, ctx->num); else { for (i = 0; i < ctx->num; i++) @@ -758,14 +806,14 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -776,14 +824,14 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -797,11 +845,12 @@ tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, 0, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) + if (tty_pane_full_width(tty, ctx) && !tty_fake_bce(tty, wp) && + tty_term_has(tty->term, TTYC_EL)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s)); @@ -813,11 +862,12 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - ctx->ocx); @@ -826,9 +876,10 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); - if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) { + if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1) && + !tty_fake_bce(tty, ctx->wp)) { tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putcode(tty, TTYC_EL1); } else { @@ -843,14 +894,14 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orupper) return; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_RI)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); @@ -866,7 +917,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orlower) return; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) || !tty_term_has(tty->term, TTYC_CSR)) { if (tty_large_region(tty, ctx)) wp->flags |= PANE_REDRAW; @@ -883,7 +934,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP)) return; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -898,12 +949,13 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { tty_putcode(tty, TTYC_EL); if (ctx->ocy != screen_size_y(s) - 1) { tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); @@ -931,12 +983,13 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { for (i = 0; i < ctx->ocy; i++) { tty_putcode(tty, TTYC_EL); tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); @@ -958,12 +1011,13 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { for (i = 0; i < screen_size_y(s); i++) { tty_putcode(tty, TTYC_EL); if (i != screen_size_y(s) - 1) { @@ -986,7 +1040,7 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); @@ -1008,7 +1062,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); /* Is the cursor in the very last position? */ - width = grid_cell_width(ctx->cell); + width = ctx->cell->data.width; if (ctx->ocx > wp->sx - width) { if (ctx->xoff != 0 || wp->sx != tty->sx) { /* @@ -1025,14 +1079,14 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) * move as far left as possible and redraw the last * cell to move into the last position. */ - cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell); + cx = screen_size_x(s) - ctx->last_cell.data.width; tty_cursor_pane(tty, ctx, cx, ctx->ocy); - tty_cell(tty, &ctx->last_cell); + tty_cell(tty, &ctx->last_cell, wp); } } else tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell); + tty_cell(tty, ctx->cell, wp); } void @@ -1044,7 +1098,7 @@ tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx) * Cannot rely on not being a partial character, so just redraw the * whole line. */ - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); } void @@ -1077,15 +1131,15 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) tty->cx = tty->cy = UINT_MAX; tty->rupper = tty->rlower = UINT_MAX; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor(tty, 0, 0); } void -tty_cell(struct tty *tty, const struct grid_cell *gc) +tty_cell(struct tty *tty, const struct grid_cell *gc, + const struct window_pane *wp) { - struct utf8_data ud; - u_int i; + u_int i; /* Skip last character if terminal is stupid. */ if (tty->term->flags & TERM_EARLYWRAP && @@ -1097,26 +1151,25 @@ tty_cell(struct tty *tty, const struct grid_cell *gc) return; /* Set the attributes. */ - tty_attributes(tty, gc); + tty_attributes(tty, gc, wp); /* Get the cell and if ASCII write with putc to do ACS translation. */ - grid_cell_get(gc, &ud); - if (ud.size == 1) { - if (*ud.data < 0x20 || *ud.data == 0x7f) + if (gc->data.size == 1) { + if (*gc->data.data < 0x20 || *gc->data.data == 0x7f) return; - tty_putc(tty, *ud.data); + tty_putc(tty, *gc->data.data); return; } /* If not UTF-8, write _. */ if (!(tty->flags & TTY_UTF8)) { - for (i = 0; i < ud.width; i++) + for (i = 0; i < gc->data.width; i++) tty_putc(tty, '_'); return; } /* Write the data. */ - tty_putn(tty, ud.data, ud.size, ud.width); + tty_putn(tty, gc->data.data, gc->data.size, gc->data.width); } void @@ -1135,8 +1188,8 @@ tty_reset(struct tty *tty) /* Set region inside pane. */ void -tty_region_pane( - struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) +tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, + u_int rlower) { tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower); } @@ -1301,12 +1354,14 @@ out: } void -tty_attributes(struct tty *tty, const struct grid_cell *gc) +tty_attributes(struct tty *tty, const struct grid_cell *gc, + const struct window_pane *wp) { struct grid_cell *tc = &tty->cell, gc2; u_char changed; memcpy(&gc2, gc, sizeof gc2); + tty_default_colours(&gc2, wp); /* * If no setab, try to use the reverse attribute as a best-effort for a @@ -1347,12 +1402,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc) if (changed & GRID_ATTR_DIM) tty_putcode(tty, TTYC_DIM); if (changed & GRID_ATTR_ITALICS) - { - if (tty_term_has(tty->term, TTYC_SITM)) - tty_putcode(tty, TTYC_SITM); - else - tty_putcode(tty, TTYC_SMSO); - } + tty_set_italics(tty); if (changed & GRID_ATTR_UNDERSCORE) tty_putcode(tty, TTYC_SMUL); if (changed & GRID_ATTR_BLINK) @@ -1397,7 +1447,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) * * Otherwise, try to set the default colour only as needed. */ - have_ax = tty_term_has(tty->term, TTYC_AX); + have_ax = tty_term_flag(tty->term, TTYC_AX); if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { @@ -1443,17 +1493,20 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) { u_int colours; + colours = tty_term_number(tty->term, TTYC_COLORS); + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { /* And not a 256 colour mode? */ - if (!(tty->term->flags & TERM_88COLOURS) && - !(tty->term_flags & TERM_88COLOURS) && - !(tty->term->flags & TERM_256COLOURS) && + if (!(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->fg = colour_256to16(gc->fg); if (gc->fg & 8) { gc->fg &= 7; - gc->attr |= GRID_ATTR_BRIGHT; + if (colours >= 16) + gc->fg += 90; + else + gc->attr |= GRID_ATTR_BRIGHT; } else gc->attr &= ~GRID_ATTR_BRIGHT; gc->flags &= ~GRID_FLAG_FG256; @@ -1462,7 +1515,6 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) } /* Is this an aixterm colour? */ - colours = tty_term_number(tty->term, TTYC_COLORS); if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { gc->fg -= 90; gc->attr |= GRID_ATTR_BRIGHT; @@ -1474,6 +1526,8 @@ tty_check_bg(struct tty *tty, struct grid_cell *gc) { u_int colours; + colours = tty_term_number(tty->term, TTYC_COLORS); + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { /* @@ -1481,25 +1535,22 @@ tty_check_bg(struct tty *tty, struct grid_cell *gc) * palette. Bold background doesn't exist portably, so just * discard the bold bit if set. */ - if (!(tty->term->flags & TERM_88COLOURS) && - !(tty->term_flags & TERM_88COLOURS) && - !(tty->term->flags & TERM_256COLOURS) && + if (!(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->bg = colour_256to16(gc->bg); - if (gc->bg & 8) + if (gc->bg & 8) { gc->bg &= 7; - gc->attr &= ~GRID_ATTR_BRIGHT; + if (colours >= 16) + gc->fg += 90; + } gc->flags &= ~GRID_FLAG_BG256; } return; } /* Is this an aixterm colour? */ - colours = tty_term_number(tty->term, TTYC_COLORS); - if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) { + if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) gc->bg -= 90; - gc->attr |= GRID_ATTR_BRIGHT; - } } void @@ -1511,11 +1562,9 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { - /* Try as 256 colours or translating to 88. */ + /* Try as 256 colours. */ if (tty_try_256(tty, fg, "38") == 0) goto save_fg; - if (tty_try_88(tty, fg, "38") == 0) - goto save_fg; /* Else already handled by tty_check_fg. */ return; } @@ -1546,25 +1595,18 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { - /* Try as 256 colours or translating to 88. */ + /* Try as 256 colours. */ if (tty_try_256(tty, bg, "48") == 0) goto save_bg; - if (tty_try_88(tty, bg, "48") == 0) - goto save_bg; /* Else already handled by tty_check_bg. */ return; } /* Is this an aixterm bright colour? */ if (bg >= 90 && bg <= 97) { - /* 16 colour terminals or above only. */ - if (tty_term_number(tty->term, TTYC_COLORS) >= 16) { - xsnprintf(s, sizeof s, "\033[%dm", bg + 10); - tty_puts(tty, s); - goto save_bg; - } - bg -= 90; - /* no such thing as a bold background */ + xsnprintf(s, sizeof s, "\033[%dm", bg + 10); + tty_puts(tty, s); + goto save_bg; } /* Otherwise set the background colour. */ @@ -1582,32 +1624,75 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) { char s[32]; - if (!(tty->term->flags & TERM_256COLOURS) && - !(tty->term_flags & TERM_256COLOURS)) - return (-1); + /* + * If the user has specified -2 to the client, setaf and setab may not + * work (or they may not want to use them), so send the usual sequence. + */ + if (tty->term_flags & TERM_256COLOURS) + goto fallback; - xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); - tty_puts(tty, s); - return (0); -} + /* + * If the terminfo entry has 256 colours and setaf and setab exist, + * assume that they work correctly. + */ + if (tty->term->flags & TERM_256COLOURS) { + if (*type == '3') { + if (!tty_term_has(tty->term, TTYC_SETAF)) + goto fallback; + tty_putcode1(tty, TTYC_SETAF, colour); + } else { + if (!tty_term_has(tty->term, TTYC_SETAB)) + goto fallback; + tty_putcode1(tty, TTYC_SETAB, colour); + } + return (0); + } -int -tty_try_88(struct tty *tty, u_char colour, const char *type) -{ - char s[32]; - - if (!(tty->term->flags & TERM_88COLOURS) && - !(tty->term_flags & TERM_88COLOURS)) - return (-1); - colour = colour_256to88(colour); + return (-1); +fallback: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); tty_puts(tty, s); return (0); } void -tty_bell(struct tty *tty) +tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) { - tty_putcode(tty, TTYC_BEL); + const struct grid_cell *agc, *pgc, *wgc; + + if (wp == NULL) + return; + + pgc = &wp->colgc; + agc = options_get_style(wp->window->options, "window-active-style"); + wgc = options_get_style(wp->window->options, "window-style"); + + if (gc->fg == 8 && !(gc->flags & GRID_FLAG_FG256)) { + if (pgc->fg != 8 || (pgc->flags & GRID_FLAG_FG256)) { + gc->fg = pgc->fg; + gc->flags |= (pgc->flags & GRID_FLAG_FG256); + } else if (wp == wp->window->active && + (agc->fg != 8 || (agc->flags & GRID_FLAG_FG256))) { + gc->fg = agc->fg; + gc->flags |= (agc->flags & GRID_FLAG_FG256); + } else { + gc->fg = wgc->fg; + gc->flags |= (wgc->flags & GRID_FLAG_FG256); + } + } + + if (gc->bg == 8 && !(gc->flags & GRID_FLAG_BG256)) { + if (pgc->bg != 8 || (pgc->flags & GRID_FLAG_BG256)) { + gc->bg = pgc->bg; + gc->flags |= (pgc->flags & GRID_FLAG_BG256); + } else if (wp == wp->window->active && + (agc->bg != 8 || (agc->flags & GRID_FLAG_BG256))) { + gc->bg = agc->bg; + gc->flags |= (agc->flags & GRID_FLAG_BG256); + } else { + gc->bg = wgc->bg; + gc->flags |= (wgc->flags & GRID_FLAG_BG256); + } + } } diff --git a/utf8.c b/utf8.c index 88d847a6..ad99edce 100644 --- a/utf8.c +++ b/utf8.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -18,6 +18,7 @@ #include +#include #include #include "tmux.h" @@ -32,174 +33,347 @@ struct utf8_width_entry { struct utf8_width_entry *right; }; -/* Random order. Not optimal but it'll do for now... */ -struct utf8_width_entry utf8_width_table[] = { - { 0x00951, 0x00954, 0, NULL, NULL }, - { 0x00ccc, 0x00ccd, 0, NULL, NULL }, - { 0x0fff9, 0x0fffb, 0, NULL, NULL }, - { 0x20000, 0x2fffd, 2, NULL, NULL }, - { 0x00ebb, 0x00ebc, 0, NULL, NULL }, - { 0x01932, 0x01932, 0, NULL, NULL }, - { 0x0070f, 0x0070f, 0, NULL, NULL }, - { 0x00a70, 0x00a71, 0, NULL, NULL }, - { 0x02329, 0x02329, 2, NULL, NULL }, - { 0x00acd, 0x00acd, 0, NULL, NULL }, - { 0x00ac7, 0x00ac8, 0, NULL, NULL }, - { 0x00a3c, 0x00a3c, 0, NULL, NULL }, - { 0x009cd, 0x009cd, 0, NULL, NULL }, - { 0x00591, 0x005bd, 0, NULL, NULL }, - { 0x01058, 0x01059, 0, NULL, NULL }, - { 0x0ffe0, 0x0ffe6, 2, NULL, NULL }, - { 0x01100, 0x0115f, 2, NULL, NULL }, - { 0x0fe20, 0x0fe23, 0, NULL, NULL }, - { 0x0302a, 0x0302f, 0, NULL, NULL }, - { 0x01772, 0x01773, 0, NULL, NULL }, - { 0x005bf, 0x005bf, 0, NULL, NULL }, - { 0x006ea, 0x006ed, 0, NULL, NULL }, - { 0x00bc0, 0x00bc0, 0, NULL, NULL }, - { 0x00962, 0x00963, 0, NULL, NULL }, - { 0x01732, 0x01734, 0, NULL, NULL }, - { 0x00d41, 0x00d43, 0, NULL, NULL }, - { 0x01b42, 0x01b42, 0, NULL, NULL }, - { 0x00a41, 0x00a42, 0, NULL, NULL }, - { 0x00eb4, 0x00eb9, 0, NULL, NULL }, - { 0x00b01, 0x00b01, 0, NULL, NULL }, - { 0x00e34, 0x00e3a, 0, NULL, NULL }, - { 0x03040, 0x03098, 2, NULL, NULL }, - { 0x0093c, 0x0093c, 0, NULL, NULL }, - { 0x00c4a, 0x00c4d, 0, NULL, NULL }, - { 0x01032, 0x01032, 0, NULL, NULL }, - { 0x00f37, 0x00f37, 0, NULL, NULL }, - { 0x00901, 0x00902, 0, NULL, NULL }, - { 0x00cbf, 0x00cbf, 0, NULL, NULL }, - { 0x0a806, 0x0a806, 0, NULL, NULL }, - { 0x00dd2, 0x00dd4, 0, NULL, NULL }, - { 0x00f71, 0x00f7e, 0, NULL, NULL }, - { 0x01752, 0x01753, 0, NULL, NULL }, - { 0x1d242, 0x1d244, 0, NULL, NULL }, - { 0x005c1, 0x005c2, 0, NULL, NULL }, - { 0x0309b, 0x0a4cf, 2, NULL, NULL }, - { 0xe0100, 0xe01ef, 0, NULL, NULL }, - { 0x017dd, 0x017dd, 0, NULL, NULL }, - { 0x00600, 0x00603, 0, NULL, NULL }, - { 0x009e2, 0x009e3, 0, NULL, NULL }, - { 0x00cc6, 0x00cc6, 0, NULL, NULL }, - { 0x0a80b, 0x0a80b, 0, NULL, NULL }, - { 0x01712, 0x01714, 0, NULL, NULL }, - { 0x00b3c, 0x00b3c, 0, NULL, NULL }, - { 0x01b00, 0x01b03, 0, NULL, NULL }, - { 0x007eb, 0x007f3, 0, NULL, NULL }, - { 0xe0001, 0xe0001, 0, NULL, NULL }, - { 0x1d185, 0x1d18b, 0, NULL, NULL }, - { 0x0feff, 0x0feff, 0, NULL, NULL }, - { 0x01b36, 0x01b3a, 0, NULL, NULL }, - { 0x01920, 0x01922, 0, NULL, NULL }, - { 0x00670, 0x00670, 0, NULL, NULL }, - { 0x00f90, 0x00f97, 0, NULL, NULL }, - { 0x01927, 0x01928, 0, NULL, NULL }, - { 0x0200b, 0x0200f, 0, NULL, NULL }, - { 0x0ff00, 0x0ff60, 2, NULL, NULL }, - { 0x0f900, 0x0faff, 2, NULL, NULL }, - { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, - { 0x00cbc, 0x00cbc, 0, NULL, NULL }, - { 0x00eb1, 0x00eb1, 0, NULL, NULL }, - { 0x10a38, 0x10a3a, 0, NULL, NULL }, - { 0x007a6, 0x007b0, 0, NULL, NULL }, - { 0x00f80, 0x00f84, 0, NULL, NULL }, +/* Sorted, then repeatedly split in the middle to balance the tree. */ +static struct utf8_width_entry utf8_width_table[] = { + { 0x00b41, 0x00b44, 0, NULL, NULL }, + { 0x008e4, 0x00902, 0, NULL, NULL }, + { 0x006d6, 0x006dd, 0, NULL, NULL }, { 0x005c4, 0x005c5, 0, NULL, NULL }, - { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, - { 0x017c9, 0x017d3, 0, NULL, NULL }, - { 0x00d4d, 0x00d4d, 0, NULL, NULL }, - { 0x1d167, 0x1d169, 0, NULL, NULL }, - { 0x01036, 0x01037, 0, NULL, NULL }, - { 0xe0020, 0xe007f, 0, NULL, NULL }, - { 0x00f35, 0x00f35, 0, NULL, NULL }, - { 0x017b4, 0x017b5, 0, NULL, NULL }, - { 0x0206a, 0x0206f, 0, NULL, NULL }, - { 0x00c46, 0x00c48, 0, NULL, NULL }, - { 0x01939, 0x0193b, 0, NULL, NULL }, - { 0x01dc0, 0x01dca, 0, NULL, NULL }, - { 0x10a0c, 0x10a0f, 0, NULL, NULL }, - { 0x0102d, 0x01030, 0, NULL, NULL }, - { 0x017c6, 0x017c6, 0, NULL, NULL }, - { 0x00ec8, 0x00ecd, 0, NULL, NULL }, - { 0x00b41, 0x00b43, 0, NULL, NULL }, - { 0x017b7, 0x017bd, 0, NULL, NULL }, - { 0x1d173, 0x1d182, 0, NULL, NULL }, - { 0x00a47, 0x00a48, 0, NULL, NULL }, - { 0x0232a, 0x0232a, 2, NULL, NULL }, - { 0x01b3c, 0x01b3c, 0, NULL, NULL }, - { 0x10a01, 0x10a03, 0, NULL, NULL }, - { 0x00ae2, 0x00ae3, 0, NULL, NULL }, - { 0x00483, 0x00486, 0, NULL, NULL }, - { 0x0135f, 0x0135f, 0, NULL, NULL }, - { 0x01a17, 0x01a18, 0, NULL, NULL }, - { 0x006e7, 0x006e8, 0, NULL, NULL }, -#ifndef __APPLE__ - { 0x03099, 0x0309a, 0, NULL, NULL }, -#endif - { 0x00b4d, 0x00b4d, 0, NULL, NULL }, - { 0x00ce2, 0x00ce3, 0, NULL, NULL }, - { 0x00bcd, 0x00bcd, 0, NULL, NULL }, - { 0x00610, 0x00615, 0, NULL, NULL }, - { 0x00f99, 0x00fbc, 0, NULL, NULL }, - { 0x009c1, 0x009c4, 0, NULL, NULL }, - { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x00591, 0x005bd, 0, NULL, NULL }, { 0x00300, 0x0036f, 0, NULL, NULL }, - { 0x03030, 0x0303e, 2, NULL, NULL }, - { 0x01b34, 0x01b34, 0, NULL, NULL }, - { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, - { 0x00dca, 0x00dca, 0, NULL, NULL }, - { 0x006d6, 0x006e4, 0, NULL, NULL }, - { 0x00f86, 0x00f87, 0, NULL, NULL }, - { 0x00b3f, 0x00b3f, 0, NULL, NULL }, - { 0x0fe30, 0x0fe6f, 2, NULL, NULL }, - { 0x01039, 0x01039, 0, NULL, NULL }, - { 0x0094d, 0x0094d, 0, NULL, NULL }, - { 0x00c55, 0x00c56, 0, NULL, NULL }, - { 0x00488, 0x00489, 0, NULL, NULL }, - { 0x00e47, 0x00e4e, 0, NULL, NULL }, - { 0x00a81, 0x00a82, 0, NULL, NULL }, - { 0x00ac1, 0x00ac5, 0, NULL, NULL }, - { 0x0202a, 0x0202e, 0, NULL, NULL }, - { 0x00dd6, 0x00dd6, 0, NULL, NULL }, - { 0x018a9, 0x018a9, 0, NULL, NULL }, - { 0x0064b, 0x0065e, 0, NULL, NULL }, - { 0x00abc, 0x00abc, 0, NULL, NULL }, - { 0x00b82, 0x00b82, 0, NULL, NULL }, - { 0x00f39, 0x00f39, 0, NULL, NULL }, - { 0x020d0, 0x020ef, 0, NULL, NULL }, - { 0x01dfe, 0x01dff, 0, NULL, NULL }, - { 0x30000, 0x3fffd, 2, NULL, NULL }, - { 0x00711, 0x00711, 0, NULL, NULL }, - { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, - { 0x01160, 0x011ff, 0, NULL, NULL }, - { 0x0180b, 0x0180d, 0, NULL, NULL }, - { 0x10a3f, 0x10a3f, 0, NULL, NULL }, - { 0x00981, 0x00981, 0, NULL, NULL }, - { 0x0a825, 0x0a826, 0, NULL, NULL }, - { 0x00941, 0x00948, 0, NULL, NULL }, - { 0x01b6b, 0x01b73, 0, NULL, NULL }, - { 0x00e31, 0x00e31, 0, NULL, NULL }, - { 0x0fe10, 0x0fe19, 2, NULL, NULL }, - { 0x00a01, 0x00a02, 0, NULL, NULL }, - { 0x00a4b, 0x00a4d, 0, NULL, NULL }, - { 0x00f18, 0x00f19, 0, NULL, NULL }, - { 0x00fc6, 0x00fc6, 0, NULL, NULL }, - { 0x02e80, 0x03029, 2, NULL, NULL }, - { 0x00b56, 0x00b56, 0, NULL, NULL }, - { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x00483, 0x00489, 0, NULL, NULL }, + { 0x005bf, 0x005bf, 0, NULL, NULL }, + { 0x005c1, 0x005c2, 0, NULL, NULL }, + { 0x00610, 0x0061a, 0, NULL, NULL }, + { 0x00600, 0x00605, 0, NULL, NULL }, { 0x005c7, 0x005c7, 0, NULL, NULL }, - { 0x02060, 0x02063, 0, NULL, NULL }, + { 0x0064b, 0x0065f, 0, NULL, NULL }, + { 0x0061c, 0x0061c, 0, NULL, NULL }, + { 0x00670, 0x00670, 0, NULL, NULL }, + { 0x007a6, 0x007b0, 0, NULL, NULL }, + { 0x006ea, 0x006ed, 0, NULL, NULL }, + { 0x006df, 0x006e4, 0, NULL, NULL }, + { 0x006e7, 0x006e8, 0, NULL, NULL }, + { 0x00711, 0x00711, 0, NULL, NULL }, + { 0x0070f, 0x0070f, 0, NULL, NULL }, + { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x0081b, 0x00823, 0, NULL, NULL }, + { 0x007eb, 0x007f3, 0, NULL, NULL }, + { 0x00816, 0x00819, 0, NULL, NULL }, + { 0x00829, 0x0082d, 0, NULL, NULL }, + { 0x00825, 0x00827, 0, NULL, NULL }, + { 0x00859, 0x0085b, 0, NULL, NULL }, + { 0x00a41, 0x00a42, 0, NULL, NULL }, + { 0x00981, 0x00981, 0, NULL, NULL }, + { 0x00941, 0x00948, 0, NULL, NULL }, + { 0x0093a, 0x0093a, 0, NULL, NULL }, + { 0x0093c, 0x0093c, 0, NULL, NULL }, + { 0x00951, 0x00957, 0, NULL, NULL }, + { 0x0094d, 0x0094d, 0, NULL, NULL }, + { 0x00962, 0x00963, 0, NULL, NULL }, + { 0x009e2, 0x009e3, 0, NULL, NULL }, + { 0x009c1, 0x009c4, 0, NULL, NULL }, + { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x009cd, 0x009cd, 0, NULL, NULL }, + { 0x00a01, 0x00a02, 0, NULL, NULL }, + { 0x00a3c, 0x00a3c, 0, NULL, NULL }, + { 0x00ac1, 0x00ac5, 0, NULL, NULL }, + { 0x00a70, 0x00a71, 0, NULL, NULL }, + { 0x00a4b, 0x00a4d, 0, NULL, NULL }, + { 0x00a47, 0x00a48, 0, NULL, NULL }, + { 0x00a51, 0x00a51, 0, NULL, NULL }, + { 0x00a81, 0x00a82, 0, NULL, NULL }, + { 0x00a75, 0x00a75, 0, NULL, NULL }, + { 0x00abc, 0x00abc, 0, NULL, NULL }, + { 0x00ae2, 0x00ae3, 0, NULL, NULL }, + { 0x00ac7, 0x00ac8, 0, NULL, NULL }, + { 0x00acd, 0x00acd, 0, NULL, NULL }, + { 0x00b3c, 0x00b3c, 0, NULL, NULL }, + { 0x00b01, 0x00b01, 0, NULL, NULL }, + { 0x00b3f, 0x00b3f, 0, NULL, NULL }, + { 0x03190, 0x031ba, 2, NULL, NULL }, + { 0x017c9, 0x017d3, 0, NULL, NULL }, + { 0x00ec8, 0x00ecd, 0, NULL, NULL }, + { 0x00cc6, 0x00cc6, 0, NULL, NULL }, { 0x00c3e, 0x00c40, 0, NULL, NULL }, + { 0x00b82, 0x00b82, 0, NULL, NULL }, + { 0x00b56, 0x00b56, 0, NULL, NULL }, + { 0x00b4d, 0x00b4d, 0, NULL, NULL }, + { 0x00b62, 0x00b63, 0, NULL, NULL }, + { 0x00bcd, 0x00bcd, 0, NULL, NULL }, + { 0x00bc0, 0x00bc0, 0, NULL, NULL }, + { 0x00c00, 0x00c00, 0, NULL, NULL }, + { 0x00c62, 0x00c63, 0, NULL, NULL }, + { 0x00c4a, 0x00c4d, 0, NULL, NULL }, + { 0x00c46, 0x00c48, 0, NULL, NULL }, + { 0x00c55, 0x00c56, 0, NULL, NULL }, + { 0x00cbc, 0x00cbc, 0, NULL, NULL }, + { 0x00c81, 0x00c81, 0, NULL, NULL }, + { 0x00cbf, 0x00cbf, 0, NULL, NULL }, + { 0x00dd2, 0x00dd4, 0, NULL, NULL }, + { 0x00d41, 0x00d44, 0, NULL, NULL }, + { 0x00ce2, 0x00ce3, 0, NULL, NULL }, + { 0x00ccc, 0x00ccd, 0, NULL, NULL }, + { 0x00d01, 0x00d01, 0, NULL, NULL }, + { 0x00d62, 0x00d63, 0, NULL, NULL }, + { 0x00d4d, 0x00d4d, 0, NULL, NULL }, + { 0x00dca, 0x00dca, 0, NULL, NULL }, + { 0x00e47, 0x00e4e, 0, NULL, NULL }, + { 0x00e31, 0x00e31, 0, NULL, NULL }, + { 0x00dd6, 0x00dd6, 0, NULL, NULL }, + { 0x00e34, 0x00e3a, 0, NULL, NULL }, + { 0x00eb4, 0x00eb9, 0, NULL, NULL }, + { 0x00eb1, 0x00eb1, 0, NULL, NULL }, + { 0x00ebb, 0x00ebc, 0, NULL, NULL }, + { 0x0105e, 0x01060, 0, NULL, NULL }, + { 0x00f8d, 0x00f97, 0, NULL, NULL }, + { 0x00f39, 0x00f39, 0, NULL, NULL }, + { 0x00f35, 0x00f35, 0, NULL, NULL }, + { 0x00f18, 0x00f19, 0, NULL, NULL }, + { 0x00f37, 0x00f37, 0, NULL, NULL }, + { 0x00f80, 0x00f84, 0, NULL, NULL }, + { 0x00f71, 0x00f7e, 0, NULL, NULL }, + { 0x00f86, 0x00f87, 0, NULL, NULL }, + { 0x01032, 0x01037, 0, NULL, NULL }, + { 0x00fc6, 0x00fc6, 0, NULL, NULL }, + { 0x00f99, 0x00fbc, 0, NULL, NULL }, + { 0x0102d, 0x01030, 0, NULL, NULL }, + { 0x0103d, 0x0103e, 0, NULL, NULL }, + { 0x01039, 0x0103a, 0, NULL, NULL }, + { 0x01058, 0x01059, 0, NULL, NULL }, + { 0x0135d, 0x0135f, 0, NULL, NULL }, + { 0x01085, 0x01086, 0, NULL, NULL }, + { 0x01071, 0x01074, 0, NULL, NULL }, + { 0x01082, 0x01082, 0, NULL, NULL }, + { 0x0109d, 0x0109d, 0, NULL, NULL }, + { 0x0108d, 0x0108d, 0, NULL, NULL }, + { 0x01100, 0x011ff, 2, NULL, NULL }, + { 0x01772, 0x01773, 0, NULL, NULL }, + { 0x01732, 0x01734, 0, NULL, NULL }, + { 0x01712, 0x01714, 0, NULL, NULL }, + { 0x01752, 0x01753, 0, NULL, NULL }, + { 0x017b7, 0x017bd, 0, NULL, NULL }, + { 0x017b4, 0x017b5, 0, NULL, NULL }, + { 0x017c6, 0x017c6, 0, NULL, NULL }, + { 0x01c2c, 0x01c33, 0, NULL, NULL }, + { 0x01a7f, 0x01a7f, 0, NULL, NULL }, + { 0x01a17, 0x01a18, 0, NULL, NULL }, + { 0x01920, 0x01922, 0, NULL, NULL }, + { 0x0180b, 0x0180e, 0, NULL, NULL }, + { 0x017dd, 0x017dd, 0, NULL, NULL }, + { 0x018a9, 0x018a9, 0, NULL, NULL }, + { 0x01932, 0x01932, 0, NULL, NULL }, + { 0x01927, 0x01928, 0, NULL, NULL }, + { 0x01939, 0x0193b, 0, NULL, NULL }, + { 0x01a60, 0x01a60, 0, NULL, NULL }, + { 0x01a56, 0x01a56, 0, NULL, NULL }, + { 0x01a1b, 0x01a1b, 0, NULL, NULL }, + { 0x01a58, 0x01a5e, 0, NULL, NULL }, + { 0x01a65, 0x01a6c, 0, NULL, NULL }, + { 0x01a62, 0x01a62, 0, NULL, NULL }, + { 0x01a73, 0x01a7c, 0, NULL, NULL }, + { 0x01b80, 0x01b81, 0, NULL, NULL }, + { 0x01b36, 0x01b3a, 0, NULL, NULL }, + { 0x01b00, 0x01b03, 0, NULL, NULL }, + { 0x01ab0, 0x01abe, 0, NULL, NULL }, + { 0x01b34, 0x01b34, 0, NULL, NULL }, + { 0x01b42, 0x01b42, 0, NULL, NULL }, + { 0x01b3c, 0x01b3c, 0, NULL, NULL }, + { 0x01b6b, 0x01b73, 0, NULL, NULL }, + { 0x01be6, 0x01be6, 0, NULL, NULL }, + { 0x01ba8, 0x01ba9, 0, NULL, NULL }, + { 0x01ba2, 0x01ba5, 0, NULL, NULL }, + { 0x01bab, 0x01bad, 0, NULL, NULL }, + { 0x01bed, 0x01bed, 0, NULL, NULL }, + { 0x01be8, 0x01be9, 0, NULL, NULL }, + { 0x01bef, 0x01bf1, 0, NULL, NULL }, + { 0x02329, 0x0232a, 2, NULL, NULL }, + { 0x01dc0, 0x01df5, 0, NULL, NULL }, + { 0x01ce2, 0x01ce8, 0, NULL, NULL }, + { 0x01cd0, 0x01cd2, 0, NULL, NULL }, + { 0x01c36, 0x01c37, 0, NULL, NULL }, + { 0x01cd4, 0x01ce0, 0, NULL, NULL }, + { 0x01cf4, 0x01cf4, 0, NULL, NULL }, + { 0x01ced, 0x01ced, 0, NULL, NULL }, + { 0x01cf8, 0x01cf9, 0, NULL, NULL }, + { 0x02060, 0x02064, 0, NULL, NULL }, + { 0x0200b, 0x0200f, 0, NULL, NULL }, + { 0x01dfc, 0x01dff, 0, NULL, NULL }, + { 0x0202a, 0x0202e, 0, NULL, NULL }, + { 0x02066, 0x0206f, 0, NULL, NULL }, + { 0x020d0, 0x020f0, 0, NULL, NULL }, + { 0x03001, 0x03029, 2, NULL, NULL }, + { 0x02e80, 0x02e99, 2, NULL, NULL }, + { 0x02d7f, 0x02d7f, 0, NULL, NULL }, + { 0x02cef, 0x02cf1, 0, NULL, NULL }, + { 0x02de0, 0x02dff, 0, NULL, NULL }, + { 0x02f00, 0x02fd5, 2, NULL, NULL }, + { 0x02e9b, 0x02ef3, 2, NULL, NULL }, + { 0x02ff0, 0x02ffb, 2, NULL, NULL }, + { 0x03099, 0x0309a, 0, NULL, NULL }, + { 0x0302e, 0x0303e, 2, NULL, NULL }, + { 0x0302a, 0x0302d, 0, NULL, NULL }, + { 0x03041, 0x03096, 2, NULL, NULL }, + { 0x03105, 0x0312d, 2, NULL, NULL }, + { 0x0309b, 0x030ff, 2, NULL, NULL }, + { 0x03131, 0x0318e, 2, NULL, NULL }, + { 0x10a3f, 0x10a3f, 0, NULL, NULL }, + { 0x0aa4c, 0x0aa4c, 0, NULL, NULL }, + { 0x0a825, 0x0a826, 0, NULL, NULL }, + { 0x0a490, 0x0a4c6, 2, NULL, NULL }, + { 0x03250, 0x032fe, 2, NULL, NULL }, + { 0x031f0, 0x0321e, 2, NULL, NULL }, + { 0x031c0, 0x031e3, 2, NULL, NULL }, + { 0x03220, 0x03247, 2, NULL, NULL }, + { 0x04e00, 0x09fcc, 2, NULL, NULL }, + { 0x03300, 0x04db5, 2, NULL, NULL }, + { 0x0a000, 0x0a48c, 2, NULL, NULL }, + { 0x0a6f0, 0x0a6f1, 0, NULL, NULL }, + { 0x0a674, 0x0a67d, 0, NULL, NULL }, + { 0x0a66f, 0x0a672, 0, NULL, NULL }, + { 0x0a69f, 0x0a69f, 0, NULL, NULL }, + { 0x0a806, 0x0a806, 0, NULL, NULL }, + { 0x0a802, 0x0a802, 0, NULL, NULL }, + { 0x0a80b, 0x0a80b, 0, NULL, NULL }, + { 0x0a9b6, 0x0a9b9, 0, NULL, NULL }, + { 0x0a947, 0x0a951, 0, NULL, NULL }, + { 0x0a8e0, 0x0a8f1, 0, NULL, NULL }, + { 0x0a8c4, 0x0a8c4, 0, NULL, NULL }, + { 0x0a926, 0x0a92d, 0, NULL, NULL }, + { 0x0a980, 0x0a982, 0, NULL, NULL }, + { 0x0a960, 0x0a97c, 2, NULL, NULL }, + { 0x0a9b3, 0x0a9b3, 0, NULL, NULL }, + { 0x0aa29, 0x0aa2e, 0, NULL, NULL }, + { 0x0a9bc, 0x0a9bc, 0, NULL, NULL }, + { 0x0a9e5, 0x0a9e5, 0, NULL, NULL }, + { 0x0aa35, 0x0aa36, 0, NULL, NULL }, + { 0x0aa31, 0x0aa32, 0, NULL, NULL }, + { 0x0aa43, 0x0aa43, 0, NULL, NULL }, + { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, + { 0x0aaf6, 0x0aaf6, 0, NULL, NULL }, + { 0x0aab7, 0x0aab8, 0, NULL, NULL }, + { 0x0aab0, 0x0aab0, 0, NULL, NULL }, + { 0x0aa7c, 0x0aa7c, 0, NULL, NULL }, + { 0x0aab2, 0x0aab4, 0, NULL, NULL }, + { 0x0aac1, 0x0aac1, 0, NULL, NULL }, + { 0x0aabe, 0x0aabf, 0, NULL, NULL }, + { 0x0aaec, 0x0aaed, 0, NULL, NULL }, + { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, + { 0x0abe8, 0x0abe8, 0, NULL, NULL }, + { 0x0abe5, 0x0abe5, 0, NULL, NULL }, + { 0x0abed, 0x0abed, 0, NULL, NULL }, + { 0x0f900, 0x0fa6d, 2, NULL, NULL }, + { 0x0d800, 0x0dfff, 0, NULL, NULL }, + { 0x0fa70, 0x0fad9, 2, NULL, NULL }, + { 0x0fff9, 0x0fffb, 0, NULL, NULL }, + { 0x0fe30, 0x0fe52, 2, NULL, NULL }, + { 0x0fe10, 0x0fe19, 2, NULL, NULL }, + { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, + { 0x0fe20, 0x0fe2d, 0, NULL, NULL }, + { 0x0fe68, 0x0fe6b, 2, NULL, NULL }, + { 0x0fe54, 0x0fe66, 2, NULL, NULL }, + { 0x0feff, 0x0feff, 0, NULL, NULL }, + { 0x10a01, 0x10a03, 0, NULL, NULL }, + { 0x102e0, 0x102e0, 0, NULL, NULL }, + { 0x101fd, 0x101fd, 0, NULL, NULL }, + { 0x10376, 0x1037a, 0, NULL, NULL }, + { 0x10a0c, 0x10a0f, 0, NULL, NULL }, { 0x10a05, 0x10a06, 0, NULL, NULL }, + { 0x10a38, 0x10a3a, 0, NULL, NULL }, + { 0x11633, 0x1163a, 0, NULL, NULL }, + { 0x11236, 0x11237, 0, NULL, NULL }, + { 0x11100, 0x11102, 0, NULL, NULL }, + { 0x1107f, 0x11081, 0, NULL, NULL }, + { 0x11001, 0x11001, 0, NULL, NULL }, + { 0x10ae5, 0x10ae6, 0, NULL, NULL }, + { 0x11038, 0x11046, 0, NULL, NULL }, + { 0x110b9, 0x110ba, 0, NULL, NULL }, + { 0x110b3, 0x110b6, 0, NULL, NULL }, + { 0x110bd, 0x110bd, 0, NULL, NULL }, + { 0x11180, 0x11181, 0, NULL, NULL }, + { 0x1112d, 0x11134, 0, NULL, NULL }, + { 0x11127, 0x1112b, 0, NULL, NULL }, + { 0x11173, 0x11173, 0, NULL, NULL }, + { 0x1122f, 0x11231, 0, NULL, NULL }, + { 0x111b6, 0x111be, 0, NULL, NULL }, + { 0x11234, 0x11234, 0, NULL, NULL }, + { 0x11370, 0x11374, 0, NULL, NULL }, + { 0x11301, 0x11301, 0, NULL, NULL }, + { 0x112df, 0x112df, 0, NULL, NULL }, + { 0x112e3, 0x112ea, 0, NULL, NULL }, + { 0x11340, 0x11340, 0, NULL, NULL }, + { 0x1133c, 0x1133c, 0, NULL, NULL }, + { 0x11366, 0x1136c, 0, NULL, NULL }, + { 0x114c2, 0x114c3, 0, NULL, NULL }, + { 0x114ba, 0x114ba, 0, NULL, NULL }, + { 0x114b3, 0x114b8, 0, NULL, NULL }, + { 0x114bf, 0x114c0, 0, NULL, NULL }, + { 0x115bc, 0x115bd, 0, NULL, NULL }, + { 0x115b2, 0x115b5, 0, NULL, NULL }, + { 0x115bf, 0x115c0, 0, NULL, NULL }, + { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, + { 0x16b30, 0x16b36, 0, NULL, NULL }, + { 0x116ad, 0x116ad, 0, NULL, NULL }, + { 0x1163f, 0x11640, 0, NULL, NULL }, + { 0x1163d, 0x1163d, 0, NULL, NULL }, + { 0x116ab, 0x116ab, 0, NULL, NULL }, + { 0x116b7, 0x116b7, 0, NULL, NULL }, + { 0x116b0, 0x116b5, 0, NULL, NULL }, + { 0x16af0, 0x16af4, 0, NULL, NULL }, + { 0x1bca0, 0x1bca3, 0, NULL, NULL }, + { 0x1b000, 0x1b001, 2, NULL, NULL }, + { 0x16f8f, 0x16f92, 0, NULL, NULL }, + { 0x1bc9d, 0x1bc9e, 0, NULL, NULL }, + { 0x1d173, 0x1d182, 0, NULL, NULL }, + { 0x1d167, 0x1d169, 0, NULL, NULL }, + { 0x1d185, 0x1d18b, 0, NULL, NULL }, + { 0x2a700, 0x2b734, 2, NULL, NULL }, + { 0x1f210, 0x1f23a, 2, NULL, NULL }, + { 0x1e8d0, 0x1e8d6, 0, NULL, NULL }, + { 0x1d242, 0x1d244, 0, NULL, NULL }, + { 0x1f200, 0x1f202, 2, NULL, NULL }, + { 0x1f250, 0x1f251, 2, NULL, NULL }, + { 0x1f240, 0x1f248, 2, NULL, NULL }, + { 0x20000, 0x2a6d6, 2, NULL, NULL }, + { 0xe0020, 0xe007f, 0, NULL, NULL }, + { 0x2f800, 0x2fa1d, 2, NULL, NULL }, + { 0x2b740, 0x2b81d, 2, NULL, NULL }, + { 0xe0001, 0xe0001, 0, NULL, NULL }, + { 0xf0000, 0xffffd, 0, NULL, NULL }, + { 0xe0100, 0xe01ef, 0, NULL, NULL }, + { 0x100000, 0x10fffd, 0, NULL, NULL }, }; +static struct utf8_width_entry *utf8_width_root = NULL; -struct utf8_width_entry *utf8_width_root = NULL; +static void utf8_build(void); -int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *); -u_int utf8_combine(const struct utf8_data *); -u_int utf8_width(const struct utf8_data *); +/* Set a single character. */ +void +utf8_set(struct utf8_data *ud, u_char ch) +{ + u_int i; + + *ud->data = ch; + ud->have = 1; + ud->size = 1; + + ud->width = 1; + + for (i = ud->size; i < sizeof ud->data; i++) + ud->data[i] = '\0'; +} + +/* Copy UTF-8 character. */ +void +utf8_copy(struct utf8_data *to, const struct utf8_data *from) +{ + u_int i; + + memcpy(to, from, sizeof *to); + + for (i = to->size; i < sizeof to->data; i++) + to->data[i] = '\0'; +} /* * Open UTF-8 sequence. @@ -207,117 +381,144 @@ u_int utf8_width(const struct utf8_data *); * 11000010-11011111 C2-DF start of 2-byte sequence * 11100000-11101111 E0-EF start of 3-byte sequence * 11110000-11110100 F0-F4 start of 4-byte sequence - * - * Returns 1 if more UTF-8 to come, 0 if not UTF-8. */ -int -utf8_open(struct utf8_data *utf8data, u_char ch) +enum utf8_state +utf8_open(struct utf8_data *ud, u_char ch) { - memset(utf8data, 0, sizeof *utf8data); + memset(ud, 0, sizeof *ud); if (ch >= 0xc2 && ch <= 0xdf) - utf8data->size = 2; + ud->size = 2; else if (ch >= 0xe0 && ch <= 0xef) - utf8data->size = 3; + ud->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) - utf8data->size = 4; + ud->size = 4; else - return (0); - utf8_append(utf8data, ch); - return (1); + return (UTF8_ERROR); + utf8_append(ud, ch); + return (UTF8_MORE); } -/* - * Append character to UTF-8, closing if finished. - * - * Returns 1 if more UTF-8 data to come, 0 if finished. - */ -int -utf8_append(struct utf8_data *utf8data, u_char ch) +/* Append character to UTF-8, closing if finished. */ +enum utf8_state +utf8_append(struct utf8_data *ud, u_char ch) { - if (utf8data->have >= utf8data->size) + if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); - if (utf8data->size > sizeof utf8data->data) + if (ud->size > sizeof ud->data) fatalx("UTF-8 character size too large"); - utf8data->data[utf8data->have++] = ch; - if (utf8data->have != utf8data->size) - return (1); + if (ud->have != 0 && (ch & 0xc0) != 0x80) + ud->width = 0xff; - utf8data->width = utf8_width(utf8data); - return (0); -} + ud->data[ud->have++] = ch; + if (ud->have != ud->size) + return (UTF8_MORE); -/* Check if two width tree entries overlap. */ -int -utf8_overlap( - struct utf8_width_entry *item1, struct utf8_width_entry *item2) -{ - if (item1->first >= item2->first && item1->first <= item2->last) - return (1); - if (item1->last >= item2->first && item1->last <= item2->last) - return (1); - if (item2->first >= item1->first && item2->first <= item1->last) - return (1); - if (item2->last >= item1->first && item2->last <= item1->last) - return (1); - return (0); + if (ud->width == 0xff) + return (UTF8_ERROR); + ud->width = utf8_width(utf8_combine(ud)); + return (UTF8_DONE); } /* Build UTF-8 width tree. */ -void +static void utf8_build(void) { struct utf8_width_entry **ptr, *item, *node; - u_int i, j; + u_int i; for (i = 0; i < nitems(utf8_width_table); i++) { item = &utf8_width_table[i]; - for (j = 0; j < nitems(utf8_width_table); j++) { - if (i != j && utf8_overlap(item, &utf8_width_table[j])) - log_fatalx("utf8 overlap: %u %u", i, j); - } - ptr = &utf8_width_root; while (*ptr != NULL) { node = *ptr; if (item->last < node->first) - ptr = &(node->left); + ptr = &node->left; else if (item->first > node->last) - ptr = &(node->right); + ptr = &node->right; } *ptr = item; } } +/* Lookup width of UTF-8 data in tree. */ +u_int +utf8_width(u_int uc) +{ + struct utf8_width_entry *item; + + if (utf8_width_root == NULL) + utf8_build(); + + item = utf8_width_root; + while (item != NULL) { + if (uc < item->first) + item = item->left; + else if (uc > item->last) + item = item->right; + else + return (item->width); + } + return (1); +} + /* Combine UTF-8 into 32-bit Unicode. */ u_int -utf8_combine(const struct utf8_data *utf8data) +utf8_combine(const struct utf8_data *ud) { - u_int value; + u_int uc; - value = 0xff; - switch (utf8data->size) { + uc = 0xfffd; + switch (ud->size) { case 1: - value = utf8data->data[0]; + uc = ud->data[0]; break; case 2: - value = utf8data->data[1] & 0x3f; - value |= (utf8data->data[0] & 0x1f) << 6; + uc = ud->data[1] & 0x3f; + uc |= (ud->data[0] & 0x1f) << 6; break; case 3: - value = utf8data->data[2] & 0x3f; - value |= (utf8data->data[1] & 0x3f) << 6; - value |= (utf8data->data[0] & 0x0f) << 12; + uc = ud->data[2] & 0x3f; + uc |= (ud->data[1] & 0x3f) << 6; + uc |= (ud->data[0] & 0xf) << 12; break; case 4: - value = utf8data->data[3] & 0x3f; - value |= (utf8data->data[2] & 0x3f) << 6; - value |= (utf8data->data[1] & 0x3f) << 12; - value |= (utf8data->data[0] & 0x3f) << 18; + uc = ud->data[3] & 0x3f; + uc |= (ud->data[2] & 0x3f) << 6; + uc |= (ud->data[1] & 0x3f) << 12; + uc |= (ud->data[0] & 0x7) << 18; break; } - return (value); + return (uc); +} + +/* Split 32-bit Unicode into UTF-8. */ +enum utf8_state +utf8_split(u_int uc, struct utf8_data *ud) +{ + if (uc < 0x7f) { + ud->size = 1; + ud->data[0] = uc; + } else if (uc < 0x7ff) { + ud->size = 2; + ud->data[0] = 0xc0 | ((uc >> 6) & 0x1f); + ud->data[1] = 0x80 | (uc & 0x3f); + } else if (uc < 0xffff) { + ud->size = 3; + ud->data[0] = 0xe0 | ((uc >> 12) & 0xf); + ud->data[1] = 0x80 | ((uc >> 6) & 0x3f); + ud->data[2] = 0x80 | (uc & 0x3f); + } else if (uc < 0x1fffff) { + ud->size = 4; + ud->data[0] = 0xf0 | ((uc >> 18) & 0x7); + ud->data[1] = 0x80 | ((uc >> 12) & 0x3f); + ud->data[2] = 0x80 | ((uc >> 6) & 0x3f); + ud->data[3] = 0x80 | (uc & 0x3f); + } else + return (UTF8_ERROR); + ud->width = utf8_width(uc); + return (UTF8_DONE); } /* Split a two-byte UTF-8 character. */ @@ -333,23 +534,212 @@ utf8_split2(u_int uc, u_char *ptr) return (1); } -/* Lookup width of UTF-8 data in tree. */ -u_int -utf8_width(const struct utf8_data *utf8data) +/* + * Encode len characters from src into dst, which is guaranteed to have four + * bytes available for each character from src (for \abc or UTF-8) plus space + * for \0. + */ +int +utf8_strvis(char *dst, const char *src, size_t len, int flag) { - struct utf8_width_entry *item; - u_int value; + struct utf8_data ud; + const char *start, *end; + enum utf8_state more; + size_t i; - value = utf8_combine(utf8data); + start = dst; + end = src + len; - item = utf8_width_root; - while (item != NULL) { - if (value < item->first) - item = item->left; - else if (value > item->last) - item = item->right; - else - return (item->width); + while (src < end) { + if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { + while (++src < end && more == UTF8_MORE) + more = utf8_append(&ud, *src); + if (more == UTF8_DONE) { + /* UTF-8 character finished. */ + for (i = 0; i < ud.size; i++) + *dst++ = ud.data[i]; + continue; + } + /* Not a complete, valid UTF-8 character. */ + src -= ud.have; + } + if (src < end - 1) + dst = vis(dst, src[0], flag, src[1]); + else if (src < end) + dst = vis(dst, src[0], flag, '\0'); + src++; } - return (1); + + *dst = '\0'; + return (dst - start); +} + +/* + * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free + * the returned string. Anything not valid printable ASCII or UTF-8 is + * stripped. + */ +char * +utf8_sanitize(const char *src) +{ + char *dst; + size_t n; + enum utf8_state more; + struct utf8_data ud; + u_int i; + + dst = NULL; + + n = 0; + while (*src != '\0') { + dst = xreallocarray(dst, n + 1, sizeof *dst); + if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { + while (*++src != '\0' && more == UTF8_MORE) + more = utf8_append(&ud, *src); + if (more == UTF8_DONE) { + dst = xreallocarray(dst, n + ud.width, + sizeof *dst); + for (i = 0; i < ud.width; i++) + dst[n++] = '_'; + continue; + } + src -= ud.have; + } + if (*src > 0x1f && *src < 0x7f) + dst[n++] = *src; + else + dst[n++] = '_'; + src++; + } + + dst = xreallocarray(dst, n + 1, sizeof *dst); + dst[n] = '\0'; + return (dst); +} + +/* + * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. + * Caller frees. + */ +struct utf8_data * +utf8_fromcstr(const char *src) +{ + struct utf8_data *dst; + size_t n; + enum utf8_state more; + + dst = NULL; + + n = 0; + while (*src != '\0') { + dst = xreallocarray(dst, n + 1, sizeof *dst); + if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { + while (*++src != '\0' && more == UTF8_MORE) + more = utf8_append(&dst[n], *src); + if (more == UTF8_DONE) { + n++; + continue; + } + src -= dst[n].have; + } + utf8_set(&dst[n], *src); + n++; + src++; + } + + dst = xreallocarray(dst, n + 1, sizeof *dst); + dst[n].size = 0; + return (dst); +} + +/* Convert from a buffer of UTF-8 characters into a string. Caller frees. */ +char * +utf8_tocstr(struct utf8_data *src) +{ + char *dst; + size_t n; + + dst = NULL; + + n = 0; + for(; src->size != 0; src++) { + dst = xreallocarray(dst, n + src->size, 1); + memcpy(dst + n, src->data, src->size); + n += src->size; + } + + dst = xreallocarray(dst, n + 1, 1); + dst[n] = '\0'; + return (dst); +} + +/* Get width of UTF-8 string. */ +u_int +utf8_cstrwidth(const char *s) +{ + struct utf8_data tmp; + u_int width; + enum utf8_state more; + + width = 0; + while (*s != '\0') { + if ((more = utf8_open(&tmp, *s)) == UTF8_MORE) { + while (*++s != '\0' && more == UTF8_MORE) + more = utf8_append(&tmp, *s); + if (more == UTF8_DONE) { + width += tmp.width; + continue; + } + s -= tmp.have; + } + if (*s > 0x1f && *s != 0x7f) + width++; + s++; + } + return (width); +} + +/* Trim UTF-8 string to width. Caller frees. */ +char * +utf8_trimcstr(const char *s, u_int width) +{ + struct utf8_data *tmp, *next; + char *out; + u_int at; + + tmp = utf8_fromcstr(s); + + at = 0; + for (next = tmp; next->size != 0; next++) { + if (at + next->width > width) { + next->size = 0; + break; + } + at += next->width; + } + + out = utf8_tocstr(tmp); + free(tmp); + return (out); +} + +/* Pad UTF-8 string to width. Caller frees. */ +char * +utf8_padcstr(const char *s, u_int width) +{ + size_t slen; + char *out; + u_int n, i; + + n = utf8_cstrwidth(s); + if (n >= width) + return (xstrdup(s)); + + slen = strlen(s); + out = xmalloc(slen + 1 + (width - n)); + memcpy(out, s, slen); + for (i = n; i < width; i++) + out[slen++] = ' '; + out[slen] = '\0'; + return (out); } diff --git a/window-choose.c b/window-choose.c index 3c68d101..fdfc47a0 100644 --- a/window-choose.c +++ b/window-choose.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -22,29 +22,30 @@ #include #include +#include "array.h" #include "tmux.h" struct screen *window_choose_init(struct window_pane *); void window_choose_free(struct window_pane *); void window_choose_resize(struct window_pane *, u_int, u_int); -void window_choose_key(struct window_pane *, struct session *, int); -void window_choose_mouse( - struct window_pane *, struct session *, struct mouse_event *); +void window_choose_key(struct window_pane *, struct client *, + struct session *, key_code, struct mouse_event *); void window_choose_default_callback(struct window_choose_data *); +struct window_choose_mode_item *window_choose_get_item(struct window_pane *, + key_code, struct mouse_event *); -void window_choose_fire_callback( - struct window_pane *, struct window_choose_data *); +void window_choose_fire_callback(struct window_pane *, + struct window_choose_data *); void window_choose_redraw_screen(struct window_pane *); -void window_choose_write_line( - struct window_pane *, struct screen_write_ctx *, u_int); +void window_choose_write_line(struct window_pane *, + struct screen_write_ctx *, u_int); void window_choose_scroll_up(struct window_pane *); void window_choose_scroll_down(struct window_pane *); -void window_choose_collapse(struct window_pane *, struct session *); +void window_choose_collapse(struct window_pane *, struct session *, u_int); void window_choose_expand(struct window_pane *, struct session *, u_int); -void window_choose_collapse_all(struct window_pane *); enum window_choose_input_type { WINDOW_CHOOSE_NORMAL = -1, @@ -56,8 +57,14 @@ const struct window_mode window_choose_mode = { window_choose_free, window_choose_resize, window_choose_key, - window_choose_mouse, - NULL, +}; + +struct window_choose_mode_item { + struct window_choose_data *wcd; + char *name; + int pos; + int state; +#define TREE_EXPANDED 0x1 }; struct window_choose_mode_data { @@ -79,9 +86,10 @@ struct window_choose_mode_data { void window_choose_free1(struct window_choose_mode_data *); int window_choose_key_index(struct window_choose_mode_data *, u_int); -int window_choose_index_key(struct window_choose_mode_data *, int); +int window_choose_index_key(struct window_choose_mode_data *, key_code); void window_choose_prompt_input(enum window_choose_input_type, - const char *, struct window_pane *, int); + const char *, struct window_pane *, key_code); +void window_choose_reset_top(struct window_pane *, u_int); void window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) @@ -98,7 +106,29 @@ window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) item->pos = ARRAY_LENGTH(&data->list) - 1; item->state = 0; - data->width = xsnprintf (tmp, sizeof tmp , "%u", item->pos); + data->width = xsnprintf(tmp, sizeof tmp , "%d", item->pos); +} + +void +window_choose_set_current(struct window_pane *wp, u_int cur) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + data->selected = cur; + window_choose_reset_top(wp, screen_size_y(s)); +} + +void +window_choose_reset_top(struct window_pane *wp, u_int sy) +{ + struct window_choose_mode_data *data = wp->modedata; + + data->top = 0; + if (data->selected > sy - 1) + data->top = data->selected - (sy - 1); + + window_choose_redraw_screen(wp); } void @@ -106,11 +136,6 @@ window_choose_ready(struct window_pane *wp, u_int cur, void (*callbackfn)(struct window_choose_data *)) { struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - - data->selected = cur; - if (data->selected > screen_size_y(s) - 1) - data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); data->callbackfn = callbackfn; if (data->callbackfn == NULL) @@ -118,6 +143,7 @@ window_choose_ready(struct window_pane *wp, u_int cur, ARRAY_CONCAT(&data->old_list, &data->list); + window_choose_set_current(wp, cur); window_choose_collapse_all(wp); } @@ -142,10 +168,8 @@ window_choose_init(struct window_pane *wp) s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; - if (options_get_number(&wp->window->options, "mode-mouse")) - s->mode |= MODE_MOUSE_STANDARD; - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_choice); else @@ -162,7 +186,7 @@ window_choose_data_create(int type, struct client *c, struct session *s) wcd = xmalloc(sizeof *wcd); wcd->type = type; - wcd->ft = format_create(); + wcd->ft = format_create(NULL, 0); wcd->ft_template = NULL; wcd->command = NULL; @@ -184,11 +208,11 @@ window_choose_data_create(int type, struct client *c, struct session *s) void window_choose_data_free(struct window_choose_data *wcd) { - wcd->start_client->references--; - wcd->start_session->references--; + server_client_unref(wcd->start_client); + session_unref(wcd->start_session); if (wcd->tree_session != NULL) - wcd->tree_session->references--; + session_unref(wcd->tree_session); free(wcd->ft_template); format_free(wcd->ft); @@ -219,7 +243,7 @@ window_choose_data_run(struct window_choose_data *cdata) return; } - cmdq_run(cdata->start_client->cmdq, cmdlist); + cmdq_run(cdata->start_client->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); } @@ -269,17 +293,14 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; - data->top = 0; - if (data->selected > sy - 1) - data->top = data->selected - (sy - 1); - + window_choose_reset_top(wp, sy); screen_resize(s, sx, sy, 0); window_choose_redraw_screen(wp); } void -window_choose_fire_callback( - struct window_pane *wp, struct window_choose_data *wcd) +window_choose_fire_callback(struct window_pane *wp, + struct window_choose_data *wcd) { struct window_choose_mode_data *data = wp->modedata; @@ -293,7 +314,7 @@ window_choose_fire_callback( void window_choose_prompt_input(enum window_choose_input_type input_type, - const char *prompt, struct window_pane *wp, int key) + const char *prompt, struct window_pane *wp, key_code key) { struct window_choose_mode_data *data = wp->modedata; size_t input_len; @@ -302,7 +323,7 @@ window_choose_prompt_input(enum window_choose_input_type input_type, data->input_prompt = prompt; input_len = strlen(data->input_str) + 2; - data->input_str = xrealloc(data->input_str, 1, input_len); + data->input_str = xrealloc(data->input_str, input_len); data->input_str[input_len - 2] = key; data->input_str[input_len - 1] = '\0'; @@ -310,18 +331,16 @@ window_choose_prompt_input(enum window_choose_input_type input_type, } void -window_choose_collapse(struct window_pane *wp, struct session *s) +window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item, *chosen; struct window_choose_data *wcd; - u_int i, pos; + u_int i; ARRAY_DECL(, struct window_choose_mode_item) list_copy; ARRAY_INIT(&list_copy); - pos = data->selected; - chosen = &ARRAY_ITEM(&data->list, pos); chosen->state &= ~TREE_EXPANDED; @@ -330,8 +349,7 @@ window_choose_collapse(struct window_pane *wp, struct session *s) * assign the actual result we want to render and copy the new one over * the top of it. */ - for (i = 0; i < ARRAY_LENGTH(&data->list); i++) - { + for (i = 0; i < ARRAY_LENGTH(&data->list); i++) { item = &ARRAY_ITEM(&data->list, i); wcd = item->wcd; @@ -339,9 +357,8 @@ window_choose_collapse(struct window_pane *wp, struct session *s) /* We only show the session when collapsed. */ if (wcd->type & TREE_SESSION) { item->state &= ~TREE_EXPANDED; + ARRAY_ADD(&list_copy, *item); - ARRAY_ADD(&list_copy, - ARRAY_ITEM(&data->list, i)); /* * Update the selection to this session item so * we don't end up highlighting a non-existent @@ -365,13 +382,14 @@ window_choose_collapse_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; + struct screen *scr = &data->screen; struct session *s, *chosen; u_int i; chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session; RB_FOREACH(s, sessions, &sessions) - window_choose_collapse(wp, s); + window_choose_collapse(wp, s, data->selected); /* Reset the selection back to the starting session. */ for (i = 0; i < ARRAY_LENGTH(&data->list); i++) { @@ -383,7 +401,7 @@ window_choose_collapse_all(struct window_pane *wp) if (item->wcd->type & TREE_SESSION) data->selected = i; } - window_choose_redraw_screen(wp); + window_choose_reset_top(wp, screen_size_y(scr)); } void @@ -391,6 +409,7 @@ window_choose_expand_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; + struct screen *scr = &data->screen; struct session *s; u_int i; @@ -406,7 +425,7 @@ window_choose_expand_all(struct window_pane *wp) } } - window_choose_redraw_screen(wp); + window_choose_reset_top(wp, screen_size_y(scr)); } void @@ -470,8 +489,28 @@ window_choose_expand(struct window_pane *wp, struct session *s, u_int pos) } } +struct window_choose_mode_item * +window_choose_get_item(struct window_pane *wp, key_code key, + struct mouse_event *m) +{ + struct window_choose_mode_data *data = wp->modedata; + u_int x, y, idx; + + if (!KEYC_IS_MOUSE(key)) + return (&ARRAY_ITEM(&data->list, data->selected)); + + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) + return (NULL); + + idx = data->top + y; + if (idx >= ARRAY_LENGTH(&data->list)) + return (NULL); + return (&ARRAY_ITEM(&data->list, idx)); +} + void -window_choose_key(struct window_pane *wp, unused struct session *sess, int key) +window_choose_key(struct window_pane *wp, __unused struct client *c, + __unused struct session *sess, key_code key, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -520,23 +559,28 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_fire_callback(wp, NULL); break; case MODEKEYCHOICE_CHOOSE: - item = &ARRAY_ITEM(&data->list, data->selected); + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; window_choose_fire_callback(wp, item->wcd); break; case MODEKEYCHOICE_TREE_TOGGLE: - item = &ARRAY_ITEM(&data->list, data->selected); - if (item->state & TREE_EXPANDED) - window_choose_collapse(wp, item->wcd->tree_session); - else { + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; + if (item->state & TREE_EXPANDED) { + window_choose_collapse(wp, item->wcd->tree_session, + data->selected); + } else { window_choose_expand(wp, item->wcd->tree_session, data->selected); } window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_TREE_COLLAPSE: - item = &ARRAY_ITEM(&data->list, data->selected); + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; if (item->state & TREE_EXPANDED) { - window_choose_collapse(wp, item->wcd->tree_session); + window_choose_collapse(wp, item->wcd->tree_session, + data->selected); window_choose_redraw_screen(wp); } break; @@ -544,7 +588,8 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_collapse_all(wp); break; case MODEKEYCHOICE_TREE_EXPAND: - item = &ARRAY_ITEM(&data->list, data->selected); + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; if (!(item->state & TREE_EXPANDED)) { window_choose_expand(wp, item->wcd->tree_session, data->selected); @@ -569,10 +614,10 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_scroll_up(wp); else { screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, data->selected - data->top); - window_choose_write_line( - wp, &ctx, data->selected + 1 - data->top); + window_choose_write_line(wp, &ctx, + data->selected - data->top); + window_choose_write_line(wp, &ctx, + data->selected + 1 - data->top); screen_write_stop(&ctx); } break; @@ -589,10 +634,10 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) if (data->selected < data->top + screen_size_y(s)) { screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, data->selected - data->top); - window_choose_write_line( - wp, &ctx, data->selected - 1 - data->top); + window_choose_write_line(wp, &ctx, + data->selected - data->top); + window_choose_write_line(wp, &ctx, + data->selected - 1 - data->top); screen_write_stop(&ctx); } else window_choose_scroll_down(wp); @@ -604,8 +649,8 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) data->selected--; window_choose_scroll_up(wp); screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, screen_size_y(s) - 1); + window_choose_write_line(wp, &ctx, + screen_size_y(s) - 1); screen_write_stop(&ctx); } else window_choose_scroll_up(wp); @@ -663,6 +708,29 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM, "Goto Item", wp, key); break; + case MODEKEYCHOICE_STARTOFLIST: + data->selected = 0; + data->top = 0; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_TOPLINE: + data->selected = data->top; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_BOTTOMLINE: + data->selected = data->top + screen_size_y(s) - 1; + if (data->selected > items - 1) + data->selected = items - 1; + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_ENDOFLIST: + data->selected = items - 1; + if (screen_size_y(s) < items) + data->top = items - screen_size_y(s); + else + data->top = 0; + window_choose_redraw_screen(wp); + break; default: idx = window_choose_index_key(data, key); if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list)) @@ -676,51 +744,25 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) } void -window_choose_mouse( - struct window_pane *wp, unused struct session *sess, struct mouse_event *m) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - struct window_choose_mode_item *item; - u_int idx; - - if (~m->event & MOUSE_EVENT_CLICK) - return; - if (m->x >= screen_size_x(s)) - return; - if (m->y >= screen_size_y(s)) - return; - - idx = data->top + m->y; - if (idx >= ARRAY_LENGTH(&data->list)) - return; - data->selected = idx; - - item = &ARRAY_ITEM(&data->list, data->selected); - window_choose_fire_callback(wp, item->wcd); -} - -void -window_choose_write_line( - struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, + u_int py) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct screen *s = &data->screen; struct grid_cell gc; size_t last, xoff = 0; char hdr[32], label[32]; - int utf8flag, key; + int key; if (data->callbackfn == NULL) fatalx("called before callback assigned"); last = screen_size_y(s) - 1; - utf8flag = options_get_number(&wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); screen_write_cursormove(ctx, 0, py); if (data->top + py < ARRAY_LENGTH(&data->list)) { @@ -731,10 +773,10 @@ window_choose_write_line( key = window_choose_key_index(data, data->top + py); if (key != -1) - xsnprintf (label, sizeof label, "(%c)", key); + xsnprintf(label, sizeof label, "(%c)", key); else - xsnprintf (label, sizeof label, "(%d)", item->pos); - screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag, + xsnprintf(label, sizeof label, "(%d)", item->pos); + screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, "%*s %s %s", data->width + 2, label, /* * Add indication to tree if necessary about whether it's @@ -747,7 +789,7 @@ window_choose_write_line( screen_write_putc(ctx, &gc, ' '); if (data->input_type != WINDOW_CHOOSE_NORMAL) { - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); xoff = xsnprintf(hdr, sizeof hdr, "%s: %s", data->input_prompt, data->input_str); @@ -779,7 +821,7 @@ window_choose_key_index(struct window_choose_mode_data *data, u_int idx) } int -window_choose_index_key(struct window_choose_mode_data *data, int key) +window_choose_index_key(struct window_choose_mode_data *data, key_code key) { static const char keys[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" @@ -792,7 +834,7 @@ window_choose_index_key(struct window_choose_mode_data *data, int key) mkey = mode_key_lookup(&data->mdata, *ptr, NULL); if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) continue; - if (key == *ptr) + if (key == (key_code)*ptr) return (idx); idx++; } @@ -866,7 +908,7 @@ window_choose_add_session(struct window_pane *wp, struct client *c, wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); - format_session(wcd->ft, s); + format_defaults(wcd->ft, NULL, s, NULL, NULL); wcd->command = cmd_template_replace(action, s->name, 1); @@ -875,36 +917,6 @@ window_choose_add_session(struct window_pane *wp, struct client *c, return (wcd); } -struct window_choose_data * -window_choose_add_item(struct window_pane *wp, struct client *c, - struct winlink *wl, const char *template, const char *action, u_int idx) -{ - struct window_choose_data *wcd; - char *expanded; - - wcd = window_choose_data_create(TREE_OTHER, c, c->session); - wcd->idx = wl->idx; - - wcd->ft_template = xstrdup(template); - format_add(wcd->ft, "line", "%u", idx); - format_session(wcd->ft, wcd->start_session); - format_winlink(wcd->ft, wcd->start_session, wl); - format_window_pane(wcd->ft, wl->window->active); - - /* - * Interpolate action here, since the data we pass back is the expanded - * template itself. - */ - xasprintf(&expanded, "%s", format_expand(wcd->ft, wcd->ft_template)); - wcd->command = cmd_template_replace(action, expanded, 1); - free(expanded); - - window_choose_add(wp, wcd); - - return (wcd); - -} - struct window_choose_data * window_choose_add_window(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, const char *template, @@ -923,9 +935,7 @@ window_choose_add_window(struct window_pane *wp, struct client *c, wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); - format_session(wcd->ft, s); - format_winlink(wcd->ft, s, wl); - format_window_pane(wcd->ft, wl->window->active); + format_defaults(wcd->ft, NULL, s, wl, NULL); xasprintf(&expanded, "%s:%d", s->name, wl->idx); wcd->command = cmd_template_replace(action, expanded, 1); diff --git a/window-clock.c b/window-clock.c index 61cf1502..e8451f22 100644 --- a/window-clock.c +++ b/window-clock.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -27,9 +27,10 @@ struct screen *window_clock_init(struct window_pane *); void window_clock_free(struct window_pane *); void window_clock_resize(struct window_pane *, u_int, u_int); -void window_clock_key(struct window_pane *, struct session *, int); -void window_clock_timer(struct window_pane *); +void window_clock_key(struct window_pane *, struct client *, + struct session *, key_code, struct mouse_event *); +void window_clock_timer_callback(int, short, void *); void window_clock_draw_screen(struct window_pane *); const struct window_mode window_clock_mode = { @@ -37,24 +38,123 @@ const struct window_mode window_clock_mode = { window_clock_free, window_clock_resize, window_clock_key, - NULL, - window_clock_timer, }; struct window_clock_mode_data { struct screen screen; time_t tim; + struct event timer; }; +const char window_clock_table[14][5][5] = { + { { 1,1,1,1,1 }, /* 0 */ + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,1 }, /* 1 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 2 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 3 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,0,0,0,1 }, /* 4 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 5 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 6 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 7 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 8 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 9 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,0 }, /* : */ + { 0,0,1,0,0 }, + { 0,0,0,0,0 }, + { 0,0,1,0,0 }, + { 0,0,0,0,0 } }, + { { 1,1,1,1,1 }, /* A */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* P */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,0,0,0,0 } }, + { { 1,0,0,0,1 }, /* M */ + { 1,1,0,1,1 }, + { 1,0,1,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, +}; + +void +window_clock_timer_callback(__unused int fd, __unused short events, void *arg) +{ + struct window_pane *wp = arg; + struct window_clock_mode_data *data = wp->modedata; + struct tm now, then; + time_t t; + struct timeval tv = { .tv_sec = 1 }; + + evtimer_del(&data->timer); + evtimer_add(&data->timer, &tv); + + t = time(NULL); + gmtime_r(&t, &now); + gmtime_r(&data->tim, &then); + if (now.tm_min == then.tm_min) + return; + data->tim = t; + + window_clock_draw_screen(wp); + server_redraw_window(wp->window); +} + struct screen * window_clock_init(struct window_pane *wp) { struct window_clock_mode_data *data; struct screen *s; + struct timeval tv = { .tv_sec = 1 }; wp->modedata = data = xmalloc(sizeof *data); data->tim = time(NULL); + evtimer_set(&data->timer, window_clock_timer_callback, wp); + evtimer_add(&data->timer, &tv); + s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; @@ -69,6 +169,7 @@ window_clock_free(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; + evtimer_del(&data->timer); screen_free(&data->screen); free(data); } @@ -84,41 +185,89 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_clock_key( - struct window_pane *wp, unused struct session *sess, unused int key) +window_clock_key(struct window_pane *wp, __unused struct client *c, + __unused struct session *sess, __unused key_code key, + __unused struct mouse_event *m) { window_pane_reset_mode(wp); } -void -window_clock_timer(struct window_pane *wp) -{ - struct window_clock_mode_data *data = wp->modedata; - struct tm now, then; - time_t t; - - t = time(NULL); - gmtime_r(&t, &now); - gmtime_r(&data->tim, &then); - if (now.tm_min == then.tm_min) - return; - data->tim = t; - - window_clock_draw_screen(wp); - server_redraw_window(wp->window); -} - void window_clock_draw_screen(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; struct screen_write_ctx ctx; int colour, style; + struct screen *s = &data->screen; + struct grid_cell gc; + char tim[64], *ptr; + time_t t; + struct tm *tm; + u_int i, j, x, y, idx; - colour = options_get_number(&wp->window->options, "clock-mode-colour"); - style = options_get_number(&wp->window->options, "clock-mode-style"); + colour = options_get_number(wp->window->options, "clock-mode-colour"); + style = options_get_number(wp->window->options, "clock-mode-style"); + + screen_write_start(&ctx, NULL, s); + + t = time(NULL); + tm = localtime(&t); + if (style == 0) { + strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); + if (tm->tm_hour >= 12) + strlcat(tim, "PM", sizeof tim); + else + strlcat(tim, "AM", sizeof tim); + } else + strftime(tim, sizeof tim, "%H:%M", tm); + + screen_write_clearscreen(&ctx); + + if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { + if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { + x = (screen_size_x(s) / 2) - (strlen(tim) / 2); + y = screen_size_y(s) / 2; + screen_write_cursormove(&ctx, x, y); + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, colour); + screen_write_puts(&ctx, &gc, "%s", tim); + } + + screen_write_stop(&ctx); + return; + } + + x = (screen_size_x(s) / 2) - 3 * strlen(tim); + y = (screen_size_y(s) / 2) - 3; + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_bg(&gc, colour); + for (ptr = tim; *ptr != '\0'; ptr++) { + if (*ptr >= '0' && *ptr <= '9') + idx = *ptr - '0'; + else if (*ptr == ':') + idx = 10; + else if (*ptr == 'A') + idx = 11; + else if (*ptr == 'P') + idx = 12; + else if (*ptr == 'M') + idx = 13; + else { + x += 6; + continue; + } + + for (j = 0; j < 5; j++) { + for (i = 0; i < 5; i++) { + screen_write_cursormove(&ctx, x + i, y + j); + if (window_clock_table[idx][j][i]) + screen_write_putc(&ctx, &gc, ' '); + } + } + x += 6; + } - screen_write_start(&ctx, NULL, &data->screen); - clock_draw(&ctx, colour, style); screen_write_stop(&ctx); } diff --git a/window-copy.c b/window-copy.c index 3cb7eb38..2d6cd77b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,6 +18,7 @@ #include +#include #include #include @@ -27,67 +28,74 @@ struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); -void window_copy_key(struct window_pane *, struct session *, int); -int window_copy_key_input(struct window_pane *, int); -int window_copy_key_numeric_prefix(struct window_pane *, int); -void window_copy_mouse( - struct window_pane *, struct session *, struct mouse_event *); +void window_copy_key(struct window_pane *, struct client *, struct session *, + key_code, struct mouse_event *); +int window_copy_key_input(struct window_pane *, key_code); +int window_copy_key_numeric_prefix(struct window_pane *, key_code); +void window_copy_redraw_selection(struct window_pane *, u_int); void window_copy_redraw_lines(struct window_pane *, u_int, u_int); -void window_copy_write_line( - struct window_pane *, struct screen_write_ctx *, u_int); -void window_copy_write_lines( - struct window_pane *, struct screen_write_ctx *, u_int, u_int); +void window_copy_redraw_screen(struct window_pane *); +void window_copy_write_line(struct window_pane *, struct screen_write_ctx *, + u_int); +void window_copy_write_lines(struct window_pane *, + struct screen_write_ctx *, u_int, u_int); void window_copy_scroll_to(struct window_pane *, u_int, u_int); -int window_copy_search_compare( - struct grid *, u_int, u_int, struct grid *, u_int); -int window_copy_search_lr( - struct grid *, struct grid *, u_int *, u_int, u_int, u_int); -int window_copy_search_rl( - struct grid *, struct grid *, u_int *, u_int, u_int, u_int); +int window_copy_search_compare(struct grid *, u_int, u_int, struct grid *, + u_int, int); +int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, + u_int, u_int, int); +int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, + u_int, u_int, int); void window_copy_search_up(struct window_pane *, const char *); void window_copy_search_down(struct window_pane *, const char *); void window_copy_goto_line(struct window_pane *, const char *); void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); +int window_copy_update_selection(struct window_pane *, int); void *window_copy_get_selection(struct window_pane *, size_t *); -void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); -void window_copy_copy_pipe( - struct window_pane *, struct session *, int, const char *); -void window_copy_copy_selection(struct window_pane *, int); +void window_copy_copy_buffer(struct window_pane *, const char *, void *, + size_t); +void window_copy_copy_pipe(struct window_pane *, struct session *, + const char *, const char *); +void window_copy_copy_selection(struct window_pane *, const char *); +void window_copy_append_selection(struct window_pane *, const char *); void window_copy_clear_selection(struct window_pane *); -void window_copy_copy_line( - struct window_pane *, char **, size_t *, u_int, u_int, u_int); +void window_copy_copy_line(struct window_pane *, char **, size_t *, u_int, + u_int, u_int); int window_copy_in_set(struct window_pane *, u_int, u_int, const char *); u_int window_copy_find_length(struct window_pane *, u_int); void window_copy_cursor_start_of_line(struct window_pane *); void window_copy_cursor_back_to_indentation(struct window_pane *); void window_copy_cursor_end_of_line(struct window_pane *); +void window_copy_other_end(struct window_pane *); void window_copy_cursor_left(struct window_pane *); void window_copy_cursor_right(struct window_pane *); void window_copy_cursor_up(struct window_pane *, int); void window_copy_cursor_down(struct window_pane *, int); void window_copy_cursor_jump(struct window_pane *); void window_copy_cursor_jump_back(struct window_pane *); -void window_copy_cursor_jump_to(struct window_pane *); -void window_copy_cursor_jump_to_back(struct window_pane *); +void window_copy_cursor_jump_to(struct window_pane *, int); +void window_copy_cursor_jump_to_back(struct window_pane *, int); void window_copy_cursor_next_word(struct window_pane *, const char *); void window_copy_cursor_next_word_end(struct window_pane *, const char *); void window_copy_cursor_previous_word(struct window_pane *, const char *); void window_copy_scroll_up(struct window_pane *, u_int); void window_copy_scroll_down(struct window_pane *, u_int); void window_copy_rectangle_toggle(struct window_pane *); +void window_copy_drag_update(struct client *, struct mouse_event *); +void window_copy_drag_release(struct client *, struct mouse_event *); const struct window_mode window_copy_mode = { window_copy_init, window_copy_free, window_copy_resize, window_copy_key, - window_copy_mouse, - NULL, }; +#include "window-copy.h" + struct screen * window_copy_init(struct window_pane *wp) { @@ -106,6 +114,7 @@ window_copy_init(struct window_pane *wp) data->backing_written = 0; data->rectflag = 0; + data->scroll_exit = 0; data->inputtype = WINDOW_COPY_OFF; data->inputprompt = NULL; @@ -123,14 +132,13 @@ window_copy_init(struct window_pane *wp) s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); - if (options_get_number(&wp->window->options, "mode-mouse")) - s->mode |= MODE_MOUSE_STANDARD; - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else mode_key_init(&data->mdata, &mode_key_tree_vi_copy); + s->sel.modekeys = keys; data->backing = NULL; @@ -142,7 +150,7 @@ window_copy_init(struct window_pane *wp) } void -window_copy_init_from_pane(struct window_pane *wp) +window_copy_init_from_pane(struct window_pane *wp, int scroll_exit) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -155,6 +163,7 @@ window_copy_init_from_pane(struct window_pane *wp) data->backing = &wp->base; data->cx = data->backing->cx; data->cy = data->backing->cy; + data->scroll_exit = scroll_exit; s->cx = data->cx; s->cy = data->cy; @@ -165,7 +174,9 @@ window_copy_init_from_pane(struct window_pane *wp) screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_stop(&ctx); +#ifdef TMATE tmate_sync_copy_mode(wp); +#endif } void @@ -176,7 +187,6 @@ window_copy_init_for_output(struct window_pane *wp) data->backing = xmalloc(sizeof *data->backing); screen_init(data->backing, screen_size_x(&wp->base), screen_size_y(&wp->base), UINT_MAX); - data->backing->mode &= ~MODE_WRAP; } void @@ -216,8 +226,8 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) struct screen *backing = data->backing; struct screen_write_ctx back_ctx, ctx; struct grid_cell gc; - int utf8flag; - u_int old_hsize; + u_int old_hsize, old_cy; + #ifdef TMATE char *msg; #endif @@ -225,7 +235,6 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) if (backing == &wp->base) return; - utf8flag = options_get_number(&wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); @@ -239,13 +248,14 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) screen_write_linefeed(&back_ctx, 0); } else data->backing_written = 1; + old_cy = backing->cy; #ifdef TMATE xvasprintf(&msg, fmt, ap); - screen_write_nputs(&back_ctx, 0, &gc, utf8flag, "%s", msg); + screen_write_nputs(&back_ctx, 0, &gc, "%s", msg); tmate_write_copy_mode(wp, msg); free(msg); #else - screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); + screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); #endif screen_write_stop(&back_ctx); @@ -260,9 +270,8 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) if (screen_hsize(data->backing)) window_copy_redraw_lines(wp, 0, 1); - /* Write the line, if it's visible. */ - if (backing->cy + data->oy < screen_size_y(backing)) - window_copy_redraw_lines(wp, backing->cy, 1); + /* Write the new lines. */ + window_copy_redraw_lines(wp, old_cy, backing->cy - old_cy + 1); screen_write_stop(&ctx); } @@ -281,7 +290,7 @@ window_copy_pageup(struct window_pane *wp) data->oy = screen_hsize(data->backing); else data->oy += n; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } @@ -292,9 +301,9 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) struct screen *s = &data->screen; struct screen_write_ctx ctx; - screen_resize(s, sx, sy, 0); + screen_resize(s, sx, sy, 1); if (data->backing != &wp->base) - screen_resize(data->backing, sx, sy, 0); + screen_resize(data->backing, sx, sy, 1); if (data->cy > sy - 1) data->cy = sy - 1; @@ -313,19 +322,20 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) } static void -__window_copy_key(struct window_pane *wp, struct session *sess, int key) +__window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, + key_code key, struct mouse_event *m) { const char *word_separators; struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - u_int n; - int np, keys; + u_int n, np; + int keys; enum mode_key_cmd cmd; - const char *arg; + const char *arg, *ss; - np = data->numprefix; - if (np <= 0) - np = 1; + np = 1; + if (data->numprefix > 0) + np = data->numprefix; if (data->inputtype == WINDOW_COPY_JUMPFORWARD || data->inputtype == WINDOW_COPY_JUMPBACK || @@ -337,15 +347,18 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) if (data->inputtype == WINDOW_COPY_JUMPFORWARD) { for (; np != 0; np--) window_copy_cursor_jump(wp); - } else if (data->inputtype == WINDOW_COPY_JUMPBACK) { + } + if (data->inputtype == WINDOW_COPY_JUMPBACK) { for (; np != 0; np--) window_copy_cursor_jump_back(wp); - } else if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) { + } + if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); - } else if (data->inputtype == WINDOW_COPY_JUMPTOBACK) { + window_copy_cursor_jump_to(wp, 0); + } + if (data->inputtype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 0); } } data->jumptype = data->inputtype; @@ -364,10 +377,32 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) } cmd = mode_key_lookup(&data->mdata, key, &arg); + if (cmd != MODEKEYCOPY_PREVIOUSPAGE && + cmd != MODEKEYCOPY_NEXTPAGE && + cmd != MODEKEYCOPY_SCROLLUP && + cmd != MODEKEYCOPY_SCROLLDOWN && + cmd != MODEKEYCOPY_HALFPAGEUP && + cmd != MODEKEYCOPY_HALFPAGEDOWN) + data->scroll_exit = 0; switch (cmd) { + case MODEKEYCOPY_APPENDSELECTION: + if (sess != NULL) { + window_copy_append_selection(wp, NULL); + if (arg == NULL) { + window_pane_reset_mode(wp); + return; + } + window_copy_clear_selection(wp); + window_copy_redraw_screen(wp); + } + break; case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); return; + case MODEKEYCOPY_OTHEREND: + if (np % 2) + window_copy_other_end(wp); + break; case MODEKEYCOPY_LEFT: for (; np != 0; np--) window_copy_cursor_left(wp); @@ -391,6 +426,10 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) case MODEKEYCOPY_SCROLLDOWN: for (; np != 0; np--) window_copy_cursor_down(wp, 1); + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } break; case MODEKEYCOPY_PREVIOUSPAGE: for (; np != 0; np--) @@ -406,7 +445,11 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) else data->oy -= n; } - window_copy_update_selection(wp); + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HALFPAGEUP: @@ -417,7 +460,7 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) else data->oy += n; } - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HALFPAGEDOWN: @@ -428,47 +471,60 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) else data->oy -= n; } - window_copy_update_selection(wp); + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_TOPLINE: data->cx = 0; data->cy = 0; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_MIDDLELINE: data->cx = 0; data->cy = (screen_size_y(s) - 1) / 2; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_BOTTOMLINE: data->cx = 0; data->cy = screen_size_y(s) - 1; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HISTORYTOP: data->cx = 0; data->cy = 0; data->oy = screen_hsize(data->backing); - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HISTORYBOTTOM: data->cx = 0; data->cy = screen_size_y(s) - 1; data->oy = 0; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_STARTSELECTION: - window_copy_start_selection(wp); - window_copy_redraw_screen(wp); + if (KEYC_IS_MOUSE(key)) { + if (c != NULL) + window_copy_start_drag(c, m); + } else { + s->sel.lineflag = LINE_SEL_NONE; + window_copy_start_selection(wp); + window_copy_redraw_screen(wp); + } break; - case MODEKEYCOPY_COPYLINE: case MODEKEYCOPY_SELECTLINE: + s->sel.lineflag = LINE_SEL_LEFT_RIGHT; + data->rectflag = 0; + /* FALLTHROUGH */ + case MODEKEYCOPY_COPYLINE: window_copy_cursor_start_of_line(wp); /* FALLTHROUGH */ case MODEKEYCOPY_COPYENDOFLINE: @@ -482,7 +538,7 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) if (sess != NULL && (cmd == MODEKEYCOPY_COPYLINE || cmd == MODEKEYCOPY_COPYENDOFLINE)) { - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); return; } @@ -493,16 +549,20 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) break; case MODEKEYCOPY_COPYPIPE: if (sess != NULL) { - window_copy_copy_pipe(wp, sess, data->numprefix, arg); + window_copy_copy_pipe(wp, sess, NULL, arg); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { - window_copy_copy_selection(wp, data->numprefix); - window_pane_reset_mode(wp); - return; + window_copy_copy_selection(wp, NULL); + if (arg == NULL) { + window_pane_reset_mode(wp); + return; + } + window_copy_clear_selection(wp); + window_copy_redraw_screen(wp); } break; case MODEKEYCOPY_STARTOFLINE: @@ -524,13 +584,13 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) break; case MODEKEYCOPY_NEXTWORD: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word(wp, word_separators); break; case MODEKEYCOPY_NEXTWORDEND: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word_end(wp, word_separators); break; @@ -540,7 +600,7 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) break; case MODEKEYCOPY_PREVIOUSWORD: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_previous_word(wp, word_separators); break; @@ -559,10 +619,10 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) window_copy_cursor_jump_back(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 1); } break; case MODEKEYCOPY_JUMPREVERSE: @@ -574,10 +634,10 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) window_copy_cursor_jump(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 1); } break; case MODEKEYCOPY_JUMPBACK: @@ -615,35 +675,30 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) case WINDOW_COPY_JUMPBACK: case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOBACK: + case WINDOW_COPY_NAMEDBUFFER: case WINDOW_COPY_NUMERICPREFIX: #ifdef TMATE case WINDOW_COPY_PASSWORD: break; #endif case WINDOW_COPY_SEARCHUP: + ss = data->searchstr; if (cmd == MODEKEYCOPY_SEARCHAGAIN) { - for (; np != 0; np--) { - window_copy_search_up( - wp, data->searchstr); - } + for (; np != 0; np--) + window_copy_search_up(wp, ss); } else { - for (; np != 0; np--) { - window_copy_search_down( - wp, data->searchstr); - } + for (; np != 0; np--) + window_copy_search_down(wp, ss); } break; case WINDOW_COPY_SEARCHDOWN: + ss = data->searchstr; if (cmd == MODEKEYCOPY_SEARCHAGAIN) { - for (; np != 0; np--) { - window_copy_search_down( - wp, data->searchstr); - } + for (; np != 0; np--) + window_copy_search_down(wp, ss); } else { - for (; np != 0; np--) { - window_copy_search_up( - wp, data->searchstr); - } + for (; np != 0; np--) + window_copy_search_up(wp, ss); } break; } @@ -653,6 +708,12 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) data->inputprompt = "Goto Line"; *data->inputstr = '\0'; goto input_on; + case MODEKEYCOPY_STARTNAMEDBUFFER: + data->inputtype = WINDOW_COPY_NAMEDBUFFER; + data->inputexit = (arg == NULL); + data->inputprompt = "Buffer"; + *data->inputstr = '\0'; + goto input_on; case MODEKEYCOPY_STARTNUMBERPREFIX: key &= KEYC_MASK_KEY; if (key >= '0' && key <= '9') { @@ -663,6 +724,7 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) } break; case MODEKEYCOPY_RECTANGLETOGGLE: + s->sel.lineflag = LINE_SEL_NONE; window_copy_rectangle_toggle(wp); break; default: @@ -673,7 +735,7 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) return; input_on: - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); else @@ -683,7 +745,7 @@ input_on: return; input_off: - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else @@ -696,19 +758,25 @@ input_off: } void -window_copy_key(struct window_pane *wp, struct session *sess, int key) +window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, + key_code key, struct mouse_event *m) { - __window_copy_key(wp, sess, key); + __window_copy_key(wp, c, sess, key, m); +#ifdef TMATE tmate_sync_copy_mode(wp); +#endif } int -window_copy_key_input(struct window_pane *wp, int key) +window_copy_key_input(struct window_pane *wp, key_code key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - size_t inputlen; + const char *bufdata; + size_t inputlen, n, bufsize; int np; + struct paste_buffer *pb; + u_char ch; switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYEDIT_CANCEL: @@ -722,6 +790,21 @@ window_copy_key_input(struct window_pane *wp, int key) case MODEKEYEDIT_DELETELINE: *data->inputstr = '\0'; break; + case MODEKEYEDIT_PASTE: + if ((pb = paste_get_top(NULL)) == NULL) + break; + bufdata = paste_buffer_data(pb, &bufsize); + for (n = 0; n < bufsize; n++) { + ch = (u_char)bufdata[n]; + if (ch < 32 || ch == 127) + break; + } + inputlen = strlen(data->inputstr); + + data->inputstr = xrealloc(data->inputstr, inputlen + n + 1); + memcpy(data->inputstr + inputlen, bufdata, n); + data->inputstr[inputlen + n] = '\0'; + break; case MODEKEYEDIT_ENTER: np = data->numprefix; if (np <= 0) @@ -747,6 +830,16 @@ window_copy_key_input(struct window_pane *wp, int key) data->searchtype = data->inputtype; data->searchstr = xstrdup(data->inputstr); break; + case WINDOW_COPY_NAMEDBUFFER: + window_copy_copy_selection(wp, data->inputstr); + *data->inputstr = '\0'; + if (data->inputexit) { + window_pane_reset_mode(wp); + return (0); + } + window_copy_clear_selection(wp); + window_copy_redraw_screen(wp); + break; case WINDOW_COPY_GOTOLINE: window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; @@ -758,11 +851,10 @@ window_copy_key_input(struct window_pane *wp, int key) data->password_cb_private); } *data->inputstr = '\0'; - window_copy_copy_selection(wp, -1); + window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); #endif } - data->numprefix = -1; return (1); case MODEKEY_OTHER: @@ -770,7 +862,7 @@ window_copy_key_input(struct window_pane *wp, int key) break; inputlen = strlen(data->inputstr) + 2; - data->inputstr = xrealloc(data->inputstr, 1, inputlen); + data->inputstr = xrealloc(data->inputstr, inputlen); data->inputstr[inputlen - 2] = key; data->inputstr[inputlen - 1] = '\0'; break; @@ -783,7 +875,7 @@ window_copy_key_input(struct window_pane *wp, int key) } int -window_copy_key_numeric_prefix(struct window_pane *wp, int key) +window_copy_key_numeric_prefix(struct window_pane *wp, key_code key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -800,76 +892,6 @@ window_copy_key_numeric_prefix(struct window_pane *wp, int key) return (0); } -static void -__window_copy_mouse( - struct window_pane *wp, struct session *sess, struct mouse_event *m) -{ - struct window_copy_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - u_int i; - - if (m->x >= screen_size_x(s)) - return; - if (m->y >= screen_size_y(s)) - return; - - /* If mouse wheel (buttons 4 and 5), scroll. */ - if (m->event == MOUSE_EVENT_WHEEL) { - if (m->wheel == MOUSE_WHEEL_UP) { - for (i = 0; i < 5; i++) - window_copy_cursor_up(wp, 1); - } else if (m->wheel == MOUSE_WHEEL_DOWN) { - for (i = 0; i < 5; i++) - window_copy_cursor_down(wp, 1); - if (data->oy == 0) - goto reset_mode; - } - return; - } - - /* - * If already reading motion, move the cursor while buttons are still - * pressed, or stop the selection on their release. - */ - if (s->mode & MODE_MOUSE_BUTTON) { - if (~m->event & MOUSE_EVENT_UP) { - window_copy_update_cursor(wp, m->x, m->y); - if (window_copy_update_selection(wp)) - window_copy_redraw_screen(wp); - return; - } - goto reset_mode; - } - - /* Otherwise if other buttons pressed, start selection and motion. */ - if (~m->event & MOUSE_EVENT_UP) { - s->mode &= ~MODE_MOUSE_STANDARD; - s->mode |= MODE_MOUSE_BUTTON; - - window_copy_update_cursor(wp, m->x, m->y); - window_copy_start_selection(wp); - window_copy_redraw_screen(wp); - } - - return; - -reset_mode: - s->mode &= ~MODE_MOUSE_BUTTON; - s->mode |= MODE_MOUSE_STANDARD; - if (sess != NULL) { - window_copy_copy_selection(wp, -1); - window_pane_reset_mode(wp); - } -} - -void -window_copy_mouse( - struct window_pane *wp, struct session *sess, struct mouse_event *m) -{ - __window_copy_mouse(wp, sess, m); - tmate_sync_copy_mode(wp); -} - void window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) { @@ -892,39 +914,46 @@ window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) } data->oy = gd->hsize - offset; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } int -window_copy_search_compare( - struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx) +window_copy_search_compare(struct grid *gd, u_int px, u_int py, + struct grid *sgd, u_int spx, int cis) { - const struct grid_cell *gc, *sgc; - struct utf8_data ud, sud; + struct grid_cell gc, sgc; + const struct utf8_data *ud, *sud; - gc = grid_peek_cell(gd, px, py); - grid_cell_get(gc, &ud); - sgc = grid_peek_cell(sgd, spx, 0); - grid_cell_get(sgc, &sud); + grid_get_cell(gd, px, py, &gc); + ud = &gc.data; + grid_get_cell(sgd, spx, 0, &sgc); + sud = &sgc.data; - if (ud.size != sud.size || ud.width != sud.width) + if (ud->size != sud->size || ud->width != sud->width) return (0); - return (memcmp(ud.data, sud.data, ud.size) == 0); + + if (cis && ud->size == 1) + return (tolower(ud->data[0]) == sud->data[0]); + + return (memcmp(ud->data, sud->data, ud->size) == 0); } int window_copy_search_lr(struct grid *gd, - struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) + struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; + int matched; for (ax = first; ax < last; ax++) { if (ax + sgd->sx >= gd->sx) break; for (bx = 0; bx < sgd->sx; bx++) { px = ax + bx; - if (!window_copy_search_compare(gd, px, py, sgd, bx)) + matched = window_copy_search_compare(gd, px, py, sgd, + bx, cis); + if (!matched) break; } if (bx == sgd->sx) { @@ -937,16 +966,19 @@ window_copy_search_lr(struct grid *gd, int window_copy_search_rl(struct grid *gd, - struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) + struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; + int matched; for (ax = last + 1; ax > first; ax--) { if (gd->sx - (ax - 1) < sgd->sx) continue; for (bx = 0; bx < sgd->sx; bx++) { px = ax - 1 + bx; - if (!window_copy_search_compare(gd, px, py, sgd, bx)) + matched = window_copy_search_compare(gd, px, py, sgd, + bx, cis); + if (!matched) break; } if (bx == sgd->sx) { @@ -967,18 +999,18 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, last, fx, fy, px; - int utf8flag, n, wrapped, wrapflag; + int n, wrapped, wrapflag, cis; + const char *ptr; if (*searchstr == '\0') return; - utf8flag = options_get_number(&wp->window->options, "utf8"); - wrapflag = options_get_number(&wp->window->options, "wrap-search"); - searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + wrapflag = options_get_number(wp->window->options, "wrap-search"); + searchlen = screen_write_strlen("%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); - screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_nputs(&ctx, -1, &gc, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; @@ -993,13 +1025,21 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) fx--; n = wrapped = 0; + cis = 1; + for (ptr = searchstr; *ptr != '\0'; ptr++) { + if (*ptr != tolower((u_char)*ptr)) { + cis = 0; + break; + } + } + retry: sgd = ss.grid; for (i = fy + 1; i > 0; i--) { last = screen_size_x(s); if (i == fy + 1) last = fx; - n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last); + n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last, cis); if (n) { window_copy_scroll_to(wp, px, i - 1); break; @@ -1025,18 +1065,18 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, first, fx, fy, px; - int utf8flag, n, wrapped, wrapflag; + int n, wrapped, wrapflag, cis; + const char *ptr; if (*searchstr == '\0') return; - utf8flag = options_get_number(&wp->window->options, "utf8"); - wrapflag = options_get_number(&wp->window->options, "wrap-search"); - searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + wrapflag = options_get_number(wp->window->options, "wrap-search"); + searchlen = screen_write_strlen("%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); - screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_nputs(&ctx, -1, &gc, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; @@ -1051,13 +1091,22 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) fx++; n = wrapped = 0; + cis = 1; + for (ptr = searchstr; *ptr != '\0'; ptr++) { + if (*ptr != tolower((u_char)*ptr)) { + cis = 0; + break; + } + } + retry: sgd = ss.grid; for (i = fy + 1; i < gd->hsize + gd->sy + 1; i++) { first = 0; if (i == fy + 1) first = fx; - n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx); + n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx, + cis); if (n) { window_copy_scroll_to(wp, px, i - 1); break; @@ -1085,26 +1134,22 @@ window_copy_goto_line(struct window_pane *wp, const char *linestr) return; data->oy = lineno; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } void -window_copy_write_line( - struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, + u_int py) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct grid_cell gc; -#ifdef TMATE - char hdr[256]; -#else - char hdr[32]; -#endif - size_t last, xoff = 0, size = 0; + char hdr[512]; + size_t last, xoff = 0, size = 0, limit; - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); last = screen_size_y(s) - 1; if (py == 0) { @@ -1121,11 +1166,13 @@ window_copy_write_line( } #endif } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { + limit = sizeof hdr; + if (limit > screen_size_x(s) + 1) + limit = screen_size_x(s) + 1; if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { - xoff = size = xsnprintf(hdr, sizeof hdr, - "Repeat: %u", data->numprefix); + xoff = size = xsnprintf(hdr, limit, + "Repeat: %d", data->numprefix); } else { - #ifdef TMATE if (data->inputtype == WINDOW_COPY_PASSWORD) { int password_len = strlen(data->inputstr); @@ -1137,7 +1184,7 @@ window_copy_write_line( } else #endif - xoff = size = xsnprintf(hdr, sizeof hdr, + xoff = size = xsnprintf(hdr, limit, "%s: %s", data->inputprompt, data->inputstr); } screen_write_cursormove(ctx, 0, last); @@ -1145,10 +1192,12 @@ window_copy_write_line( } else size = 0; - screen_write_cursormove(ctx, xoff, py); - screen_write_copy(ctx, data->backing, xoff, - (screen_hsize(data->backing) - data->oy) + py, - screen_size_x(s) - size, 1); + if (size < screen_size_x(s)) { + screen_write_cursormove(ctx, xoff, py); + screen_write_copy(ctx, data->backing, xoff, + (screen_hsize(data->backing) - data->oy) + py, + screen_size_x(s) - size, 1); + } if (py == data->cy && data->cx == screen_size_x(s)) { memcpy(&gc, &grid_default_cell, sizeof gc); @@ -1158,8 +1207,8 @@ window_copy_write_line( } void -window_copy_write_lines( - struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny) +window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx, + u_int py, u_int ny) { u_int yy; @@ -1167,6 +1216,23 @@ window_copy_write_lines( window_copy_write_line(wp, ctx, py); } +void +window_copy_redraw_selection(struct window_pane *wp, u_int old_y) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int new_y, start, end; + + new_y = data->cy; + if (old_y <= new_y) { + start = old_y; + end = new_y; + } else { + start = new_y; + end = old_y; + } + window_copy_redraw_lines(wp, start, end - start + 1); +} + void window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) { @@ -1220,23 +1286,23 @@ window_copy_start_selection(struct window_pane *wp) data->sely = screen_hsize(data->backing) + data->cy - data->oy; s->sel.flag = 1; - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); } int -window_copy_update_selection(struct window_pane *wp) +window_copy_update_selection(struct window_pane *wp, int may_redraw) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct grid_cell gc; u_int sx, sy, ty, cy; - if (!s->sel.flag) + if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) return (0); /* Set colours. */ - window_mode_attrs(&gc, oo); + style_apply(&gc, oo, "mode-style"); /* Find top of screen. */ ty = screen_hsize(data->backing) - data->oy; @@ -1259,7 +1325,7 @@ window_copy_update_selection(struct window_pane *wp) screen_set_selection(s, sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc); - if (data->rectflag) { + if (data->rectflag && may_redraw) { /* * Can't rely on the caller to redraw the right lines for * rectangle selection - find the highest line and the number @@ -1282,11 +1348,11 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) struct screen *s = &data->screen; char *buf; size_t off; - u_int i, xx, yy, sx, sy, ex, ey; + u_int i, xx, yy, sx, sy, ex, ey, ey_last; u_int firstsx, lastex, restex, restsx; int keys; - if (!s->sel.flag) + if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) return (NULL); buf = xmalloc(1); @@ -1311,9 +1377,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) } /* Trim ex to end of line. */ - xx = window_copy_find_length(wp, ey); - if (ex > xx) - ex = xx; + ey_last = window_copy_find_length(wp, ey); + if (ex > ey_last) + ex = ey_last; /* * Deal with rectangle-copy if necessary; four situations: start of @@ -1328,7 +1394,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) * bottom-right-most, regardless of copy direction. If it is vi, also * keep bottom-right-most character. */ - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (data->rectflag) { /* * Need to ignore the column with the cursor in it, which for @@ -1364,17 +1430,10 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) } /* Copy the lines. */ - if (sy == ey) - window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); - else { - window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); - if (ey - sy > 1) { - for (i = sy + 1; i < ey; i++) { - window_copy_copy_line( - wp, &buf, &off, i, restsx, restex); - } - } - window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex); + for (i = sy; i <= ey; i++) { + window_copy_copy_line(wp, &buf, &off, i, + (i == sy ? firstsx : restsx), + (i == ey ? lastex : restex)); } /* Don't bother if no data. */ @@ -1382,71 +1441,113 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) free(buf); return (NULL); } - *len = off - 1; /* remove final \n */ + if (keys == MODEKEY_EMACS || lastex <= ey_last) + off -= 1; /* remove final \n (unless at end in vi mode) */ + *len = off; return (buf); } void -window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) +window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, + size_t len) { - u_int limit; struct screen_write_ctx ctx; - if (options_get_number(&global_options, "set-clipboard")) { + if (options_get_number(global_options, "set-clipboard")) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); } - if (idx == -1) { - limit = options_get_number(&global_options, "buffer-limit"); - paste_add(&global_buffers, buf, len, limit); - } else - paste_replace(&global_buffers, idx, buf, len); + if (paste_set(buf, len, bufname, NULL) != 0) + free(buf); } void -window_copy_copy_pipe( - struct window_pane *wp, struct session *sess, int idx, const char *arg) +window_copy_copy_pipe(struct window_pane *wp, struct session *sess, + const char *bufname, const char *arg) { - void *buf; - size_t len; - struct job *job; - + void *buf; + size_t len; + struct job *job; + struct format_tree *ft; + char *expanded; buf = window_copy_get_selection(wp, &len); if (buf == NULL) return; - job = job_run(arg, sess, NULL, NULL, NULL); + ft = format_create(NULL, 0); + format_defaults(ft, NULL, sess, NULL, wp); + expanded = format_expand(ft, arg); + + job = job_run(expanded, sess, NULL, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); - window_copy_copy_buffer(wp, idx, buf, len); + free(expanded); + format_free(ft); + + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_copy_selection(struct window_pane *wp, int idx) +window_copy_copy_selection(struct window_pane *wp, const char *bufname) { - void* buf; - size_t len; + void *buf; + size_t len; buf = window_copy_get_selection(wp, &len); if (buf == NULL) return; - window_copy_copy_buffer(wp, idx, buf, len); + window_copy_copy_buffer(wp, bufname, buf, len); } void -window_copy_copy_line(struct window_pane *wp, - char **buf, size_t *off, u_int sy, u_int sx, u_int ex) +window_copy_append_selection(struct window_pane *wp, const char *bufname) +{ + char *buf; + struct paste_buffer *pb; + const char *bufdata; + size_t len, bufsize; + struct screen_write_ctx ctx; + + buf = window_copy_get_selection(wp, &len); + if (buf == NULL) + return; + + if (options_get_number(global_options, "set-clipboard")) { + screen_write_start(&ctx, wp, NULL); + screen_write_setselection(&ctx, buf, len); + screen_write_stop(&ctx); + } + + if (bufname == NULL || *bufname == '\0') + pb = paste_get_top(&bufname); + else + pb = paste_get_name(bufname); + if (pb != NULL) { + bufdata = paste_buffer_data(pb, &bufsize); + buf = xrealloc(buf, len + bufsize); + memmove(buf + bufsize, buf, len); + memcpy(buf, bufdata, bufsize); + len += bufsize; + } + if (paste_set(buf, len, bufname, NULL) != 0) + free(buf); +} + +void +window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, + u_int sx, u_int ex) { struct window_copy_mode_data *data = wp->modedata; struct grid *gd = data->backing->grid; - const struct grid_cell *gc; + struct grid_cell gc; struct grid_line *gl; struct utf8_data ud; u_int i, xx, wrapped = 0; + const char *s; if (sx > ex) return; @@ -1471,12 +1572,19 @@ window_copy_copy_line(struct window_pane *wp, if (sx < ex) { for (i = sx; i < ex; i++) { - gc = grid_peek_cell(gd, i, sy); - if (gc->flags & GRID_FLAG_PADDING) + grid_get_cell(gd, i, sy, &gc); + if (gc.flags & GRID_FLAG_PADDING) continue; - grid_cell_get(gc, &ud); + utf8_copy(&ud, &gc.data); + if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { + s = tty_acs_get(NULL, ud.data[0]); + if (s != NULL && strlen(s) <= sizeof ud.data) { + ud.size = strlen(s); + memcpy(ud.data, s, ud.size); + } + } - *buf = xrealloc(*buf, 1, (*off) + ud.size); + *buf = xrealloc(*buf, (*off) + ud.size); memcpy(*buf + *off, ud.data, ud.size); *off += ud.size; } @@ -1484,7 +1592,7 @@ window_copy_copy_line(struct window_pane *wp, /* Only add a newline if the line wasn't wrapped. */ if (!wrapped || ex != xx) { - *buf = xrealloc(*buf, 1, (*off) + 1); + *buf = xrealloc(*buf, (*off) + 1); (*buf)[(*off)++] = '\n'; } } @@ -1507,16 +1615,17 @@ int window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) { struct window_copy_mode_data *data = wp->modedata; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; + const struct utf8_data *ud; - gc = grid_peek_cell(data->backing->grid, px, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || gc->flags & GRID_FLAG_PADDING) + grid_get_cell(data->backing->grid, px, py, &gc); + + ud = &gc.data; + if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING)) return (0); - if (*ud.data == 0x00 || *ud.data == 0x7f) + if (*ud->data == 0x00 || *ud->data == 0x7f) return (0); - return (strchr(set, *ud.data) != NULL); + return (strchr(set, *ud->data) != NULL); } u_int @@ -1524,8 +1633,7 @@ window_copy_find_length(struct window_pane *wp, u_int py) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px; /* @@ -1538,9 +1646,8 @@ window_copy_find_length(struct window_pane *wp, u_int py) if (px > screen_size_x(s)) px = screen_size_x(s); while (px > 0) { - gc = grid_peek_cell(s->grid, px - 1, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || *ud.data != ' ') + grid_get_cell(s->grid, px - 1, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') break; px--; } @@ -1552,18 +1659,20 @@ window_copy_cursor_start_of_line(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; + struct screen *s = &data->screen; struct grid *gd = back_s->grid; u_int py; - if (data->cx == 0) { + if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) { py = screen_hsize(back_s) + data->cy - data->oy; - while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) { + while (py > 0 && + gd->linedata[py-1].flags & GRID_LINE_WRAPPED) { window_copy_cursor_up(wp, 0); py = screen_hsize(back_s) + data->cy - data->oy; } } window_copy_update_cursor(wp, 0, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -1572,23 +1681,21 @@ window_copy_cursor_back_to_indentation(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py, xx; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; px = 0; py = screen_hsize(data->backing) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(data->backing->grid, px, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || *ud.data != ' ') + grid_get_cell(data->backing->grid, px, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') break; px++; } window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -1597,13 +1704,14 @@ window_copy_cursor_end_of_line(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; + struct screen *s = &data->screen; struct grid *gd = back_s->grid; u_int px, py; py = screen_hsize(back_s) + data->cy - data->oy; px = window_copy_find_length(wp, py); - if (data->cx == px) { + if (data->cx == px && s->sel.lineflag == LINE_SEL_NONE) { if (data->screen.sel.flag && data->rectflag) px = screen_size_x(back_s); if (gd->linedata[py].flags & GRID_LINE_WRAPPED) { @@ -1618,10 +1726,48 @@ window_copy_cursor_end_of_line(struct window_pane *wp) } window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } +void +window_copy_other_end(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int selx, sely, cx, cy, yy, hsize; + + if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) + return; + + if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) + s->sel.lineflag = LINE_SEL_RIGHT_LEFT; + else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) + s->sel.lineflag = LINE_SEL_LEFT_RIGHT; + + selx = data->selx; + sely = data->sely; + cx = data->cx; + cy = data->cy; + yy = screen_hsize(data->backing) + data->cy - data->oy; + + data->selx = cx; + data->sely = yy; + data->cx = selx; + + hsize = screen_hsize(data->backing); + if (sely < hsize - data->oy) { + data->oy = hsize - sely; + data->cy = 0; + } else if (sely > hsize - data->oy + screen_size_y(s)) { + data->oy = hsize - sely + screen_size_y(s) - 1; + data->cy = screen_size_y(s) - 1; + } else + data->cy = cy + sely - yy; + + window_copy_redraw_screen(wp); +} + void window_copy_cursor_left(struct window_pane *wp) { @@ -1632,7 +1778,7 @@ window_copy_cursor_left(struct window_pane *wp) window_copy_cursor_end_of_line(wp); } else { window_copy_update_cursor(wp, data->cx - 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } } @@ -1655,7 +1801,7 @@ window_copy_cursor_right(struct window_pane *wp) window_copy_cursor_down(wp, 0); } else { window_copy_update_cursor(wp, data->cx + 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } } @@ -1674,6 +1820,9 @@ window_copy_cursor_up(struct window_pane *wp, int scroll_only) data->lastsx = ox; } + if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) + window_copy_other_end(wp); + data->cx = data->lastcx; if (scroll_only || data->cy == 0) { window_copy_scroll_down(wp, 1); @@ -1685,7 +1834,7 @@ window_copy_cursor_up(struct window_pane *wp, int scroll_only) } } else { window_copy_update_cursor(wp, data->cx, data->cy - 1); - if (window_copy_update_selection(wp)) { + if (window_copy_update_selection(wp, 1)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wp, data->cy, 1); else @@ -1700,6 +1849,11 @@ window_copy_cursor_up(struct window_pane *wp, int scroll_only) data->cx > px) window_copy_cursor_end_of_line(wp); } + + if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) + window_copy_cursor_end_of_line(wp); + else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) + window_copy_cursor_start_of_line(wp); } void @@ -1716,6 +1870,9 @@ window_copy_cursor_down(struct window_pane *wp, int scroll_only) data->lastsx = ox; } + if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely) + window_copy_other_end(wp); + data->cx = data->lastcx; if (scroll_only || data->cy == screen_size_y(s) - 1) { window_copy_scroll_up(wp, 1); @@ -1723,7 +1880,7 @@ window_copy_cursor_down(struct window_pane *wp, int scroll_only) window_copy_redraw_lines(wp, data->cy - 1, 2); } else { window_copy_update_cursor(wp, data->cx, data->cy + 1); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy - 1, 2); } @@ -1734,6 +1891,11 @@ window_copy_cursor_down(struct window_pane *wp, int scroll_only) data->cx > px) window_copy_cursor_end_of_line(wp); } + + if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) + window_copy_cursor_end_of_line(wp); + else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) + window_copy_cursor_start_of_line(wp); } void @@ -1741,8 +1903,7 @@ window_copy_cursor_jump(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py, xx; px = data->cx + 1; @@ -1750,12 +1911,11 @@ window_copy_cursor_jump(struct window_pane *wp) xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1768,8 +1928,7 @@ window_copy_cursor_jump_back(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py; px = data->cx; @@ -1779,12 +1938,11 @@ window_copy_cursor_jump_back(struct window_pane *wp) px--; for (;;) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1795,25 +1953,23 @@ window_copy_cursor_jump_back(struct window_pane *wp) } void -window_copy_cursor_jump_to(struct window_pane *wp) +window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py, xx; - px = data->cx + 1; + px = data->cx + 1 + jump_again; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px - 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1822,12 +1978,11 @@ window_copy_cursor_jump_to(struct window_pane *wp) } void -window_copy_cursor_jump_to_back(struct window_pane *wp) +window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py; px = data->cx; @@ -1836,13 +1991,15 @@ window_copy_cursor_jump_to_back(struct window_pane *wp) if (px > 0) px--; + if (jump_again && px > 0) + px--; + for (;;) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px + 1, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } @@ -1890,23 +2047,29 @@ window_copy_cursor_next_word(struct window_pane *wp, const char *separators) } while (expected == 1); window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } void -window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) +window_copy_cursor_next_word_end(struct window_pane *wp, + const char *separators) { struct window_copy_mode_data *data = wp->modedata; + struct options *oo = wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; - int expected = 1; + int keys, expected = 1; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + keys = options_get_number(oo, "mode-keys"); + if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators)) + px++; + /* * First skip past any word characters, then any nonword characters. * @@ -1931,14 +2094,18 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) expected = !expected; } while (expected == 0); + if (keys == MODEKEY_VI && px != 0) + px--; + window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } /* Move to the previous place where a word begins. */ void -window_copy_cursor_previous_word(struct window_pane *wp, const char *separators) +window_copy_cursor_previous_word(struct window_pane *wp, + const char *separators) { struct window_copy_mode_data *data = wp->modedata; u_int px, py; @@ -1970,7 +2137,7 @@ window_copy_cursor_previous_word(struct window_pane *wp, const char *separators) out: window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp)) + if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); } @@ -1987,6 +2154,8 @@ window_copy_scroll_up(struct window_pane *wp, u_int ny) return; data->oy -= ny; + window_copy_update_selection(wp, 0); + screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_deleteline(&ctx, ny); @@ -1996,12 +2165,9 @@ window_copy_scroll_up(struct window_pane *wp, u_int ny) window_copy_write_line(wp, &ctx, 1); if (screen_size_y(s) > 3) window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); - if (s->sel.flag && screen_size_y(s) > ny) { - window_copy_update_selection(wp); + if (s->sel.flag && screen_size_y(s) > ny) window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); - } screen_write_cursormove(&ctx, data->cx, data->cy); - window_copy_update_selection(wp); screen_write_stop(&ctx); } @@ -2021,20 +2187,30 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny) return; data->oy += ny; + window_copy_update_selection(wp, 0); + screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_insertline(&ctx, ny); window_copy_write_lines(wp, &ctx, 0, ny); - if (s->sel.flag && screen_size_y(s) > ny) { - window_copy_update_selection(wp); + if (s->sel.flag && screen_size_y(s) > ny) window_copy_write_line(wp, &ctx, ny); - } else if (ny == 1) /* nuke position */ + else if (ny == 1) /* nuke position */ window_copy_write_line(wp, &ctx, 1); screen_write_cursormove(&ctx, data->cx, data->cy); - window_copy_update_selection(wp); screen_write_stop(&ctx); } +int +window_copy_scroll_position(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + if (wp->mode != &window_copy_mode) + return (-1); + return (data->oy); +} + void window_copy_rectangle_toggle(struct window_pane *wp) { @@ -2048,6 +2224,61 @@ window_copy_rectangle_toggle(struct window_pane *wp) if (data->cx > px) window_copy_update_cursor(wp, px, data->cy); - window_copy_update_selection(wp); + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } + +void +window_copy_start_drag(struct client *c, struct mouse_event *m) +{ + struct window_pane *wp; + u_int x, y; + + wp = cmd_mouse_pane(m, NULL, NULL); + if (wp == NULL || wp->mode != &window_copy_mode) + return; + + if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) + return; + + c->tty.mouse_drag_update = window_copy_drag_update; + c->tty.mouse_drag_release = window_copy_drag_release; + + window_copy_update_cursor(wp, x, y); + window_copy_start_selection(wp); + window_copy_redraw_screen(wp); +} + +void +window_copy_drag_update(__unused struct client *c, struct mouse_event *m) +{ + struct window_pane *wp; + struct window_copy_mode_data *data; + u_int x, y, old_cy; + + wp = cmd_mouse_pane(m, NULL, NULL); + if (wp == NULL || wp->mode != &window_copy_mode) + return; + data = wp->modedata; + + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) + return; + old_cy = data->cy; + + window_copy_update_cursor(wp, x, y); + if (window_copy_update_selection(wp, 1)) + window_copy_redraw_selection(wp, old_cy); +} + +void +window_copy_drag_release(__unused struct client *c, struct mouse_event *m) +{ + struct window_pane *wp; + + wp = cmd_mouse_pane(m, NULL, NULL); + if (wp == NULL || wp->mode != &window_copy_mode) + return; + + window_copy_copy_selection(wp, NULL); + window_pane_reset_mode(wp); +} diff --git a/window-copy.h b/window-copy.h new file mode 100644 index 00000000..29e74ad8 --- /dev/null +++ b/window-copy.h @@ -0,0 +1,87 @@ +#ifndef WINDOW_COPY_H +#define WINDOW_COPY_H + +#include "tmux.h" + +enum window_copy_input_type { + WINDOW_COPY_OFF, + WINDOW_COPY_NAMEDBUFFER, + WINDOW_COPY_NUMERICPREFIX, + WINDOW_COPY_SEARCHUP, + WINDOW_COPY_SEARCHDOWN, + WINDOW_COPY_JUMPFORWARD, + WINDOW_COPY_JUMPBACK, + WINDOW_COPY_JUMPTOFORWARD, + WINDOW_COPY_JUMPTOBACK, + WINDOW_COPY_GOTOLINE, +#ifdef TMATE + WINDOW_COPY_PASSWORD, +#endif +}; + +/* + * Copy-mode's visible screen (the "screen" field) is filled from one of + * two sources: the original contents of the pane (used when we + * actually enter via the "copy-mode" command, to copy the contents of + * the current pane), or else a series of lines containing the output + * from an output-writing tmux command (such as any of the "show-*" or + * "list-*" commands). + * + * In either case, the full content of the copy-mode grid is pointed at + * by the "backing" field, and is copied into "screen" as needed (that + * is, when scrolling occurs). When copy-mode is backed by a pane, + * backing points directly at that pane's screen structure (&wp->base); + * when backed by a list of output-lines from a command, it points at + * a newly-allocated screen structure (which is deallocated when the + * mode ends). + */ + +#ifdef TMATE +typedef void (*copy_password_callback)(const char *password, void *private); +#endif + +struct window_copy_mode_data { + struct screen screen; + + struct screen *backing; + int backing_written; /* backing display started */ + + struct mode_key_data mdata; + + u_int oy; + + u_int selx; + u_int sely; + + int rectflag; /* in rectangle copy mode? */ + int scroll_exit; /* exit on scroll to end? */ + + u_int cx; + u_int cy; + + u_int lastcx; /* position in last line w/ content */ + u_int lastsx; /* size of last line w/ content */ + + enum window_copy_input_type inputtype; + const char *inputprompt; + char *inputstr; + int inputexit; + + int numprefix; + + enum window_copy_input_type searchtype; + char *searchstr; + + enum window_copy_input_type jumptype; + char jumpchar; + +#ifdef TMATE + copy_password_callback password_cb; + void *password_cb_private; +#endif +}; + +extern int window_copy_update_selection(struct window_pane *, int); +extern void window_copy_redraw_screen(struct window_pane *); + +#endif diff --git a/window.c b/window.c index 5757cf88..6de39d8a 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -17,13 +17,11 @@ */ #include -#include #include #include #include -#include -#include +#include #include #include #include @@ -58,11 +56,22 @@ struct windows windows; struct window_pane_tree all_window_panes; u_int next_window_pane_id; u_int next_window_id; +u_int next_active_point; void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); +struct window_pane *window_pane_choose_best(struct window_pane **, u_int); + +RB_GENERATE(windows, window, entry, window_cmp); + +int +window_cmp(struct window *w1, struct window *w2) +{ + return (w1->id - w2->id); +} + RB_GENERATE(winlinks, winlink, entry, winlink_cmp); int @@ -244,38 +253,43 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) } } -int -window_index(struct window *s, u_int *i) +struct window * +window_find_by_id_str(const char *s) { - for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) { - if (s == ARRAY_ITEM(&windows, *i)) - return (0); - } - return (-1); + const char *errstr; + u_int id; + + if (*s != '@') + return (NULL); + + id = strtonum(s + 1, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (NULL); + return (window_find_by_id(id)); } struct window * window_find_by_id(u_int id) { - struct window *w; - u_int i; + struct window w; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w->id == id) - return (w); - } - return (NULL); + w.id = id; + return (RB_FIND(windows, &windows, &w)); +} + +void +window_update_activity(struct window *w) +{ + gettimeofday(&w->activity_time, NULL); + alerts_queue(w, WINDOW_ACTIVITY); } struct window * window_create1(u_int sx, u_int sy) { struct window *w; - u_int i; w = xcalloc(1, sizeof *w); - w->id = next_window_id++; w->name = NULL; w->flags = 0; @@ -288,27 +302,22 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; - options_init(&w->options, &global_w_options); - if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); + w->options = options_create(global_w_options); - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if (ARRAY_ITEM(&windows, i) == NULL) { - ARRAY_SET(&windows, i, w); - break; - } - } - if (i == ARRAY_LENGTH(&windows)) - ARRAY_ADD(&windows, w); w->references = 0; + w->id = next_window_id++; + RB_INSERT(windows, &windows, w); + + window_update_activity(w); + return (w); } struct window * -window_create(const char *name, const char *cmd, const char *shell, - const char *cwd, struct environ *env, struct termios *tio, - u_int sx, u_int sy, u_int hlimit, char **cause) +window_create(const char *name, int argc, char **argv, const char *path, + const char *shell, const char *cwd, struct environ *env, + struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; struct window_pane *wp; @@ -317,7 +326,8 @@ window_create(const char *name, const char *cmd, const char *shell, wp = window_add_pane(w, hlimit); layout_init(w, wp); - if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) { + if (window_pane_spawn(wp, argc, argv, path, shell, cwd, env, tio, + cause) != 0) { window_destroy(w); return (NULL); } @@ -325,7 +335,7 @@ window_create(const char *name, const char *cmd, const char *shell, w->active = TAILQ_FIRST(&w->panes); if (name != NULL) { w->name = xstrdup(name); - options_set_number(&w->options, "automatic-rename", 0); + options_set_number(w->options, "automatic-rename", 0); } else w->name = default_window_name(w); @@ -335,23 +345,21 @@ window_create(const char *name, const char *cmd, const char *shell, void window_destroy(struct window *w) { - u_int i; - - window_unzoom(w); - - if (window_index(w, &i) != 0) - fatalx("index not found"); - ARRAY_SET(&windows, i, NULL); - while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) - ARRAY_TRUNC(&windows, 1); + RB_REMOVE(windows, &windows, w); if (w->layout_root != NULL) - layout_free(w); + layout_free_cell(w->layout_root); + if (w->saved_layout_root != NULL) + layout_free_cell(w->saved_layout_root); + free(w->old_layout); - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + if (event_initialized(&w->name_event)) + evtimer_del(&w->name_event); - options_free(&w->options); + if (event_initialized(&w->alerts_timer)) + evtimer_del(&w->alerts_timer); + + options_free(w->options); window_destroy_panes(w); @@ -385,7 +393,9 @@ window_set_name(struct window *w, const char *new_name) free(w->name); w->name = xstrdup(new_name); notify_window_renamed(w); +#ifdef TMATE tmate_sync_layout(); +#endif } void @@ -395,11 +405,23 @@ window_resize(struct window *w, u_int sx, u_int sy) w->sy = sy; } -void +int +window_has_pane(struct window *w, struct window_pane *wp) +{ + struct window_pane *wp1; + + TAILQ_FOREACH(wp1, &w->panes, entry) { + if (wp1 == wp) + return (1); + } + return (0); +} + +int window_set_active_pane(struct window *w, struct window_pane *wp) { if (wp == w->active) - return; + return (0); w->last = w->active; w->active = wp; while (!window_pane_visible(w->active)) { @@ -407,8 +429,35 @@ window_set_active_pane(struct window *w, struct window_pane *wp) if (w->active == NULL) w->active = TAILQ_LAST(&w->panes, window_panes); if (w->active == wp) - return; + return (1); } + w->active->active_point = next_active_point++; + w->active->flags |= PANE_CHANGED; + return (1); +} + +void +window_redraw_active_switch(struct window *w, struct window_pane *wp) +{ + const struct grid_cell *agc, *wgc; + + if (wp == w->active) + return; + + /* + * If window-style and window-active-style are the same, we don't need + * to redraw panes when switching active panes. Otherwise, if the + * active or inactive pane do not have a custom style, they will need + * to be redrawn. + */ + agc = options_get_style(w->options, "window-active-style"); + wgc = options_get_style(w->options, "window-style"); + if (style_equal(agc, wgc)) + return; + if (style_equal(&grid_default_cell, &w->active->colgc)) + w->active->flags |= PANE_REDRAW; + if (style_equal(&grid_default_cell, &wp->colgc)) + wp->flags |= PANE_REDRAW; } struct window_pane * @@ -428,16 +477,6 @@ window_get_active_at(struct window *w, u_int x, u_int y) return (NULL); } -void -window_set_active_at(struct window *w, u_int x, u_int y) -{ - struct window_pane *wp; - - wp = window_get_active_at(w, x, y); - if (wp != NULL && wp != w->active) - window_set_active_pane(w, wp); -} - struct window_pane * window_find_string(struct window *w, const char *s) { @@ -498,6 +537,7 @@ window_zoom(struct window_pane *wp) w->saved_layout_root = w->layout_root; layout_init(w, wp); w->flags |= WINDOW_ZOOMED; + notify_window_layout_changed(w); return (0); } @@ -513,12 +553,14 @@ window_unzoom(struct window *w) w->flags &= ~WINDOW_ZOOMED; layout_free(w); w->layout_root = w->saved_layout_root; + w->saved_layout_root = NULL; TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; } layout_fix_panes(w, w->sx, w->sy); + notify_window_layout_changed(w); return (0); } @@ -537,8 +579,11 @@ window_add_pane(struct window *w, u_int hlimit) } void -window_remove_pane(struct window *w, struct window_pane *wp) +window_lost_pane(struct window *w, struct window_pane *wp) { + if (wp == marked_pane.wp) + server_clear_marked(); + if (wp == w->active) { w->active = w->last; w->last = NULL; @@ -547,8 +592,16 @@ window_remove_pane(struct window *w, struct window_pane *wp) if (w->active == NULL) w->active = TAILQ_NEXT(wp, entry); } + if (w->active != NULL) + w->active->flags |= PANE_CHANGED; } else if (wp == w->last) w->last = NULL; +} + +void +window_remove_pane(struct window *w, struct window_pane *wp) +{ + window_lost_pane(w, wp); TAILQ_REMOVE(&w->panes, wp, entry); window_pane_destroy(wp); @@ -560,7 +613,7 @@ window_pane_at_index(struct window *w, u_int idx) struct window_pane *wp; u_int n; - n = options_get_number(&w->options, "pane-base-index"); + n = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wp, &w->panes, entry) { if (n == idx) return (wp); @@ -598,7 +651,7 @@ window_pane_index(struct window_pane *wp, u_int *i) struct window_pane *wq; struct window *w = wp->window; - *i = options_get_number(&w->options, "pane-base-index"); + *i = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wq, &w->panes, entry) { if (wp == wq) { return (0); @@ -633,11 +686,11 @@ window_destroy_panes(struct window *w) } } -/* Return list of printable window flag symbols. No flags is just a space. */ +/* Retuns the printable flags on a window, empty string if no flags set. */ char * window_printable_flags(struct session *s, struct winlink *wl) { - char flags[BUFSIZ]; + char flags[32]; int pos; pos = 0; @@ -645,23 +698,35 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '#'; if (wl->flags & WINLINK_BELL) flags[pos++] = '!'; - if (wl->flags & WINLINK_CONTENT) - flags[pos++] = '+'; if (wl->flags & WINLINK_SILENCE) flags[pos++] = '~'; if (wl == s->curw) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; + if (server_check_marked() && wl == marked_pane.wl) + flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; - if (pos == 0) - flags[pos++] = ' '; flags[pos] = '\0'; return (xstrdup(flags)); } -/* Find pane in global tree by id. */ +struct window_pane * +window_pane_find_by_id_str(const char *s) +{ + const char *errstr; + u_int id; + + if (*s != '%') + return (NULL); + + id = strtonum(s + 1, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (NULL); + return (window_pane_find_by_id(id)); +} + struct window_pane * window_pane_find_by_id(u_int id) { @@ -675,6 +740,7 @@ struct window_pane * window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) { struct window_pane *wp; + char host[HOST_NAME_MAX + 1]; wp = xcalloc(1, sizeof *wp); wp->window = w; @@ -682,7 +748,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); - wp->cmd = NULL; + wp->argc = 0; + wp->argv = NULL; wp->shell = NULL; wp->cwd = NULL; @@ -703,13 +770,20 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_off = 0; wp->pipe_event = NULL; +#ifdef TMATE wp->tmate_off = 0; +#endif wp->saved_grid = NULL; + memcpy(&wp->colgc, &grid_default_cell, sizeof wp->colgc); + screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; + if (gethostname(host, sizeof host) == 0) + screen_set_title(&wp->base, host); + input_init(wp); return (wp); @@ -720,10 +794,13 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); - if (event_initialized(&wp->changes_timer)) - evtimer_del(&wp->changes_timer); + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); if (wp->fd != -1) { +#ifdef HAVE_UTEMPTER + utempter_remove_record(wp->fd); +#endif bufferevent_free(wp->event); close(wp->fd); } @@ -741,39 +818,48 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); - free(wp->cwd); + free((void *)wp->cwd); free(wp->shell); - free(wp->cmd); + cmd_free_argv(wp->argc, wp->argv); free(wp); } int -window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, - const char *cwd, struct environ *env, struct termios *tio, char **cause) +window_pane_spawn(struct window_pane *wp, int argc, char **argv, + const char *path, const char *shell, const char *cwd, struct environ *env, + struct termios *tio, char **cause) { struct winsize ws; - char *argv0, paneid[16]; - const char *ptr; + char *argv0, *cmd, **argvp; + const char *ptr, *first, *home; struct termios tio2; +#ifdef HAVE_UTEMPTER + char s[32]; +#endif + int i; if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); } - if (cmd != NULL) { - free(wp->cmd); - wp->cmd = xstrdup(cmd); + if (argc > 0) { + cmd_free_argv(wp->argc, wp->argv); + wp->argc = argc; + wp->argv = cmd_copy_argv(argc, argv); } if (shell != NULL) { free(wp->shell); wp->shell = xstrdup(shell); } if (cwd != NULL) { - free(wp->cwd); + free((void *)wp->cwd); wp->cwd = xstrdup(cwd); } - log_debug("spawn: %s -- %s", wp->shell, wp->cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); + log_debug("spawn: %s -- %s", wp->shell, cmd); + for (i = 0; i < wp->argc; i++) + log_debug("spawn: argv[%d] = %s", i, wp->argv[i]); memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&wp->base); @@ -783,10 +869,13 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, case -1: wp->fd = -1; xasprintf(cause, "%s: %s", cmd, strerror(errno)); + free(cmd); return (-1); case 0: - if (chdir(wp->cwd) != 0) - chdir("/"); + if (chdir(wp->cwd) != 0) { + if ((home = find_home()) == NULL || chdir(home) != 0) + chdir("/"); + } if (tcgetattr(STDIN_FILENO, &tio2) != 0) fatal("tcgetattr failed"); @@ -794,16 +883,16 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); tio2.c_cc[VERASE] = '\177'; #ifdef IUTF8 - if (options_get_number(&wp->window->options, "utf8")) - tio2.c_iflag |= IUTF8; + tio2.c_iflag |= IUTF8; #endif if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) fatal("tcgetattr failed"); closefrom(STDERR_FILENO + 1); - xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); - environ_set(env, "TMUX_PANE", paneid); + if (path != NULL) + environ_set(env, "PATH", "%s", path); + environ_set(env, "TMUX_PANE", "%%%u", wp->id); environ_push(env); clear_signals(1); @@ -812,115 +901,127 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, setenv("SHELL", wp->shell, 1); ptr = strrchr(wp->shell, '/'); - if (*wp->cmd != '\0') { - /* Use the command. */ + /* + * If given one argument, assume it should be passed to sh -c; + * with more than one argument, use execvp(). If there is no + * arguments, create a login shell. + */ + if (wp->argc > 0) { + if (wp->argc != 1) { + /* Copy to ensure argv ends in NULL. */ + argvp = cmd_copy_argv(wp->argc, wp->argv); + execvp(argvp[0], argvp); + fatal("execvp failed"); + } + first = wp->argv[0]; + if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "%s", ptr + 1); else xasprintf(&argv0, "%s", wp->shell); - execl(wp->shell, argv0, "-c", wp->cmd, (char *) NULL); + execl(wp->shell, argv0, "-c", first, (char *)NULL); fatal("execl failed"); } - - /* No command; fork a login shell. */ if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "-%s", ptr + 1); else xasprintf(&argv0, "-%s", wp->shell); - execl(wp->shell, argv0, (char *) NULL); + execl(wp->shell, argv0, (char *)NULL); fatal("execl failed"); } +#ifdef HAVE_UTEMPTER + xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id); + utempter_add_record(wp->fd, s); +#endif + setblocking(wp->fd, 0); - wp->event = bufferevent_new(wp->fd, - window_pane_read_callback, NULL, window_pane_error_callback, wp); + wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, + window_pane_error_callback, wp); + + bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); + free(cmd); return (0); } void -window_pane_timer_start(struct window_pane *wp) +window_pane_timer_callback(__unused int fd, __unused short events, void *data) { - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = 1000; - - evtimer_del(&wp->changes_timer); - evtimer_set(&wp->changes_timer, window_pane_timer_callback, wp); - evtimer_add(&wp->changes_timer, &tv); + window_pane_read_callback(NULL, data); } void -window_pane_timer_callback(unused int fd, unused short events, void *data) +window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; - struct window *w = wp->window; - u_int interval, trigger; + struct evbuffer *evb = wp->event->input; + char *new_data; + size_t new_size, available; + struct client *c; + struct timeval tv; - interval = options_get_number(&w->options, "c0-change-interval"); - trigger = options_get_number(&w->options, "c0-change-trigger"); + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); - if (wp->changes_redraw++ == interval) { - wp->flags |= PANE_REDRAW; - wp->changes_redraw = 0; + log_debug("%%%u has %zu bytes", wp->id, EVBUFFER_LENGTH(evb)); + TAILQ_FOREACH(c, &clients, entry) { + if (!tty_client_ready(c, wp)) + continue; + + available = EVBUFFER_LENGTH(c->tty.event->output); + if (available > READ_BACKOFF) + goto start_timer; } - if (trigger == 0 || wp->changes < trigger) { - wp->flags |= PANE_REDRAW; - wp->flags &= ~PANE_DROP; - } else - window_pane_timer_start(wp); - wp->changes = 0; -} - -void -window_pane_read_callback(unused struct bufferevent *bufev, void *data) -{ - struct window_pane *wp = data; - char *new_data; - size_t new_size; - - new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; + new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { +#ifdef TMATE /* FIXME tmux: - * - new_data = EVBUFFER_DATA(wp->event->input); - * + new_data = EVBUFFER_DATA(wp->event->input) + wp->pipe_off; - * also, can the buffer be too small? + * - new_data + * + new_data + wp->pipe_off; */ - new_data = EVBUFFER_DATA(wp->event->input); +#endif + new_data = EVBUFFER_DATA(evb); bufferevent_write(wp->pipe_event, new_data, new_size); } +#ifdef TMATE new_size = EVBUFFER_LENGTH(wp->event->input) - wp->tmate_off; new_data = EVBUFFER_DATA(wp->event->input) + wp->tmate_off; if (new_size > 0) tmate_pty_data(wp, new_data, new_size); +#endif input_parse(wp); - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); - wp->tmate_off = EVBUFFER_LENGTH(wp->event->input); + wp->pipe_off = EVBUFFER_LENGTH(evb); +#ifdef TMATE + wp->tmate_off = EVBUFFER_LENGTH(evb); +#endif + return; - /* - * 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."); +start_timer: + log_debug("%%%u backing off (%s %zu > %d)", wp->id, c->ttyname, + available, READ_BACKOFF); + + tv.tv_sec = 0; + tv.tv_usec = READ_TIME; + + evtimer_set(&wp->timer, window_pane_timer_callback, wp); + evtimer_add(&wp->timer, &tv); } void -window_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +window_pane_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { struct window_pane *wp = data; - server_destroy_pane(wp); + server_destroy_pane(wp, 1); } void @@ -951,7 +1052,7 @@ window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid != NULL) return; - if (!options_get_number(&wp->window->options, "alternate-screen")) + if (!options_get_number(wp->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -981,7 +1082,7 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid == NULL) return; - if (!options_get_number(&wp->window->options, "alternate-screen")) + if (!options_get_number(wp->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -1030,7 +1131,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) if ((s = wp->mode->init(wp)) != NULL) wp->screen = s; - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_CHANGED); return (0); } @@ -1044,60 +1145,47 @@ window_pane_reset_mode(struct window_pane *wp) wp->mode = NULL; wp->screen = &wp->base; - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_CHANGED); +#ifdef TMATE tmate_sync_copy_mode(wp); +#endif } void -window_pane_key(struct window_pane *wp, struct session *sess, int key) +window_pane_key(struct window_pane *wp, struct client *c, struct session *s, + key_code key, struct mouse_event *m) { struct window_pane *wp2; - if (!window_pane_visible(wp)) + if (KEYC_IS_MOUSE(key) && m == NULL) return; if (wp->mode != NULL) { if (wp->mode->key != NULL) - wp->mode->key(wp, sess, key); + wp->mode->key(wp, c, s, key, m); return; } - if (wp->fd == -1) + if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) return; - input_key(wp, key); - if (options_get_number(&wp->window->options, "synchronize-panes")) { + + input_key(wp, key, m); + + if (KEYC_IS_MOUSE(key)) + return; + if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; - if (wp2->fd != -1 && window_pane_visible(wp2)) - input_key(wp2, key); + if (wp2->fd == -1 || wp2->flags & PANE_INPUTOFF) + continue; + if (window_pane_visible(wp2)) + input_key(wp2, key, NULL); } } } -void -window_pane_mouse( - struct window_pane *wp, struct session *sess, struct mouse_event *m) -{ - if (!window_pane_visible(wp)) - return; - - if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx) - return; - if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy) - return; - m->x -= wp->xoff; - m->y -= wp->yoff; - - if (wp->mode != NULL) { - if (wp->mode->mouse != NULL && - options_get_number(&wp->window->options, "mode-mouse")) - wp->mode->mouse(wp, sess, m); - } else if (wp->fd != -1) - input_mouse(wp, sess, m); -} - int window_pane_visible(struct window_pane *wp) { @@ -1113,7 +1201,8 @@ window_pane_visible(struct window_pane *wp) } char * -window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno) +window_pane_search(struct window_pane *wp, const char *searchstr, + u_int *lineno) { struct screen *s = &wp->base; char *newsearchstr, *line, *msg; @@ -1137,150 +1226,254 @@ window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno) return (msg); } -/* Find the pane directly above another. */ +/* Get MRU pane from a list. */ +struct window_pane * +window_pane_choose_best(struct window_pane **list, u_int size) +{ + struct window_pane *next, *best; + u_int i; + + if (size == 0) + return (NULL); + + best = list[0]; + for (i = 1; i < size; i++) { + next = list[i]; + if (next->active_point > best->active_point) + best = next; + } + return (best); +} + +/* + * Find the pane directly above another. We build a list of those adjacent to + * top edge and then choose the best. + */ struct window_pane * window_pane_find_up(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, top; + struct window_pane *next, *best, **list; + u_int edge, left, right, end, size; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - top = wp->yoff; - if (top == 0) - top = wp->window->sy + 1; - left = wp->xoff; + list = NULL; + size = 0; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + edge = wp->yoff; + if (edge == 0) + edge = wp->window->sy + 1; + + left = wp->xoff; + right = wp->xoff + wp->sx; + + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->yoff + wp2->sy + 1 != top) + if (next->yoff + next->sy + 1 != edge) continue; - if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (wp2); + end = next->xoff + next->sx - 1; + + found = 0; + if (next->xoff < left && end > right) + found = 1; + else if (next->xoff >= left && next->xoff <= right) + found = 1; + else if (end >= left && end <= right) + found = 1; + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - return (NULL); + + best = window_pane_choose_best(list, size); + free(list); + return (best); } /* Find the pane directly below another. */ struct window_pane * window_pane_find_down(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, bottom; + struct window_pane *next, *best, **list; + u_int edge, left, right, end, size; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - bottom = wp->yoff + wp->sy + 1; - if (bottom >= wp->window->sy) - bottom = 0; - left = wp->xoff; + list = NULL; + size = 0; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + edge = wp->yoff + wp->sy + 1; + if (edge >= wp->window->sy) + edge = 0; + + left = wp->xoff; + right = wp->xoff + wp->sx; + + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->yoff != bottom) + if (next->yoff != edge) continue; - if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) - return (wp2); + end = next->xoff + next->sx - 1; + + found = 0; + if (next->xoff < left && end > right) + found = 1; + else if (next->xoff >= left && next->xoff <= right) + found = 1; + else if (end >= left && end <= right) + found = 1; + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - return (NULL); + + best = window_pane_choose_best(list, size); + free(list); + return (best); } -/* - * Find the pane directly to the left of another, adjacent to the left side and - * containing the top edge. - */ +/* Find the pane directly to the left of another. */ struct window_pane * window_pane_find_left(struct window_pane *wp) { - struct window_pane *wp2; - u_int left, top; + struct window_pane *next, *best, **list; + u_int edge, top, bottom, end, size; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - left = wp->xoff; - if (left == 0) - left = wp->window->sx + 1; - top = wp->yoff; + list = NULL; + size = 0; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + edge = wp->xoff; + if (edge == 0) + edge = wp->window->sx + 1; + + top = wp->yoff; + bottom = wp->yoff + wp->sy; + + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->xoff + wp2->sx + 1 != left) + if (next->xoff + next->sx + 1 != edge) continue; - if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (wp2); + end = next->yoff + next->sy - 1; + + found = 0; + if (next->yoff < top && end > bottom) + found = 1; + else if (next->yoff >= top && next->yoff <= bottom) + found = 1; + else if (end >= top && end <= bottom) + found = 1; + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - return (NULL); + + best = window_pane_choose_best(list, size); + free(list); + return (best); } -/* - * Find the pane directly to the right of another, that is adjacent to the - * right edge and including the top edge. - */ +/* Find the pane directly to the right of another. */ struct window_pane * window_pane_find_right(struct window_pane *wp) { - struct window_pane *wp2; - u_int right, top; + struct window_pane *next, *best, **list; + u_int edge, top, bottom, end, size; + int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - right = wp->xoff + wp->sx + 1; - if (right >= wp->window->sx) - right = 0; - top = wp->yoff; + list = NULL; + size = 0; - TAILQ_FOREACH(wp2, &wp->window->panes, entry) { - if (!window_pane_visible(wp2)) + edge = wp->xoff + wp->sx + 1; + if (edge >= wp->window->sx) + edge = 0; + + top = wp->yoff; + bottom = wp->yoff + wp->sy; + + TAILQ_FOREACH(next, &wp->window->panes, entry) { + if (next == wp || !window_pane_visible(next)) continue; - if (wp2->xoff != right) + if (next->xoff != edge) continue; - if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) - return (wp2); + end = next->yoff + next->sy - 1; + + found = 0; + if (next->yoff < top && end > bottom) + found = 1; + else if (next->yoff >= top && next->yoff <= bottom) + found = 1; + else if (end >= top && end <= bottom) + found = 1; + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - return (NULL); + + best = window_pane_choose_best(list, size); + free(list); + return (best); } /* Clear alert flags for a winlink */ void winlink_clear_flags(struct winlink *wl) { - struct winlink *wm; struct session *s; - struct window *w; - u_int i; + struct winlink *wl_loop; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; - - RB_FOREACH(s, sessions, &sessions) { - if ((wm = session_has(s, w)) == NULL) + RB_FOREACH(s, sessions, &sessions) { + RB_FOREACH(wl_loop, winlinks, &s->windows) { + if (wl_loop->window != wl->window) + continue; + if ((wl_loop->flags & WINLINK_ALERTFLAGS) == 0) continue; - if (wm->window != wl->window) - continue; - if ((wm->flags & WINLINK_ALERTFLAGS) == 0) - continue; - - wm->flags &= ~WINLINK_ALERTFLAGS; + wl_loop->flags &= ~WINLINK_ALERTFLAGS; + wl_loop->window->flags &= ~WINDOW_ALERTFLAGS; server_status_session(s); } } } -/* Set the grid_cell with fg/bg/attr information when window is in a mode. */ -void -window_mode_attrs(struct grid_cell *gc, struct options *oo) +int +winlink_shuffle_up(struct session *s, struct winlink *wl) { - memcpy(gc, &grid_default_cell, sizeof *gc); - colour_set_fg(gc, options_get_number(oo, "mode-fg")); - colour_set_bg(gc, options_get_number(oo, "mode-bg")); - gc->attr |= options_get_number(oo, "mode-attr"); + int idx, last; + + idx = wl->idx + 1; + + /* Find the next free index. */ + for (last = idx; last < INT_MAX; last++) { + if (winlink_find_by_index(&s->windows, last) == NULL) + break; + } + if (last == INT_MAX) + return (-1); + + /* Move everything from last - 1 to idx up a bit. */ + for (; last > idx; last--) { + wl = winlink_find_by_index(&s->windows, last - 1); + server_link_window(s, wl, s, last, 0, 0, NULL); + server_unlink_window(s, wl); + } + + return (idx); } diff --git a/www/images/tmux3.png b/www/images/tmux3.png deleted file mode 100644 index 689a2833..00000000 Binary files a/www/images/tmux3.png and /dev/null differ diff --git a/www/images/tmux4.png b/www/images/tmux4.png deleted file mode 100644 index 9e3da3f1..00000000 Binary files a/www/images/tmux4.png and /dev/null differ diff --git a/www/images/tmux5.png b/www/images/tmux5.png deleted file mode 100644 index 83cd7e15..00000000 Binary files a/www/images/tmux5.png and /dev/null differ diff --git a/www/index.html.in b/www/index.html.in deleted file mode 100644 index 5e0276e8..00000000 --- a/www/index.html.in +++ /dev/null @@ -1,64 +0,0 @@ - - - - - tmux - - - -
- -
- -

tmux is a terminal multiplexer

- -

What is a terminal multiplexer? It lets you switch easily between -several programs in one terminal, detach them (they keep running in the -background) and reattach them to a different terminal. And do a lot more. See - -the manual.

- -

-Download tmux %%VERSION%% or - -get the development version. -tmux is hosted on -SourceForge -and needs -libevent -and -ncurses -.

- -

For support contact the -tmux-users@lists.sf.net -mailing list or IRC channel -#tmux on freenode.

- -

There are some programs to use with tmux - -on GitHub and a -book on tmux.

- -
- Screenshot - Screenshot - Screenshot -
-
-
- - diff --git a/www/main.css b/www/main.css deleted file mode 100644 index 28b58424..00000000 --- a/www/main.css +++ /dev/null @@ -1,50 +0,0 @@ -body { - font-family: Sans-Serif; - font-size: 10pt; - background-color: white; -} -#body-wrapper { - overflow: auto; -} -#upper-left-title { - font-size:xx-large; - margin-top: 0; -} -#left-menu li { - list-style: none; - margin-top: 1em; -} -.menu-headings { - border-top: 1px solid black; - border-bottom: 1px solid black; - font-weight: bold; - padding: 0.5em; -} -#left-menu-container { - padding-right: 0.5em; - margin-top: 0.5em; - margin-bottom: 0.5em; - margin-right: 0.5em; - border-right: 3px solid black; - text-align: right; - width: 12em; - float: left; -} -#main-content-wrapper { - margin-left: 2em; - margin-right: 4em; -} -#main-content-wrapper li { - list-style: disc; - margin-left: 12em -} -#screenshots { - text-align: center; - margin: 1em; - margin-left: 10em; -} -#screenshots img { - text-align: center; - margin: 0.5em; -} - diff --git a/xmalloc.c b/xmalloc.c index df583e55..afa8e585 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -1,136 +1,137 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* - * Copyright (c) 2004 Nicholas Marriott + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). * - * 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. + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". */ -#include - #include -#include +#include +#include +#include #include #include #include "tmux.h" -char * -xstrdup(const char *s) +void * +xmalloc(size_t size) { - char *ptr; - size_t len; + void *ptr; - len = strlen(s) + 1; - ptr = xmalloc(len); - - strlcpy(ptr, s, len); - return (ptr); + if (size == 0) + fatal("xmalloc: zero size"); + ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: allocating %zu bytes: %s", + size, strerror(errno)); + return ptr; } void * xcalloc(size_t nmemb, size_t size) { - void *ptr; + void *ptr; if (size == 0 || nmemb == 0) - fatalx("zero size"); - if (SIZE_MAX / nmemb < size) - fatalx("nmemb * size > SIZE_MAX"); - if ((ptr = calloc(nmemb, size)) == NULL) - fatal("xcalloc failed"); - - return (ptr); + fatal("xcalloc: zero size"); + ptr = calloc(nmemb, size); + if (ptr == NULL) + fatal("xcalloc: allocating %zu * %zu bytes: %s", + nmemb, size, strerror(errno)); + return ptr; } void * -xmalloc(size_t size) +xrealloc(void *ptr, size_t size) { - void *ptr; - - if (size == 0) - fatalx("zero size"); - if ((ptr = malloc(size)) == NULL) - fatal("xmalloc failed"); - - return (ptr); + return xreallocarray(ptr, 1, size); } void * -xrealloc(void *oldptr, size_t nmemb, size_t size) +xreallocarray(void *ptr, size_t nmemb, size_t size) { - size_t newsize = nmemb * size; - void *newptr; + void *new_ptr; - if (newsize == 0) - fatalx("zero size"); - if (SIZE_MAX / nmemb < size) - fatalx("nmemb * size > SIZE_MAX"); - if ((newptr = realloc(oldptr, newsize)) == NULL) - fatal("xrealloc failed"); - - return (newptr); + if (nmemb == 0 || size == 0) + fatal("xreallocarray: zero size"); + new_ptr = reallocarray(ptr, nmemb, size); + if (new_ptr == NULL) + fatal("xreallocarray: allocating %zu * %zu bytes: %s", + nmemb, size, strerror(errno)); + return new_ptr; } -int printflike2 +char * +xstrdup(const char *str) +{ + char *cp; + + if ((cp = strdup(str)) == NULL) + fatal("xstrdup: %s", strerror(errno)); + return cp; +} + +int xasprintf(char **ret, const char *fmt, ...) { va_list ap; - int i; + int i; va_start(ap, fmt); i = xvasprintf(ret, fmt, ap); va_end(ap); - return (i); + return i; } int xvasprintf(char **ret, const char *fmt, va_list ap) { - int i; + int i; i = vasprintf(ret, fmt, ap); + if (i < 0 || *ret == NULL) - fatal("xvasprintf failed"); + fatal("xasprintf: %s", strerror(errno)); - return (i); -} - -int printflike3 -xsnprintf(char *buf, size_t len, const char *fmt, ...) -{ - va_list ap; - int i; - - va_start(ap, fmt); - i = xvsnprintf(buf, len, fmt, ap); - va_end(ap); - - return (i); + return i; } int -xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) +xsnprintf(char *str, size_t len, const char *fmt, ...) { - int i; + va_list ap; + int i; + + va_start(ap, fmt); + i = xvsnprintf(str, len, fmt, ap); + va_end(ap); + + return i; +} + +int +xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) +{ + int i; if (len > INT_MAX) - fatalx("len > INT_MAX"); + fatal("xsnprintf: len > INT_MAX"); - i = vsnprintf(buf, len, fmt, ap); - if (i < 0) - fatal("vsnprintf failed"); + i = vsnprintf(str, len, fmt, ap); - return (i); + if (i < 0 || i >= (int)len) + fatal("xsnprintf: overflow"); + + return i; } diff --git a/xmalloc.h b/xmalloc.h new file mode 100644 index 00000000..d331ce99 --- /dev/null +++ b/xmalloc.h @@ -0,0 +1,40 @@ +/* $OpenBSD$ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef XMALLOC_H +#define XMALLOC_H + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void *xrealloc(void *, size_t); +void *xreallocarray(void *, size_t, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +int xvasprintf(char **, const char *, va_list) + __attribute__((__nonnull__ (2))); +int xsnprintf(char *, size_t, const char *, ...) + __attribute__((__format__ (printf, 3, 4))) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); +int xvsnprintf(char *, size_t, const char *, va_list) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); + +#endif /* XMALLOC_H */ diff --git a/xterm-keys.c b/xterm-keys.c index 8c885875..f1490fcc 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -40,11 +40,12 @@ * We accept any but always output the latter (it comes first in the table). */ -int xterm_keys_match(const char *, const char *, size_t); -int xterm_keys_modifiers(const char *, const char *, size_t); +int xterm_keys_match(const char *, const char *, size_t, size_t *, + key_code *); +int xterm_keys_modifiers(const char *, size_t, size_t *, key_code *); struct xterm_keys_entry { - int key; + key_code key; const char *template; }; @@ -69,14 +70,6 @@ const struct xterm_keys_entry xterm_keys_table[] = { { KEYC_F10, "\033[21;_~" }, { KEYC_F11, "\033[23;_~" }, { KEYC_F12, "\033[24;_~" }, - { KEYC_F13, "\033[25;_~" }, - { KEYC_F14, "\033[26;_~" }, - { KEYC_F15, "\033[28;_~" }, - { KEYC_F16, "\033[29;_~" }, - { KEYC_F17, "\033[31;_~" }, - { KEYC_F18, "\033[32;_~" }, - { KEYC_F19, "\033[33;_~" }, - { KEYC_F20, "\033[34;_~" }, { KEYC_UP, "\033[1;_A" }, { KEYC_DOWN, "\033[1;_B" }, { KEYC_RIGHT, "\033[1;_C" }, @@ -122,47 +115,65 @@ const struct xterm_keys_entry xterm_keys_table[] = { * 0 for match, 1 if the end of the buffer is reached (need more data). */ int -xterm_keys_match(const char *template, const char *buf, size_t len) +xterm_keys_match(const char *template, const char *buf, size_t len, + size_t *size, key_code *modifiers) { size_t pos; + int retval; + + *modifiers = 0; if (len == 0) return (0); pos = 0; do { - if (*template != '_' && buf[pos] != *template) + if (*template == '_') { + retval = xterm_keys_modifiers(buf, len, &pos, + modifiers); + if (retval != 0) + return (retval); + continue; + } + if (buf[pos] != *template) return (-1); - } while (pos++ != len && *++template != '\0'); + pos++; + } while (*++template != '\0' && pos != len); if (*template != '\0') /* partial */ return (1); + *size = pos; return (0); } -/* Find modifiers based on template. */ +/* Find modifiers from buffer. */ int -xterm_keys_modifiers(const char *template, const char *buf, size_t len) +xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, + key_code *modifiers) { - size_t idx; - int param, modifiers; + u_int flags; - idx = strcspn(template, "_"); - if (idx >= len) - return (0); - param = buf[idx] - '1'; + if (len - *pos < 2) + return (1); - modifiers = 0; - if (param & 1) - modifiers |= KEYC_SHIFT; - if (param & 2) - modifiers |= KEYC_ESCAPE; - if (param & 4) - modifiers |= KEYC_CTRL; - if (param & 8) - modifiers |= KEYC_ESCAPE; - return (modifiers); + if (buf[*pos] < '0' || buf[*pos] > '9') + return (-1); + flags = buf[(*pos)++] - '0'; + if (buf[*pos] >= '0' && buf[*pos] <= '9') + flags = (flags * 10) + (buf[(*pos)++] - '0'); + flags -= 1; + + *modifiers = 0; + if (flags & 1) + *modifiers |= KEYC_SHIFT; + if (flags & 2) + *modifiers |= KEYC_ESCAPE; + if (flags & 4) + *modifiers |= KEYC_CTRL; + if (flags & 8) + *modifiers |= KEYC_ESCAPE; + return (0); } /* @@ -170,33 +181,34 @@ xterm_keys_modifiers(const char *template, const char *buf, size_t len) * key), -1 for not found, 1 for partial match. */ int -xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) +xterm_keys_find(const char *buf, size_t len, size_t *size, key_code *key) { const struct xterm_keys_entry *entry; u_int i; + int matched; + key_code modifiers; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; - switch (xterm_keys_match(entry->template, buf, len)) { - case 0: - *size = strlen(entry->template); - *key = entry->key; - *key |= xterm_keys_modifiers(entry->template, buf, len); - return (0); - case 1: - return (1); - } + + matched = xterm_keys_match(entry->template, buf, len, size, + &modifiers); + if (matched == -1) + continue; + if (matched == 0) + *key = entry->key | modifiers; + return (matched); } return (-1); } /* Lookup a key number from the table. */ char * -xterm_keys_lookup(int key) +xterm_keys_lookup(key_code key) { const struct xterm_keys_entry *entry; u_int i; - int modifiers; + key_code modifiers; char *out; modifiers = 1;