diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..ea4e6439 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: nviennot diff --git a/.github/workflows/DragonflyBSD.yml b/.github/workflows/DragonflyBSD.yml new file mode 100644 index 00000000..f014da9b --- /dev/null +++ b/.github/workflows/DragonflyBSD.yml @@ -0,0 +1,41 @@ +name: DragonflyBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/DragonflyBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/DragonflyBSD.yml' + + + +jobs: + DragonflyBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/dragonflybsd-vm@v0 + with: + prepare: | + pkg install -y automake autoconf libtool pkgconf libevent msgpack libssh gsed + usesh: true + run: | + gsed -i "s/OK/0/g" tty-term.c + autoupdate + ./autogen.sh + ./configure + make + make install + + + diff --git a/.github/workflows/FreeBSD.yml b/.github/workflows/FreeBSD.yml new file mode 100644 index 00000000..e879618e --- /dev/null +++ b/.github/workflows/FreeBSD.yml @@ -0,0 +1,40 @@ +name: FreeBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/FreeBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/FreeBSD.yml' + + + +jobs: + FreeBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/freebsd-vm@v0 + with: + prepare: | + pkg install -y automake autoconf libtool pkgconf libevent msgpack libssh + usesh: true + run: | + autoupdate + ./autogen.sh + ./configure + make + make install + + + diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml new file mode 100644 index 00000000..3dc5247e --- /dev/null +++ b/.github/workflows/MacOS.yml @@ -0,0 +1,37 @@ +name: MacOS +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/MacOS.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/MacOS.yml' + + + +jobs: + MacOS: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - run: | + brew install automake msgpack libssh + autoupdate + ./autogen.sh + ./configure + make + make install + + + + diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml new file mode 100644 index 00000000..0bb0924a --- /dev/null +++ b/.github/workflows/NetBSD.yml @@ -0,0 +1,40 @@ +name: NetBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/NetBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/NetBSD.yml' + + + +jobs: + NetBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/netbsd-vm@v0 + with: + prepare: | + pkg_add automake autoconf libtool pkgconf libevent msgpack libssh + usesh: true + run: | + autoupdate + ./autogen.sh + ./configure + make + make install + + + diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml new file mode 100644 index 00000000..8fb7901b --- /dev/null +++ b/.github/workflows/OpenBSD.yml @@ -0,0 +1,50 @@ +name: OpenBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/OpenBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/OpenBSD.yml' + + + +jobs: + OpenBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/openbsd-vm@v0 + with: + prepare: | + pkg_add automake-1.16.3 autoconf-2.71 libtool pkgconf libevent msgpack libssh curl + usesh: true + run: | + sed -i 's,,,' *.[ch] + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-Makefile_am | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-server_c | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmate-debug_c | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmate_h | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmux_c | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmux_h | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-osdep-openbsd_c | patch + export AUTOMAKE_VERSION=1.16 + export AUTOCONF_VERSION=2.71 + autoupdate + ./autogen.sh + ./configure + make + make install + + + diff --git a/.github/workflows/Ubuntu.yml b/.github/workflows/Ubuntu.yml new file mode 100644 index 00000000..cecf39b9 --- /dev/null +++ b/.github/workflows/Ubuntu.yml @@ -0,0 +1,37 @@ +name: Ubuntu +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/Ubuntu.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/Ubuntu.yml' + + + +jobs: + Ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: | + sudo apt-get install -y build-essential automake libtool libevent-dev libncurses5-dev libmsgpack-dev libssh-dev pkg-config + autoupdate + ACLOCAL_PATH=/usr/share/aclocal ./autogen.sh + ./configure + make + sudo make install + + + + diff --git a/.gitignore b/.gitignore index ba580eda..bcbdfe13 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,13 @@ tmux Makefile Makefile.in configure -tmux.1.* +tmate +cscope.* +ctags +*.log +tmate.1.* +downloads/ +ext/ +libssh-*/ +msgpack-*/ +releases/ diff --git a/.mailmap b/.mailmap index a9b07251..b4956775 100644 --- a/.mailmap +++ b/.mailmap @@ -1,9 +1,9 @@ Bob Beck beck +Claudio Jeker claudio Igor Sobrado sobrado Ingo Schwarze schwarze Jacek Masiulaniec jacekm Jason McIntyre jmc -Jason McIntyre jcm Joel Sing jsing Jonathan Gray jsg Kenneth R Westerback krw @@ -11,6 +11,7 @@ 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 @@ -24,11 +25,12 @@ Sebastian Benoit benno Stefan Sperling stsp Stuart Henderson sthen Ted Unangst tedu -Theo de Raadt deraadt Theo de Raadt Theo Deraadt +Theo de Raadt deraadt Thomas Adam Thomas -Thomas Adam n6tadam 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 index a1d7e427..5a83e0ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,40 @@ language: c +services: +- docker + 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 + include: + - arch: amd64 + env: PLATFORM=amd64 + - arch: amd64 + env: PLATFORM=i386 + - arch: arm64 + env: PLATFORM=arm32v6 + - arch: arm64 + env: PLATFORM=arm32v7 + - arch: arm64 + env: PLATFORM=arm64v8 + - arch: s390x + env: PLATFORM=s390x + - arch: ppc64le + env: PLATFORM=ppc64le + +script: +- 'docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM' +# On arch=arm64, some directories are not setup correctly, and 'ruby -S gem +# install dpl' required by the release push scripts fails. +- 'if [ "$TRAVIS_TAG" ]; then sudo chown -R $USER: /var/lib/gems /usr/local/bin; fi' +- 'if [ "$TRAVIS_TAG" ]; then ./build_static_release.sh $TRAVIS_TAG $PLATFORM; fi' + +deploy: + provider: releases + api_key: + secure: T2109tjjOsrVLEpJZK/uxmO0AuDGXYFdN4AAsNTmVwu/W5dcX57Kk2TCgqDuLfD21iGGXP0U/OYHM06IfBDODBWCA9P8ASHYsenS7wIiFnvCEMbfzoAFyBMrXN2kNdM2+ho3aqc0xE2lQKOKDLxpGm5FZrzujscXXzxQjWBU5Hk= + skip_cleanup: true + overwrite: true + file_glob: true + file: releases/*.tar.* + on: + repo: tmate-io/tmate + branch: master + tags: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..2f6387c5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +ARG PLATFORM=amd64 +FROM ${PLATFORM}/alpine:3.10 AS build + +WORKDIR /build + +RUN apk add --no-cache wget cmake make gcc g++ linux-headers zlib-dev openssl-dev \ + automake autoconf libevent-dev ncurses-dev msgpack-c-dev libexecinfo-dev \ + ncurses-static libexecinfo-static libevent-static msgpack-c ncurses-libs \ + libevent libexecinfo openssl zlib + +RUN set -ex; \ + mkdir -p /src/libssh/build; \ + cd /src; \ + wget -O libssh.tar.xz https://www.libssh.org/files/0.9/libssh-0.9.0.tar.xz; \ + tar -xf libssh.tar.xz -C /src/libssh --strip-components=1; \ + cd /src/libssh/build; \ + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF \ + -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF ..; \ + make -j $(nproc); \ + make install + +COPY compat ./compat +COPY *.c *.h autogen.sh Makefile.am configure.ac ./ + +RUN ./autogen.sh && ./configure --enable-static +RUN make -j $(nproc) +RUN objcopy --only-keep-debug tmate tmate.symbols && chmod -x tmate.symbols && strip tmate +RUN ./tmate -V + +FROM alpine:3.9 + +RUN apk --no-cache add bash +RUN mkdir /build +ENV PATH=/build:$PATH +COPY --from=build /build/tmate.symbols /build +COPY --from=build /build/tmate /build diff --git a/Makefile.am b/Makefile.am index 8b39ccfc..3da954ae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,13 +1,13 @@ # Makefile.am # Obvious program stuff. -bin_PROGRAMS = tmux -CLEANFILES = tmux.1.mdoc tmux.1.man +bin_PROGRAMS = tmate +CLEANFILES = tmate.1.mdoc tmate.1.man # Distribution tarball options. EXTRA_DIST = \ - CHANGES FAQ README TODO COPYING examples compat/*.[ch] \ - array.h compat.h tmux.h osdep-*.c mdoc2man.awk tmux.1 + CHANGES FAQ README TODO COPYING example_tmux.conf compat/*.[ch] \ + array.h compat.h tmux.h osdep-*.c xmalloc.h mdoc2man.awk tmate.1 dist-hook: make clean grep "^#found_debug=" configure @@ -21,6 +21,10 @@ if IS_GLIBC CFLAGS += -D_GNU_SOURCE endif +if IS_LINUX +CFLAGS += -rdynamic # for stack traces +endif + # Set flags for gcc. if IS_GCC CFLAGS += -std=gnu99 -O2 @@ -29,8 +33,8 @@ 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 += -Wundef -Wbad-function-cast -Winline -Wcast-align -CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign +CFLAGS += -Wundef -Wbad-function-cast -Winline +CFLAGS += -Wno-pointer-sign -Wno-attributes CPPFLAGS += -DDEBUG endif if IS_COVERAGE @@ -40,6 +44,9 @@ endif CPPFLAGS += -iquote. endif +CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-null-pointer-arithmetic +CFLAGS += -Wno-deprecated-declarations -Wno-format-nonliteral + # Set flags for Solaris. if IS_SUNOS if IS_GCC @@ -54,13 +61,13 @@ if IS_SUNCC CFLAGS += -erroff=E_EMPTY_DECLARATION endif -# Set _LINUX_SOURCE_COMPAT for AIX for mallocing 0 bytes +# Set _LINUX_SOURCE_COMPAT for AIX for malloc(0). if IS_AIX DEFS += -D_LINUX_SOURCE_COMPAT=1 endif # List of sources. -dist_tmux_SOURCES = \ +dist_tmate_SOURCES = \ alerts.c \ arguments.c \ attributes.c \ @@ -118,6 +125,7 @@ dist_tmux_SOURCES = \ cmd-send-keys.c \ cmd-set-buffer.c \ cmd-set-environment.c \ + cmd-set-hook.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ @@ -136,9 +144,9 @@ dist_tmux_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 \ @@ -154,6 +162,7 @@ dist_tmux_SOURCES = \ options-table.c \ options.c \ paste.c \ + proc.c \ resize.c \ screen-redraw.c \ screen-write.c \ @@ -165,6 +174,14 @@ dist_tmux_SOURCES = \ signal.c \ status.c \ style.c \ + tmate-debug.c \ + tmate-ssh-client.c \ + tmate-encoder.c \ + tmate-decoder.c \ + tmate-env.c \ + tmate-msg.c \ + tmate-msgpack.c \ + tmate-session.c \ tmux.c \ tty-acs.c \ tty-keys.c \ @@ -177,73 +194,76 @@ dist_tmux_SOURCES = \ window.c \ xmalloc.c \ xterm-keys.c -nodist_tmux_SOURCES = osdep-@PLATFORM@.c +nodist_tmate_SOURCES = osdep-@PLATFORM@.c # Pile in all the compat/ stuff that is needed. if NO_FORKPTY -nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c +nodist_tmate_SOURCES += compat/forkpty-@PLATFORM@.c endif if NO_IMSG -nodist_tmux_SOURCES += compat/imsg.c compat/imsg-buffer.c +nodist_tmate_SOURCES += compat/imsg.c compat/imsg-buffer.c endif if NO_CLOSEFROM -nodist_tmux_SOURCES += compat/closefrom.c +nodist_tmate_SOURCES += compat/closefrom.c endif if NO_DAEMON -nodist_tmux_SOURCES += compat/daemon.c +nodist_tmate_SOURCES += compat/daemon.c endif if NO_SETENV -nodist_tmux_SOURCES += compat/setenv.c +nodist_tmate_SOURCES += compat/setenv.c endif if NO_STRLCAT -nodist_tmux_SOURCES += compat/strlcat.c +nodist_tmate_SOURCES += compat/strlcat.c endif if NO_STRLCPY -nodist_tmux_SOURCES += compat/strlcpy.c +nodist_tmate_SOURCES += compat/strlcpy.c endif if NO_ASPRINTF -nodist_tmux_SOURCES += compat/asprintf.c +nodist_tmate_SOURCES += compat/asprintf.c endif if NO_FGETLN -nodist_tmux_SOURCES += compat/fgetln.c +nodist_tmate_SOURCES += compat/fgetln.c endif if NO_FPARSELN -nodist_tmux_SOURCES += compat/fparseln.c +nodist_tmate_SOURCES += compat/fparseln.c endif if NO_GETOPT -nodist_tmux_SOURCES += compat/getopt.c +nodist_tmate_SOURCES += compat/getopt.c endif if NO_STRCASESTR -nodist_tmux_SOURCES += compat/strcasestr.c +nodist_tmate_SOURCES += compat/strcasestr.c endif if NO_STRSEP -nodist_tmux_SOURCES += compat/strsep.c +nodist_tmate_SOURCES += compat/strsep.c endif if NO_VIS -nodist_tmux_SOURCES += compat/vis.c compat/unvis.c +nodist_tmate_SOURCES += compat/vis.c compat/unvis.c endif if NO_STRTONUM -nodist_tmux_SOURCES += compat/strtonum.c +nodist_tmate_SOURCES += compat/strtonum.c endif if NO_B64_NTOP -nodist_tmux_SOURCES += compat/b64_ntop.c +nodist_tmate_SOURCES += compat/b64_ntop.c endif if NO_CFMAKERAW -nodist_tmux_SOURCES += compat/cfmakeraw.c +nodist_tmate_SOURCES += compat/cfmakeraw.c endif if NO_OPENAT -nodist_tmux_SOURCES += compat/openat.c +nodist_tmate_SOURCES += compat/openat.c +endif +if NO_REALLOCARRAY +nodist_tmate_SOURCES += compat/reallocarray.c endif -# Install tmux.1 in the right format. +# Install tmate.1 in the right format. install-exec-hook: if test x@MANFORMAT@ = xmdoc; then \ - sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1 \ - >$(srcdir)/tmux.1.mdoc; \ + sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmate.1 \ + >$(srcdir)/tmate.1.mdoc; \ else \ - sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ - $(AWK) -f$(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ + 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)/tmux.1.@MANFORMAT@ \ - $(DESTDIR)$(mandir)/man1/tmux.1 + $(INSTALL_DATA) $(srcdir)/tmate.1.@MANFORMAT@ \ + $(DESTDIR)$(mandir)/man1/tmate.1 diff --git a/README b/README index acf15632..9141f240 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. tmux depends on libevent 2.x. Download it from: - http://www.monkey.org/~provos/libevent/ + http://libevent.org To build tmux from a release tarball, do: @@ -33,8 +33,16 @@ the source tree with: Some common questions are answered in the FAQ file and a more extensive (but slightly out of date) guide is available in the OpenBSD FAQ at http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO -file and some example configurations and a Vim syntax file are in the examples -directory. +file and an example configuration in example_tmux.conf. + +A vim(1) syntax file is available at: + + https://github.com/keith/tmux.vim + https://raw.githubusercontent.com/keith/tmux.vim/master/syntax/tmux.vim + +And a bash(1) completion file at: + + https://github.com/przepompownia/tmux-bash-completion For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. @@ -52,10 +60,7 @@ welcome. Please send by email to: tmux-users@googlegroups.com -This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under -the ISC license. 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. All other files have a license and copyright notice at their start. -- Nicholas Marriott diff --git a/README-tmux b/README-tmux new file mode 100644 index 00000000..acf15632 --- /dev/null +++ b/README-tmux @@ -0,0 +1,61 @@ +Welcome to tmux! + +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, OS X and Solaris. + +tmux depends on libevent 2.x. Download it from: + + http://www.monkey.org/~provos/libevent/ + +To build tmux from a release tarball, do: + + $ ./configure && make + $ sudo make install + +To get and build the latest from version control: + + $ git clone https://github.com/tmux/tmux.git + $ cd tmux + $ sh autogen.sh + $ ./configure && make + +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: + + $ nroff -mdoc tmux.1|less + +Some common questions are answered in the FAQ file and a more extensive (but +slightly out of date) guide is available in the OpenBSD FAQ at +http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO +file and some example configurations and a Vim syntax file are in the examples +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. For general discussion and bug reports: + + https://groups.google.com/forum/#!forum/tmux-users + +And for Git commit emails: + + https://groups.google.com/forum/#!forum/tmux-git + +Bug reports, feature suggestions and especially code contributions are most +welcome. Please send by email to: + + tmux-users@googlegroups.com + +This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under +the ISC license. 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 diff --git a/README.md b/README.md new file mode 100644 index 00000000..14bc9238 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +tmate +===== + +What is it? +----------- + +Tmate is a fork of tmux. It provides an instant pairing solution. + +License +------- + +tmate is built on top of tmux. tmux and tmate are BSD-licensed. diff --git a/TODO b/TODO index 78d566a4..e0e3de6d 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,8 @@ * ' 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 - make command sequences more usable * don't require space after ; @@ -15,10 +17,9 @@ * way to set socket path from config file - format improvements: - * option to quote format (#{session_name:quoted}) + * option to quote format (#{q:session_name}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces - * last window update time and format for it * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple attached sessions? @@ -51,10 +52,8 @@ * split-window -> split-pane? - better UTF-8 support: - * window names and titles * message display * prompt input - * multibyte key input * searching in copy mode - copy/paste improvements: @@ -63,6 +62,20 @@ * command to toggle selection not to move it in copy-mode * regex searching * copy-pipe should have -x as well + * 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 + * searching in copy mode should unwrap lines, so if you seach for "foobar" + then it should be found even if it is now "foo\nbar" (if the WRAP flag + is set on the line) + * {} to go to next/previous blank line in copy mode - layout stuff * way to tag a layout as a number/name @@ -119,7 +132,7 @@ 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? + * multiline status line? separate command prompt and status line? * customizable command aliases * automatic pane logging * BCE? We are halfway there (output side is done for pane backgrounds), diff --git a/alerts.c b/alerts.c index 806e565b..cca0d815 100644 --- a/alerts.c +++ b/alerts.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2015 Nicholas Marriott + * 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 @@ -29,13 +29,15 @@ int alerts_enabled(struct window *, int); void alerts_callback(int, short, void *); void alerts_reset(struct window *); +void alerts_run_hook(struct session *, struct winlink *, int); +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) +alerts_timer(__unused int fd, __unused short events, void *arg) { struct window *w = arg; @@ -45,7 +47,7 @@ alerts_timer(unused int fd, unused short events, void *arg) } void -alerts_callback(unused int fd, unused short events, unused void *arg) +alerts_callback(__unused int fd, __unused short events, __unused void *arg) { struct window *w; struct session *s; @@ -59,11 +61,7 @@ alerts_callback(unused int fd, unused short events, unused void *arg) continue; flags = w->flags; - 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); + alerts = alerts_check_all(s, wl); log_debug("%s:%d @%u alerts check, alerts %#x, " "flags %#x", s->name, wl->idx, w->id, @@ -74,25 +72,58 @@ alerts_callback(unused int fd, unused short events, unused void *arg) alerts_fired = 0; } +void +alerts_run_hook(struct session *s, struct winlink *wl, int flags) +{ + struct cmd_find_state fs; + + if (cmd_find_from_winlink(&fs, s, wl) != 0) + return; + + if (flags & WINDOW_BELL) + hooks_run(s->hooks, NULL, &fs, "alert-bell"); + if (flags & WINDOW_SILENCE) + hooks_run(s->hooks, NULL, &fs, "alert-silence"); + if (flags & WINDOW_ACTIVITY) + hooks_run(s->hooks, NULL, &fs, "alert-activity"); +} + +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) { + alerts_run_hook(s, wl, alerts); + 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) { - struct session *s; - + if (flags & WINDOW_BELL) + return (1); if (flags & WINDOW_ACTIVITY) { - if (options_get_number(&w->options, "monitor-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); - } - if (~flags & WINDOW_BELL) - return (0); - RB_FOREACH(s, sessions, &sessions) { - if (!session_has(s, w)) - continue; - if (options_get_number(&s->options, "bell-action") != BELL_NONE) + if (options_get_number(w->options, "monitor-silence") != 0) return (1); } return (0); @@ -116,7 +147,7 @@ alerts_reset(struct window *w) event_del(&w->alerts_timer); timerclear(&tv); - tv.tv_sec = options_get_number(&w->options, "monitor-silence"); + 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) @@ -132,15 +163,15 @@ alerts_queue(struct window *w, int flags) if (!event_initialized(&w->alerts_timer)) evtimer_set(&w->alerts_timer, alerts_timer, w); - if (w->flags & flags) - return; - w->flags |= flags; - log_debug("@%u alerts flags added %#x", w->id, flags); + if (!alerts_fired) { + w->flags |= flags; + log_debug("@%u alerts flags added %#x", w->id, flags); - if (!alerts_fired && 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; + 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; + } } } @@ -151,20 +182,20 @@ alerts_check_bell(struct session *s, struct winlink *wl) struct window *w = wl->window; int action, visual; - if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) + if (!(w->flags & WINDOW_BELL)) return (0); - if (s->curw != wl || s->flags & SESSION_UNATTACHED) + if (s->curw != wl) { wl->flags |= WINLINK_BELL; - if (s->flags & SESSION_UNATTACHED) - return (0); + w->flags &= ~WINDOW_BELL; + } if (s->curw->window == w) w->flags &= ~WINDOW_BELL; - action = options_get_number(&s->options, "bell-action"); + action = options_get_number(s->options, "bell-action"); if (action == BELL_NONE) return (0); - visual = options_get_number(&s->options, "visual-bell"); + visual = options_get_number(s->options, "visual-bell"); TAILQ_FOREACH(c, &clients, entry) { if (c->session != s || c->flags & CLIENT_CONTROL) continue; @@ -198,17 +229,17 @@ alerts_check_activity(struct session *s, struct winlink *wl) if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) + if (s->curw == wl) return (0); - if (!options_get_number(&w->options, "monitor-activity")) + if (!options_get_number(w->options, "monitor-activity")) return (0); - if (options_get_number(&s->options, "bell-on-alert")) + 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")) { + if (options_get_number(s->options, "visual-activity")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; @@ -230,17 +261,17 @@ alerts_check_silence(struct session *s, struct winlink *wl) if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) + if (s->curw == wl) return (0); - if (options_get_number(&w->options, "monitor-silence") == 0) + if (options_get_number(w->options, "monitor-silence") == 0) return (0); - if (options_get_number(&s->options, "bell-on-alert")) + 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")) { + if (options_get_number(s->options, "visual-silence")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; diff --git a/arguments.c b/arguments.c index 9f080f52..f7f8f737 100644 --- a/arguments.c +++ b/arguments.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 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 @@ -127,77 +127,73 @@ args_free(struct args *args) 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, used; + va_list ap; + char *s; + size_t slen; + + 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; - const char *quotes; struct args_entry *entry; - /* There must be at least one byte at the start. */ - if (len == 0) - return (0); - off = 0; + len = 1; + buf = xcalloc(1, len); /* Process the flags first. */ - buf[off++] = '-'; RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value != NULL) continue; - if (off == len - 1) { - buf[off] = '\0'; - return (len); - } - buf[off++] = entry->flag; - 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. */ RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value == NULL) continue; - if (off >= len) { - /* snprintf will have zero terminated. */ - return (len); - } - - if (strchr(entry->value, ' ') != NULL) - quotes = "\""; + if (*buf != '\0') + args_print_add(&buf, &len, " -%c ", entry->flag); else - quotes = ""; - used = xsnprintf(buf + off, len - off, "%s-%c %s%s%s", - off != 0 ? " " : "", entry->flag, quotes, entry->value, - quotes); - if (used > len - off) - used = len - off; - off += used; + 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 = ""; - used = xsnprintf(buf + off, len - off, "%s%s%s%s", - off != 0 ? " " : "", quotes, args->argv[i], quotes); - if (used > len - off) - used = len - off; - off += used; + args_print_add(&buf, &len, "%s", args->argv[i]); } - return (off); + return (buf); } /* Return if an argument is present. */ diff --git a/array.h b/array.h index 671bea42..209de0c5 100644 --- a/array.h +++ b/array.h @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2006 Nicholas Marriott + * Copyright (c) 2006 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 diff --git a/build_static_release.sh b/build_static_release.sh new file mode 100755 index 00000000..9cb35435 --- /dev/null +++ b/build_static_release.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -eux + +# This is invoked by .travis.yml + +VERSION=$1 +PLATFORM=$2 + +SRC_VERSION=`cat configure.ac | grep AC_INIT | sed -E 's/^AC_INIT\(tmate, (.+)\)$/\1/'` + +if [ $SRC_VERSION != $VERSION ]; then + echo "Version mismatch: $SRC_VERSION != $VERSION" + exit 1 +fi + +RELEASE_NAME=tmate-$VERSION-static-linux-$PLATFORM +echo "Building $RELEASE_NAME" + +docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM + +mkdir -p releases +cd releases + +rm -rf $RELEASE_NAME +mkdir -p $RELEASE_NAME +docker run --rm local-$PLATFORM/tmate-build cat /build/tmate > $RELEASE_NAME/tmate +chmod +x $RELEASE_NAME/tmate +tar -cf - $RELEASE_NAME | xz > tmate-$VERSION-static-linux-$PLATFORM.tar.xz + +rm -rf $RELEASE_NAME-symbols +mkdir -p $RELEASE_NAME-symbols +docker run --rm local-$PLATFORM/tmate-build cat /build/tmate.symbols > $RELEASE_NAME-symbols/tmate.symbols +tar -cf - $RELEASE_NAME-symbols | xz > dbg-symbols-tmate-$VERSION-static-linux-$PLATFORM.tar.xz diff --git a/cfg.c b/cfg.c index 9657302b..2410fbf9 100644 --- a/cfg.c +++ b/cfg.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -26,8 +26,12 @@ #include #include "tmux.h" +#include "tmate.h" char *cfg_file; +#ifdef TMATE +char *tmate_cfg_file; +#endif struct cmd_q *cfg_cmd_q; int cfg_finished; int cfg_references; @@ -67,7 +71,7 @@ start_cfg(void) cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); if (cfg_file == NULL && (home = find_home()) != NULL) { - xasprintf(&cfg_file, "%s/.tmux.conf", home); + xasprintf(&cfg_file, "%s/.tmate.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { free(cfg_file); cfg_file = NULL; @@ -131,13 +135,38 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) return (found); } +static void print_cfg_errors(void) +{ + u_int i; + + for (i = 0; i < cfg_ncauses; i++) { + tmate_info("%s", cfg_causes[i]); + free(cfg_causes[i]); + } + + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; +} + 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 + /* We do it this late, this way, CLI options take precedence over cfg file */ + tmate_load_cli_options(); + + tmate_session_start(); + if (tmate_foreground && cfg_ncauses) { + print_cfg_errors(); + exit(1); + } +#endif + if (!RB_EMPTY(&sessions)) cfg_show_causes(RB_MIN(sessions, &sessions)); diff --git a/client.c b/client.c index 1e7492d9..c587ec0d 100644 --- a/client.c +++ b/client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -32,11 +32,12 @@ #include #include "tmux.h" +#include "tmate.h" -int client_flags; -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, @@ -47,41 +48,40 @@ enum { CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, } client_exitreason = CLIENT_EXIT_NONE; -int client_exitval; -enum msgtype client_exittype; -const char *client_exitsession; -int client_attached; +int client_exitval; +enum msgtype client_exittype; +const char *client_exitsession; +int client_attached; -__dead void client_exec(const char *); +__dead void client_exec(const char *,const char *); int client_get_lock(char *); -int client_connect(struct event_base *, char *, int); -void client_send_identify(const char *, int); -int client_write_one(enum msgtype, int, const void *, size_t); -int client_write_server(enum msgtype, const 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 ((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) @@ -89,7 +89,7 @@ client_get_lock(char *lockfile) while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); - return (-1); + return (-2); } log_debug("flock succeeded"); @@ -98,7 +98,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ int -client_connect(struct event_base *base, char *path, int start_server) +client_connect(struct event_base *base, const char *path, int start_server) { struct sockaddr_un sa; size_t size; @@ -116,10 +116,10 @@ client_connect(struct event_base *base, char *path, int start_server) retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); + return (-1); log_debug("trying connect"); - if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { + if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; @@ -129,12 +129,16 @@ retry: if (!locked) { xasprintf(&lockfile, "%s.lock", path); - if ((lockfd = client_get_lock(lockfile)) == -1) { - log_debug("didn't get lock"); + if ((lockfd = client_get_lock(lockfile)) < 0) { + log_debug("didn't get lock (%d)", lockfd); + free(lockfile); - goto retry; + lockfile = NULL; + + if (lockfd == -2) + goto retry; } - log_debug("got lock"); + log_debug("got lock (%d)", lockfd); /* * Always retry at least once, even if we got the lock, @@ -146,7 +150,7 @@ retry: goto retry; } - if (unlink(path) != 0 && errno != ENOENT) { + if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { free(lockfile); close(lockfd); return (-1); @@ -154,7 +158,7 @@ retry: fd = server_start(base, lockfd, lockfile); } - if (locked) { + if (locked && lockfd >= 0) { free(lockfile); close(lockfd); } @@ -207,32 +211,108 @@ client_exit_message(void) return ("unknown reason"); } +#ifdef TMATE +extern const struct cmd_entry cmd_attach_session_entry; +extern const struct cmd_entry cmd_new_session_entry; + +/* For foreground mode */ +static int __argc; +static const char **__argv; +#endif + +int run_headless_command(int argc, const char **argv, int flags, void (*err_callback)(const char *)) +{ + struct cmd_q *cmd_q; + struct cmd_list *cmdlist; + char *cause; + cmd_q = cmdq_new(NULL); /* No client */ + + if ((cmdlist = cmd_list_parse(argc, (char **)argv, NULL, 0, &cause)) == NULL) { + if (err_callback) + err_callback(cause); + return -1; + } + + cmdq_run(cmd_q, cmdlist, NULL); + cmd_list_free(cmdlist); + cmdq_free(cmd_q); + + if (flags & DEFER_ERRORS_CFG) + return 0; + + /* error messages land in cfg_causes */ + int ret = cfg_ncauses ? -1 : 0; + for (u_int i = 0; i < cfg_ncauses; i++) { + if (err_callback) + err_callback(cfg_causes[i]); + free(cfg_causes[i]); + } + + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; + + return ret; +} + +static void initial_client_cmd_err_callback(const char *cause) +{ + tmate_info("%s", cause); +} + +void run_initial_client_cmd(void) +{ + int argc = __argc; + const char **argv = __argv; + + const char *default_argv[] = {"new-session"}; + if (argc == 0) { + argc = 1; + argv = default_argv; + } + + if (run_headless_command(argc, argv, 0, initial_client_cmd_err_callback) < 0) + exit(1); +} + /* Client main loop. */ int -client_main(struct event_base *base, 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 *data; - int cmdflags, fd, i, cwd; - const char* ttynam; + 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; +#ifdef TMATE + int cant_nest = 0; + __argc = argc; + __argv = (const char **)argv; +#endif + + /* 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; +#ifdef TMATE + cant_nest = 1; +#endif } else { msg = MSG_COMMAND; @@ -250,12 +330,26 @@ client_main(struct event_base *base, int argc, char **argv, int flags) TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; + +#ifdef TMATE + if (cmd->entry == &cmd_attach_session_entry || + cmd->entry == &cmd_new_session_entry) + cant_nest = 1; +#endif } cmd_list_free(cmdlist); } - /* Establish signal handlers. */ - set_signals(client_signal); +#ifdef TMATE + if (cant_nest && getenv("TMUX")) { + fprintf(stderr, "sessions should be nested with care, " + "unset $TMUX to force\n"); + return (1); + } +#endif + + /* Create client process structure (starts logging). */ + client_proc = proc_start("client", base, 0, client_signal); /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); @@ -264,15 +358,26 @@ client_main(struct event_base *base, int argc, char **argv, int flags) fprintf(stderr, "no server running on %s\n", socket_path); } else { +#ifdef TMATE + if (errno == ENOENT) + fprintf(stderr, "You must specify a socket name with -S. For example: \n" + " tmate -S /tmp/tmate.sock new-session -d\n" + " tmate -S /tmp/tmate.sock wait tmate-ready\n"); + else +#endif 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); /* Save these before pledge(). */ - if ((cwd = open(".", O_RDONLY)) == -1) - cwd = open("/", O_RDONLY); + if ((cwd = getcwd(path, sizeof path)) == NULL) { + if ((cwd = find_home()) == NULL) + cwd = "/"; + } if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; @@ -291,31 +396,18 @@ client_main(struct event_base *base, int argc, char **argv, int flags) #endif /* 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); - - /* Set process title, log and signals now this is the client. */ -#ifdef HAVE_SETPROCTITLE - setproctitle("client (%s)", socket_path); -#endif - logfile("client"); - - /* Create imsg. */ - imsg_init(&client_ibuf, fd); - event_set(&client_event, fd, EV_READ, client_callback, NULL); + 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 (client_flags & CLIENT_CONTROLCONTROL) { - if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { - fprintf(stderr, "tcgetattr failed: %s\n", - strerror(errno)); - return (1); - } + if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) + fatal("tcgetattr failed"); cfmakeraw(&tio); tio.c_iflag = ICRNL|IXANY; tio.c_oflag = OPOST|ONLCR; @@ -331,7 +423,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(ttynam, cwd); /* closes cwd */ + client_send_identify(ttynam, cwd); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -351,18 +443,17 @@ client_main(struct event_base *base, int argc, char **argv, int flags) size += sizeof *data; /* Send the command. */ - if (client_write_server(msg, data, size) != 0) { + 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) { @@ -379,14 +470,15 @@ client_main(struct event_base *base, int argc, char **argv, int flags) 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 messages to server. */ void -client_send_identify(const char *ttynam, int cwd) +client_send_identify(const char *ttynam, const char *cwd) { const char *s; char **ss; @@ -394,149 +486,37 @@ client_send_identify(const char *ttynam, int cwd) int fd, flags = client_flags; pid_t pid; - client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); + proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); if ((s = getenv("TERM")) == NULL) s = ""; - client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); - client_write_one(MSG_IDENTIFY_CWD, cwd, NULL, 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"); - client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); pid = getpid(); - client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); + proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; - if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) - client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) + continue; + proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } - client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); -} - -/* Helper to send one message. */ -int -client_write_one(enum msgtype type, int fd, const void *buf, size_t len) -{ - int retval; - - retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd, - (void *)buf, len); - if (retval != 1) - return (-1); - return (0); -} - -/* Write a message to the server without a file descriptor. */ -int -client_write_server(enum msgtype type, const void *buf, size_t len) -{ - int retval; - - retval = client_write_one(type, -1, buf, len); - if (retval == 0) - client_update_event(); - return (retval); -} - -/* 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, NULL); - event_add(&client_event, NULL); -} - -/* Callback to handle signals in the client. */ -void -client_signal(int sig, unused short events, unused void *arg) -{ - struct sigaction sigact; - int status; - - if (sig == SIGCHLD) - waitpid(WAIT_ANY, &status, WNOHANG); - else if (!client_attached) { - if (sig == SIGTERM) - event_loopexit(NULL); - } 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; - } - } - - client_update_event(); -} - -/* Callback for client imsg read events. */ -void -client_callback(unused int fd, short events, unused void *arg) -{ - 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(); - if (retval != 0) { - event_loopexit(NULL); - return; - } - } - - if (events & EV_WRITE) { - if (msgbuf_write(&client_ibuf.w) <= 0 && errno != EAGAIN) - 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 *arg) +client_stdin_callback(__unused int fd, __unused short events, + __unused void *arg) { struct msg_stdin_data data; @@ -544,10 +524,9 @@ client_stdin_callback(unused int fd, unused short events, unused void *arg) 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. */ @@ -570,12 +549,12 @@ client_write(int fd, const char *data, size_t size) /* Run command in shell; used for -c. */ __dead void -client_exec(const char *shell) +client_exec(const char *shell, const char *shellcmd) { const char *name, *ptr; char *argv0; - log_debug("shell %s, command %s", shell, shell_cmd); + log_debug("shell %s, command %s", shell, shellcmd); ptr = strrchr(shell, '/'); if (ptr != NULL && *(ptr + 1) != '\0') @@ -593,17 +572,73 @@ client_exec(const char *shell) setblocking(STDERR_FILENO, 1); closefrom(STDERR_FILENO + 1); - execl(shell, argv0, "-c", shell_cmd, (char *) NULL); + execl(shell, argv0, "-c", shellcmd, (char *) NULL); fatal("execl failed"); } -/* Dispatch imsgs when in wait state (before MSG_READY). */ -int -client_dispatch_wait(void) +/* Callback to handle signals in the client. */ +void +client_signal(int sig) +{ + struct sigaction sigact; + int status; + + 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; + 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; + } + } +} + +/* 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) { - struct imsg imsg; char *data; - ssize_t n, datalen; + ssize_t datalen; struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; @@ -623,163 +658,141 @@ client_dispatch_wait(void) }; #endif - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - log_debug("got %u from server", imsg.hdr.type); - 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; - } - imsg_free(&imsg); - return (-1); - case MSG_READY: - if (datalen != 0) - fatalx("bad MSG_READY size"); - - 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"); - - 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); - client_exitval = 1; - - imsg_free(&imsg); - return (-1); - case MSG_SHELL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_SHELL string"); - - clear_signals(0); - client_exec(data); - /* NOTREACHED */ - case MSG_DETACH: - case MSG_DETACHKILL: - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXITED: - imsg_free(&imsg); - return (-1); + 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"); - imsg_free(&imsg); + 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 sigaction sigact; char *data; - ssize_t n, datalen; + ssize_t datalen; - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + switch (imsg->hdr.type) { + case MSG_DETACH: + case MSG_DETACHKILL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_DETACH string"); - log_debug("got %u from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_DETACH: - case MSG_DETACHKILL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_DETACH string"); + 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_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; - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXIT: - if (datalen != 0 && datalen != sizeof (int)) - fatalx("bad MSG_EXIT 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"); - 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_exit(client_proc); + break; + case MSG_SHUTDOWN: + if (datalen != 0) + fatalx("bad MSG_SHUTDOWN size"); - imsg_free(&imsg); - return (-1); - case MSG_SHUTDOWN: - if (datalen != 0) - fatalx("bad MSG_SHUTDOWN 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"); - 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"); + 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"); - 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"); - - system(data); - client_write_server(MSG_UNLOCK, NULL, 0); - break; - } - - imsg_free(&imsg); + system(data); + proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); + break; } } diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a7ef1cd9..53c1df31 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -33,52 +33,35 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { - "attach-session", "attach", - "c:dErt:", 0, 0, - "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - CMD_STARTSERVER, - 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, - const char *cflag, int Eflag) +cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, + int Eflag) { - struct session *s; + struct session *s = cmdq->state.tflag.s; struct client *c = cmdq->client, *c_loop; - struct winlink *wl = NULL; - struct window *w = NULL; - struct window_pane *wp = NULL; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; const char *update; - char *cause; - int fd; + char *cause, *cwd; struct format_tree *ft; - char *cp; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); return (CMD_RETURN_ERROR); } - if (tflag == NULL) { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - } else if (tflag[strcspn(tflag, ":.")] != '\0') { - if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - } else { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - w = window_find_by_id_str(tflag); - if (w == NULL) { - wp = window_pane_find_by_id_str(tflag); - if (wp != NULL) - w = wp->window; - } - if (w != NULL) - wl = winlink_find_by_window(&s->windows, w); - } - if (c == NULL) return (CMD_RETURN_NORMAL); if (server_client_check_nested(c)) { @@ -94,45 +77,32 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (cflag != NULL) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, - NULL, NULL); - cp = format_expand(ft, cflag); + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, wl, wp); + cwd = format_expand(ft, cflag); format_free(ft); - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - close(s->cwd); - s->cwd = fd; + 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. - */ TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_write_client(c, MSG_DETACH, - c_loop->session->name, - strlen(c_loop->session->name) + 1); + server_client_detach(c_loop, MSG_DETACH); } } if (!Eflag) { - update = options_get_string(&s->options, + update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + 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); @@ -150,17 +120,21 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->flags |= CLIENT_READONLY; if (dflag) { - server_write_session(s, MSG_DETACH, s->name, - strlen(s->name) + 1); + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c_loop) + continue; + server_client_detach(c_loop, MSG_DETACH); + } } if (!Eflag) { - update = options_get_string(&s->options, + update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + 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); @@ -168,10 +142,13 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; - server_write_ready(c); + 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); @@ -182,7 +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'), args_get(args, 'c'), - args_has(args, 'E'))); + 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 fda39efc..a829a2c5 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -29,14 +29,19 @@ enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_bind_key_mode_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:T:", 1, -1, - "[-cnr] [-t mode-table] [-T key-table] key command [arguments]", - 0, - cmd_bind_key_exec + .name = "bind-key", + .alias = "bind", + + .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 @@ -45,7 +50,7 @@ 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')) { @@ -61,7 +66,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } 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); } @@ -89,7 +94,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_bind_key_mode_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; diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 2aa5c5b7..d220d761 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -31,39 +31,43 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { - "break-pane", "breakp", - "dPF:s:t:", 0, 0, - "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, - 0, - cmd_break_pane_exec + .name = "break-pane", + .alias = "breakp", + + .args = { "dPF:s:t:", 0, 0 }, + .usage = "[-dP] [-F format] [-s src-pane] [-t dst-window]", + + .sflag = CMD_PANE, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, + .exec = cmd_break_pane_exec }; enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE + cmdq_error(cmdq, "break pane is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; - struct winlink *wl; - struct session *src_s; - struct session *dst_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 idx; + int idx = cmdq->state.tflag.idx; struct format_tree *ft; const char *template; char *cp; - wl = cmd_find_pane(cmdq, args_get(args, 's'), &src_s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst_s)) == -2) - return (CMD_RETURN_ERROR); 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"); @@ -82,9 +86,10 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) window_set_name(w, name); free(name); layout_init(w, wp); + wp->flags |= PANE_CHANGED; if (idx == -1) - idx = -1 - options_get_number(&dst_s->options, "base-index"); + idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(dst_s, wl->idx); @@ -100,9 +105,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, - 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); @@ -111,4 +115,5 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 8958a12d..33f6cf08 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -36,12 +36,17 @@ 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] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" - CMD_TARGET_PANE_USAGE, - 0, - 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 * @@ -175,14 +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; 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); @@ -203,7 +205,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) 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 { bufname = NULL; if (args_has(args, 'b')) diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index b4590306..90872e90 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 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 @@ -33,27 +33,31 @@ 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, - 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; - int utf8flag; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } @@ -61,10 +65,6 @@ 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); - utf8flag = options_get_number(&wl->window->options, "utf8"); - if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); @@ -83,7 +83,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_defaults_paste_buffer(cdata->ft, pb, utf8flag); + format_defaults_paste_buffer(cdata->ft, pb); xasprintf(&action_data, "%s", paste_buffer_name(pb)); cdata->command = cmd_template_replace(action, action_data, 1); diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 93ac28a8..b9a24be6 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -31,18 +31,23 @@ "#{client_tty}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \ - "(last used #{client_activity_string})" + "(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, - 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 { @@ -53,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 idx, cur; - if ((c = cmd_find_client(cmdq, NULL, 1)) == 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); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 0b0fea6a..db9222ba 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -44,37 +44,52 @@ 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, - 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, - 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, - 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; @@ -87,14 +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_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 63e9d548..62683ff6 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -27,23 +27,25 @@ 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, - 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; if (wp->mode == &window_copy_mode) window_pane_reset_mode(wp); diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 1622e0b7..3ec22865 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -35,11 +35,17 @@ 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_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 { @@ -58,13 +64,10 @@ 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); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 248515cd..b5a12821 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -34,11 +34,16 @@ 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_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 { @@ -51,13 +56,10 @@ 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 { diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index e04b561b..beb4d7c8 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -27,19 +27,29 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { - "copy-mode", NULL, - "Met:u", 0, 0, - "[-Meu] " CMD_TARGET_PANE_USAGE, - 0, - 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 }; const struct cmd_entry cmd_clock_mode_entry = { - "clock-mode", NULL, - "t:", 0, 0, - CMD_TARGET_PANE_USAGE, - 0, - cmd_copy_mode_exec + .name = "clock-mode", + .alias = NULL, + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_PANE_USAGE, + + .tflag = CMD_PANE, + + .flags = 0, + .exec = cmd_copy_mode_exec }; enum cmd_retval @@ -48,15 +58,14 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct client *c = cmdq->client; struct session *s; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; 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); - } else if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); + } if (self->entry == &cmd_clock_mode_entry) { window_pane_set_mode(wp, &window_clock_mode); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 4bae9997..80c6555f 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -29,35 +29,44 @@ 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, - 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 = { - "suspend-client", "suspendc", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - 0, - cmd_detach_client_exec + .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, *cloop; + struct client *c = cmdq->state.c, *cloop; struct session *s; enum msgtype msgtype; if (self->entry == &cmd_suspend_client_entry) { - 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); + proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); return (CMD_RETURN_NORMAL); } @@ -67,36 +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); - + s = cmdq->state.sflag.s; TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session != s) - continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + if (cloop->session == s) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_STOP); } - c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session == NULL || cloop == c) - continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + if (cloop->session != NULL && cloop != c) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_NORMAL); } - server_write_client(c, msgtype, c->session->name, - strlen(c->session->name) + 1); + server_client_detach(c, msgtype); return (CMD_RETURN_STOP); } diff --git a/cmd-display-message.c b/cmd-display-message.c index ee9eafe9..a041b5a1 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -35,70 +35,47 @@ 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, - 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_find_client(cmdq, NULL, 1); - 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(); + 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 9ce89712..eed3611e 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -27,23 +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, - 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 e92ae60f..6324f26a 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -48,11 +48,16 @@ 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, - 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 { @@ -137,10 +142,10 @@ 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 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; @@ -148,14 +153,10 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; u_int i, match_flags; - if ((c = cmd_find_client(cmdq, NULL, 1)) == 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; diff --git a/cmd-find.c b/cmd-find.c index 0b1bf2aa..f95c143b 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2015 Nicholas Marriott + * 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 @@ -22,34 +22,11 @@ #include #include #include +#include #include "tmux.h" -#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 - -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; -}; - +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 *, @@ -72,12 +49,6 @@ 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 *); -void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); -void cmd_find_log_state(const char *, const char *, struct cmd_find_state *); - -struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, - enum cmd_find_type, int); - const char *cmd_find_session_table[][2] = { { NULL, NULL } }; @@ -108,6 +79,33 @@ const char *cmd_find_pane_table[][2] = { { 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) @@ -191,6 +189,12 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) 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)) @@ -244,8 +248,12 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) { struct window_pane *wp; - /* If this is running in a pane, that's great. */ - if (fs->cmdq->client->tty.path != NULL) { + /* + * 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; @@ -254,24 +262,44 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) wp = NULL; /* Not running in a pane. We know nothing. Find the best session. */ - if (wp == 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); + 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); } - /* We now know the window and pane. */ - fs->w = wp->window; - fs->wp = wp; + /* 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; - /* Find the best session and winlink. */ - if (cmd_find_best_session_with_window(fs) != 0) + 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); } @@ -283,7 +311,9 @@ int cmd_find_current_session(struct cmd_find_state *fs) { /* If we know the current client, use it. */ - if (fs->cmdq->client != NULL) { + 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; @@ -316,8 +346,11 @@ cmd_find_current_client(struct cmd_q *cmdq) u_int csize; /* If the queue client has a session, use it. */ - if (cmdq->client != NULL && cmdq->client->session != NULL) + 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); @@ -326,6 +359,7 @@ cmd_find_current_client(struct cmd_q *cmdq) /* 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) { @@ -366,6 +400,7 @@ int cmd_find_get_session(struct cmd_find_state *fs, const char *session) { struct session *s, *s_loop; + struct client *c; log_debug("%s: %s", __func__, session); @@ -382,6 +417,13 @@ cmd_find_get_session(struct cmd_find_state *fs, const char *session) if (fs->s != NULL) return (0); + /* Look for as a client. */ + c = cmd_find_client(NULL, session, 1); + if (c != NULL && c->session != NULL) { + fs->s = c->session; + return (0); + } + /* Stop now if exact only. */ if (fs->flags & CMD_FIND_EXACT_SESSION) return (-1); @@ -440,11 +482,10 @@ cmd_find_get_window(struct cmd_find_state *fs, const char *window) /* Otherwise try as a session itself. */ if (cmd_find_get_session(fs, window) == 0) { - if (~fs->flags & CMD_FIND_WINDOW_INDEX) { - fs->wl = fs->s->curw; - fs->w = fs->wl->window; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + if (~fs->flags & CMD_FIND_WINDOW_INDEX) fs->idx = fs->wl->idx; - } return (0); } @@ -466,6 +507,13 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) 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); @@ -762,34 +810,168 @@ cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) fs->idx = -1; } -/* Split target into pieces and resolve for the given type. */ -struct cmd_find_state * -cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, - int flags) +/* Check if a state if valid. */ +int +cmd_find_valid_state(struct cmd_find_state *fs) { - static struct cmd_find_state fs, current; - struct mouse_event *m; - char *colon, *period, *copy = NULL; - const char *session, *window, *pane; + struct winlink *wl; - /* Find current state. */ - cmd_find_clear_state(¤t, cmdq, flags); - if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { - current.s = marked_session; - current.wl = marked_winlink; - current.idx = current.wl->idx; - current.w = current.wl->window; - current.wp = marked_window_pane; + 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 (current.s == NULL && cmd_find_current_session(¤t) != 0) { + 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 winlink. */ +int +cmd_find_from_winlink(struct cmd_find_state *fs, struct session *s, + struct winlink *wl) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->s = s; + fs->wl = wl; + fs->w = wl->window; + fs->wp = wl->window->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); +} + +/* Find current state. */ +int +cmd_find_current(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) +{ + cmd_find_clear_state(fs, cmdq, flags); + if (cmd_find_current_session(fs) != 0) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no current session"); - goto error; + return (-1); } + 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_find_state *current, + struct cmd_q *cmdq, const char *target, enum cmd_find_type type, int flags) +{ + 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); - fs.current = ¤t; + 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 + fs->current = current; /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') @@ -800,25 +982,25 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, 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; + 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; + 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 (fs->wp == NULL) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no mouse target"); goto error; } - return (&fs); + goto found; } /* Marked target is a plain ~ or {marked}. */ @@ -828,12 +1010,8 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, cmdq_error(cmdq, "no marked target"); goto error; } - fs.s = marked_session; - fs.wl = marked_winlink; - fs.idx = fs.wl->idx; - fs.w = fs.wl->window; - fs.wp = marked_window_pane; - return (&fs); + cmd_find_copy_state(fs, &marked_pane); + goto found; } /* Find separators if they exist. */ @@ -885,11 +1063,11 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* Set exact match flags. */ if (session != NULL && *session == '=') { session++; - fs.flags |= CMD_FIND_EXACT_SESSION; + fs->flags |= CMD_FIND_EXACT_SESSION; } if (window != NULL && *window == '=') { window++; - fs.flags |= CMD_FIND_EXACT_WINDOW; + fs->flags |= CMD_FIND_EXACT_WINDOW; } /* Empty is the same as NULL. */ @@ -922,32 +1100,31 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* 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) + 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; + 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) + if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; - if (~flags & CMD_FIND_WINDOW_INDEX) - fs.wp = fs.wl->window->active; + 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) + if (cmd_find_get_pane_with_session(fs, pane) != 0) goto no_pane; goto found; } @@ -956,10 +1133,10 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, * 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) + 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) + if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } @@ -967,10 +1144,10 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* 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) + 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) + if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } @@ -978,35 +1155,40 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* 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) + if (cmd_find_get_window(fs, window) != 0) goto no_window; - if (~flags & CMD_FIND_WINDOW_INDEX) - fs.wp = fs.wl->window->active; + 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) + if (cmd_find_get_pane(fs, pane) != 0) goto no_pane; goto found; } current: - /* None is the current session. */ - free(copy); + /* Use the current session. */ + cmd_find_copy_state(fs, fs->current); if (flags & CMD_FIND_WINDOW_INDEX) - current.idx = -1; - return (¤t); + fs->idx = -1; + goto found; error: + fs->current = NULL; + log_debug(" error"); + free(copy); - return (NULL); + return (-1); found: + fs->current = NULL; + cmd_find_log_state(__func__, fs); + free(copy); - return (&fs); + return (0); no_session: if (~flags & CMD_FIND_QUIET) @@ -1024,139 +1206,6 @@ no_pane: goto error; } -/* Log the result. */ -void -cmd_find_log_state(const char *f, const char *target, struct cmd_find_state *fs) -{ - log_debug("%s: target %s%s", f, target == NULL ? "none" : target, - fs != NULL ? "" : " (failed)"); - if (fs == NULL) - return; - if (fs->s != NULL) - log_debug("\ts=$%u", fs->s->id); - else - log_debug("\ts=none"); - if (fs->wl != NULL) { - log_debug("\twl=%u %d w=@%u %s", fs->wl->idx, - fs->wl->window == fs->w, fs->w->id, fs->w->name); - } else - log_debug("\twl=none"); - if (fs->wp != NULL) - log_debug("\twp=%%%u", fs->wp->id); - else - log_debug("\twp=none"); - if (fs->idx != -1) - log_debug("\tidx=%d", fs->idx); - else - log_debug("\tidx=none"); -} - -/* Find the current session. */ -struct session * -cmd_find_current(struct cmd_q *cmdq) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_QUIET; - - fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, flags); - cmd_find_log_state(__func__, NULL, fs); - if (fs == NULL) - return (NULL); - - return (fs->s); -} - -/* Find the target session or report an error and return NULL. */ -struct session * -cmd_find_session(struct cmd_q *cmdq, const char *target, int prefer_unattached) -{ - struct cmd_find_state *fs; - int flags = 0; - - if (prefer_unattached) - flags |= CMD_FIND_PREFER_UNATTACHED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_SESSION, flags); - cmd_find_log_state(__func__, target, fs); - if (fs == NULL) - return (NULL); - - return (fs->s); -} - -/* Find the target window or report an error and return NULL. */ -struct winlink * -cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) -{ - struct cmd_find_state *fs; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, 0); - cmd_find_log_state(__func__, target, fs); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - return (fs->wl); -} - -/* Find the target window, defaulting to marked rather than current. */ -struct winlink * -cmd_find_window_marked(struct cmd_q *cmdq, const char *target, - struct session **sp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_DEFAULT_MARKED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - cmd_find_log_state(__func__, target, fs); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - return (fs->wl); -} - -/* Find the target pane and report an error and return NULL. */ -struct winlink * -cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, - struct window_pane **wpp) -{ - struct cmd_find_state *fs; - - fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, 0); - cmd_find_log_state(__func__, target, fs); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - if (wpp != NULL) - *wpp = fs->wp; - return (fs->wl); -} - -/* Find the target pane, defaulting to marked rather than current. */ -struct winlink * -cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, - struct session **sp, struct window_pane **wpp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_DEFAULT_MARKED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); - cmd_find_log_state(__func__, target, fs); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - if (wpp != NULL) - *wpp = fs->wp; - return (fs->wl); -} - /* 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) @@ -1167,10 +1216,11 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) const char *path; /* A NULL argument means the current client. */ - if (target == NULL) { + if (cmdq != NULL && 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); @@ -1202,27 +1252,6 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) cmdq_error(cmdq, "can't find client %s", copy); free(copy); + log_debug("%s: target %s, return %p", __func__, target, c); return (c); } - -/* - * 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 *target, struct session **sp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_WINDOW_INDEX; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - cmd_find_log_state(__func__, target, fs); - if (fs == NULL) - return (-2); - - if (sp != NULL) - *sp = fs->s; - return (fs->idx); -} diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 0271fdea..3e2a5251 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -36,11 +36,17 @@ 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", - "bFt:", 2, 3, - "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command [command]", - 0, - 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 { @@ -61,32 +67,20 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_if_shell_data *cdata; char *shellcmd, *cmd, *cause; struct cmd_list *cmdlist; - 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; - int cwd; + const char *cwd; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - cwd = wp->cwd; - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - if (cmdq->client != NULL && cmdq->client->session == NULL) - cwd = cmdq->client->cwd; - else if (s != NULL) - cwd = s->cwd; - else - cwd = -1; - } + 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(); + ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); @@ -97,6 +91,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) 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) { diff --git a/cmd-join-pane.c b/cmd-join-pane.c index c384631c..ea2ffbe3 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2011 George Nachman - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -33,19 +33,31 @@ 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] " CMD_SRCDST_PANE_USAGE, - 0, - 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] " CMD_SRCDST_PANE_USAGE, - 0, - cmd_join_pane_exec + .name = "move-pane", + .alias = "movep", + + .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 @@ -57,6 +69,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmd_q *cmdq) enum cmd_retval join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) { +#ifdef TMATE + cmdq_error(cmdq, "join pane is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct session *dst_s; struct winlink *src_wl, *dst_wl; @@ -67,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_marked(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); @@ -147,4 +162,5 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) notify_window_layout_changed(dst_w); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index f4735fd2..ebb2b8d6 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -29,22 +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, - 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 07d94302..d1940c33 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -30,23 +30,29 @@ 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, - 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 = { - "start-server", "start", - "", 0, 0, - "", - CMD_STARTSERVER, - cmd_kill_server_exec + .name = "start-server", + .alias = "start", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = CMD_STARTSERVER, + .exec = cmd_kill_server_exec }; enum cmd_retval -cmd_kill_server_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_kill_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { if (self->entry == &cmd_kill_server_entry) kill(getpid(), SIGTERM); diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 74843eb6..c77e45bb 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -30,11 +30,16 @@ 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, - 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 @@ -42,11 +47,17 @@ cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; 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')) { + 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); diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 4d346a71..9d388ce5 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -27,32 +27,38 @@ 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, - 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 = { - "unlink-window", "unlinkw", - "kt:", 0, 0, - "[-k] " CMD_TARGET_WINDOW_USAGE, - 0, - cmd_kill_window_exec + .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 window *w; - struct session *s; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; + 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 (self->entry == &cmd_unlink_window_entry) { if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 37571b80..238b2776 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -33,15 +33,18 @@ 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, - 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; @@ -54,8 +57,8 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { - ft = format_create(); - format_defaults_paste_buffer(ft, pb, 0); + 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 372b5283..f318ac18 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -36,11 +36,16 @@ 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, - 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 @@ -54,11 +59,9 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) 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) @@ -69,7 +72,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session == NULL || (s != NULL && s != c->session)) continue; - ft = format_create(); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index e2d5dc52..37f53787 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -18,6 +18,7 @@ #include +#include #include #include "tmux.h" @@ -29,22 +30,28 @@ 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 *, 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:T:", 0, 0, - "[-t mode-table] [-T key-table]", - 0, - 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 = { - "list-commands", "lscm", - "", 0, 0, - "", - 0, - cmd_list_keys_exec + .name = "list-commands", + .alias = "lscm", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = CMD_STARTSERVER, + .exec = cmd_list_keys_exec }; enum cmd_retval @@ -54,12 +61,21 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct key_table *table; struct key_binding *bd; const char *key, *tablename, *r; - char tmp[BUFSIZ]; - size_t used; + char *cp, tmp[BUFSIZ]; int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) - return (cmd_list_keys_commands(self, cmdq)); + return (cmd_list_keys_commands(cmdq)); + +#ifdef TMATE + /* XXX TODO Really nasty hack, we really need our own client instance... */ + struct client fake_client; + if (!cmdq->client) { + cmdq->client = &fake_client; + cmdq->client->flags = 0; + cmdq->client->session = RB_MIN(sessions, &sessions); + } +#endif if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); @@ -77,16 +93,14 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); - if (key == NULL) - continue; if (bd->can_repeat) repeat = 1; - width = strlen(table->name); + width = utf8_cstrwidth(table->name); if (width > tablewidth) - tablewidth =width; - width = strlen(key); + tablewidth = width; + width = utf8_cstrwidth(key); if (width > keywidth) keywidth = width; } @@ -97,8 +111,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); - if (key == NULL) - continue; if (!repeat) r = ""; @@ -106,12 +118,21 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) r = "-r "; else r = " "; - used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ", r, - (int)tablewidth, table->name, (int)keywidth, key); - if (used < sizeof tmp) { - cmd_list_print(bd->cmdlist, tmp + used, - (sizeof tmp) - used); - } + 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); } @@ -140,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; @@ -153,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) @@ -174,7 +191,7 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_list_keys_commands(unused struct cmd *self, struct cmd_q *cmdq) +cmd_list_keys_commands(struct cmd_q *cmdq) { const struct cmd_entry **entryp; const struct cmd_entry *entry; diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 0af391c5..76e06a71 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -35,33 +35,31 @@ 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, - 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); } @@ -125,7 +123,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 8ad55d03..27e80dbc 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -30,7 +30,7 @@ #define LIST_SESSIONS_TEMPLATE \ "#{session_name}: #{session_windows} windows " \ - "(created #{session_created_string}) " \ + "(created #{t:session_created}) " \ "[#{session_width}x#{session_height}]" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ @@ -39,11 +39,14 @@ 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, - 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 @@ -61,7 +64,7 @@ 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_defaults(ft, NULL, s, NULL, NULL); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 3f6b2a4c..11a5fddf 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -46,27 +46,27 @@ 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, - 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); } @@ -81,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; @@ -105,7 +105,7 @@ 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_defaults(ft, NULL, s, wl, NULL); diff --git a/cmd-list.c b/cmd-list.c index 0c75ed49..e999c370 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -99,25 +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, used; + 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; - if (TAILQ_NEXT(cmd, qentry) != NULL) { - used = xsnprintf(buf + off, len - off, " ; "); - if (used > len - off) - used = len - off; - off += used; - } + this = cmd_print(cmd); + + len += strlen(this) + 3; + buf = xrealloc(buf, len); + + strlcat(buf, this, len); + if (TAILQ_NEXT(cmd, qentry) != NULL) + strlcat(buf, " ; ", len); + + free(this); } - return (off); + + return (buf); } diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 897807d0..de76b855 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -35,11 +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, - 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 @@ -49,10 +52,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path, *bufname; - char *pdata, *new_pdata, *cause; + const char *path, *bufname, *cwd; + char *pdata, *new_pdata, *cause, *file, resolved[PATH_MAX]; size_t psize; - int ch, error, cwd, fd; + int ch, error; bufname = NULL; if (args_has(args, 'b')) @@ -70,18 +73,26 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_WAIT); } - if (c != NULL && c->session == NULL) + if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; - else if ((s = cmd_find_current(cmdq)) != NULL) + else if ((s = c->session) != NULL && s->cwd != NULL) cwd = s->cwd; else - cwd = AT_FDCWD; + cwd = "."; - if ((fd = openat(cwd, path, O_RDONLY)) == -1 || - (f = fdopen(fd, "rb")) == NULL) { - if (fd != -1) - close(fd); - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + 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); + } + f = fopen(resolved, "rb"); + free(file); + if (f == NULL) { + cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); return (CMD_RETURN_ERROR); } @@ -97,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) @@ -125,7 +136,7 @@ void cmd_load_buffer_callback(struct client *c, int closed, void *data) { const char *bufname = data; - char *pdata, *cause; + char *pdata, *cause, *saved; size_t psize; if (!closed) @@ -146,8 +157,13 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) if (paste_set(pdata, psize, bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ + if (~c->flags & CLIENT_UTF8) { + saved = cause; + cause = utf8_sanitize(saved); + free(saved); + } evbuffer_add_printf(c->stderr_data, "%s", cause); - server_push_stderr(c); + server_client_push_stderr(c); free(pdata); free(cause); } diff --git a/cmd-lock-server.c b/cmd-lock-server.c index de76475d..01597169 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -27,49 +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, - 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, - 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, - 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 b15df4f6..fe023423 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -29,55 +29,63 @@ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { - "move-window", "movew", - "adkrs:t:", 0, 0, - "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - 0, - 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 = { - "link-window", "linkw", - "adks:t:", 0, 0, - "[-dk] " CMD_SRCDST_WINDOW_USAGE, - 0, - cmd_move_window_exec + .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 cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE + cmdq_error(cmdq, "move window is not supported with tmate"); + 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, sflag; + 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')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == 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'); sflag = args_has(self->args, 's'); if (args_has(self->args, 'a')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - if ((idx = winlink_shuffle_up(s, s->curw)) == -1) + if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) return (CMD_RETURN_ERROR); } @@ -95,10 +103,11 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) * session already has the correct winlink id to us, either * automatically or specified by -s. */ - if (!sflag && options_get_number(&src->options, "renumber-windows")) + if (!sflag && options_get_number(src->options, "renumber-windows")) session_renumber_windows(src); recalculate_sizes(); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-new-session.c b/cmd-new-session.c index 7687398e..fb2f7eab 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -36,44 +36,56 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { - "new-session", "new", - "Ac:dDEF:n:Ps:t:x:y:", 0, -1, - "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " - "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " - "[-y height] [command]", - CMD_STARTSERVER, - 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 }; const struct cmd_entry cmd_has_session_entry = { - "has-session", "has", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - 0, - cmd_new_session_exec + .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, *c0; - struct session *s, *groupwith; + struct client *c = cmdq->client; + struct session *s, *as; + struct session *groupwith = cmdq->state.tflag.s; struct window *w; - struct environ env; + struct environ *env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; - const char *path; + const char *path, *cwd, *to_free = NULL; char **argv, *cmd, *cause, *cp; - int detached, already_attached, idx, cwd, fd = -1; - int argc; + int detached, already_attached, idx, argc; u_int sx, sy; struct format_tree *ft; struct environ_entry *envent; if (self->entry == &cmd_has_session_entry) { - if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL) - return (CMD_RETURN_ERROR); + /* + * cmd_prepare() will fail if the session cannot be found, + * hence always return success here. + */ return (CMD_RETURN_NORMAL); } @@ -88,9 +100,16 @@ 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 ((as = session_find(newname)) != NULL) { if (args_has(args, 'A')) { - return (cmd_attach_session(cmdq, newname, + /* + * 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. + */ + cmd_find_from_session(&cmdq->state.tflag, as); + return (cmd_attach_session(cmdq, args_has(args, 'D'), 0, NULL, args_has(args, 'E'))); } @@ -99,11 +118,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } } - target = args_get(args, 't'); - if (target != NULL) { - groupwith = cmd_find_session(cmdq, target, 0); - if (groupwith == NULL) - return (CMD_RETURN_ERROR); + if ((target = args_get(args, 't')) != NULL) { + if (groupwith == NULL) { + cmdq_error(cmdq, "no such session: %s", target); + goto error; + } } else groupwith = NULL; @@ -112,6 +131,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (c == NULL) detached = 1; + if (tmate_foreground) + detached = 1; + /* Is this client already attached? */ already_attached = 0; if (c != NULL && c->session != NULL) @@ -119,31 +141,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Get the new session working directory. */ if (args_has(args, 'c')) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, - NULL); - cp = format_expand(ft, args_get(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); - - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else if (cp != NULL) - free(cp); - cwd = fd; - } else if (c != NULL && c->session == NULL) + } else if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; - else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) - cwd = c0->session->cwd; - else { - fd = open(".", O_RDONLY); - cwd = fd; - } + else + cwd = "."; /* * If this is a new client, check for nesting and save the termios @@ -197,7 +202,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) 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; @@ -207,11 +212,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Figure out the command for the new window. */ argc = -1; argv = NULL; - if (target == NULL && args->argc != 0) { + 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"); + } else if (groupwith == NULL) { + cmd = options_get_string(global_s_options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = &cmd; @@ -223,36 +228,36 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (c != NULL && c->session == NULL) - envent = environ_find(&c->environ, "PATH"); + envent = environ_find(c->environ, "PATH"); else - envent = environ_find(&global_environ, "PATH"); + envent = environ_find(global_environ, "PATH"); if (envent != NULL) path = envent->value; /* Construct the environment. */ - environ_init(&env); + env = environ_create(); if (c != NULL && !args_has(args, 'E')) { - update = options_get_string(&global_s_options, + update = options_get_string(global_s_options, "update-environment"); - environ_update(update, &c->environ, &env); + environ_update(update, c->environ, env); } /* Create the new session. */ - idx = -1 - options_get_number(&global_s_options, "base-index"); - s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, + 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); goto error; } - environ_free(&env); /* Set the initial window name if one given. */ 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); } /* @@ -270,11 +275,13 @@ 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, NULL); @@ -296,9 +303,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(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, NULL, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); @@ -310,12 +316,12 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (!detached) cmdq->client_exit = 0; - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - if (fd != -1) - close(fd); + 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 9cead449..2a647b9f 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -35,43 +35,43 @@ 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, - 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; - const char *cmd, *path, *template; + 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, idx, detached, cwd, fd = -1; + 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); if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } - } else { - idx = cmd_find_index(cmdq, args_get(args, 't'), &s); - if (idx == -2) - return (CMD_RETURN_ERROR); } detached = args_has(args, 'd'); if (args->argc == 0) { - cmd = options_get_string(&s->options, "default-command"); + cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; @@ -86,30 +86,18 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; + to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); - cp = format_expand(ft, args_get(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); - - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else if (cp != NULL) - free(cp); - cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else @@ -136,7 +124,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } if (idx == -1) - idx = -1 - options_get_number(&s->options, "base-index"); + 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) { @@ -154,9 +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(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, - NULL); + ft = format_create(cmdq, 0); + format_defaults(ft, c, s, wl, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); @@ -165,12 +152,12 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - if (fd != -1) - close(fd); + 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 1c83ee7b..e0c29d28 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -33,27 +33,29 @@ 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] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, - 0, - 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, *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'); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 49b156c6..0d13b356 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -37,29 +37,30 @@ 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, - 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 session *s; - struct winlink *wl; - struct window_pane *wp; + 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; struct format_tree *ft; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - c = cmd_find_client(cmdq, NULL, 1); - /* Destroy the old pipe. */ old_fd = wp->pipe_fd; if (wp->pipe_fd != -1) { @@ -88,7 +89,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } /* Expand the command. */ - ft = format_create(); + 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); @@ -141,8 +142,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } 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 1fe34dce..f08a724e 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2013 Nicholas Marriott + * 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 @@ -24,8 +24,9 @@ #include #include "tmux.h" +#include "tmate.h" -enum cmd_retval cmdq_continue_one(struct cmd_q *); +static enum cmd_retval cmdq_continue_one(struct cmd_q *); /* Create new command queue. */ struct cmd_q * @@ -44,6 +45,9 @@ cmdq_new(struct client *c) cmdq->item = NULL; cmdq->cmd = NULL; + cmd_find_clear_state(&cmdq->current, NULL, 0); + cmdq->parent = NULL; + return (cmdq); } @@ -69,22 +73,32 @@ 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)) { - evbuffer_add_vprintf(c->stdout_data, fmt, 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); } @@ -101,18 +115,31 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) va_list ap; char *msg; size_t msglen; + char *tmp; va_start(ap, fmt); msglen = xvasprintf(&msg, fmt, ap); va_end(ap); if (c == NULL) +#ifdef TMATE + if (cmd->file && cmd->line) + cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); + else + cfg_add_cause("%s", msg); +#else cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); +#endif 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); + server_client_push_stderr(c); c->retval = 1; } else { *msg = toupper((u_char) *msg); @@ -133,7 +160,7 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long) cmdq->time, cmdq->number, flags); - server_push_stdout(c); + server_client_push_stdout(c); } /* Add command list to queue and begin processing if needed. */ @@ -166,36 +193,47 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) } /* Process one command. */ -enum cmd_retval +static enum cmd_retval cmdq_continue_one(struct cmd_q *cmdq) { struct cmd *cmd = cmdq->cmd; enum cmd_retval retval; - char tmp[1024]; + char *tmp; int flags = !!(cmd->flags & CMD_CONTROL); - cmd_print(cmd, tmp, sizeof tmp); +#ifdef TMATE + if (tmate_should_replicate_cmd(cmd->entry)) + tmate_exec_cmd(cmd); +#endif + + tmp = cmd_print(cmd); log_debug("cmdq %p: %s", cmdq, tmp); + free(tmp); cmdq->time = time(NULL); cmdq->number++; cmdq_guard(cmdq, "begin", flags); + if (cmd_prepare_state(cmd, cmdq, NULL) != 0) + goto error; retval = cmd->entry->exec(cmd, cmdq); - if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error", flags); - else - cmdq_guard(cmdq, "end", flags); + 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 client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; int empty; @@ -203,8 +241,8 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->references++; notify_disable(); - log_debug("continuing cmdq %p: flags=%#x, client=%d", cmdq, cmdq->flags, - c != NULL ? c->ibuf.fd : -1); + log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags, + c); empty = TAILQ_EMPTY(&cmdq->queue); if (empty) @@ -267,3 +305,4 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } + diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 5a45ec25..79e5aad0 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -27,24 +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, - 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"); diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 481154ce..b40f44f7 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -29,18 +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, - 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]; @@ -53,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 2f677a48..a1f15eef 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -29,25 +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, - 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 b342307d..7ec65f10 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -31,25 +31,30 @@ enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); const struct cmd_entry cmd_resize_pane_entry = { - "resize-pane", "resizep", - "DLMRt:Ux:y:Z", 0, 1, - "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE - " [adjustment]", - 0, - cmd_resize_pane_exec + .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 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; - struct winlink *wl; - struct window *w; + struct session *s = cmdq->state.tflag.s; const char *errstr; char *cause; - struct window_pane *wp; u_int adjust; int x, y; @@ -63,10 +68,6 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 6575e8e4..ba2c1cd2 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * Copyright (c) 2011 Marcel P. Partap * * Permission to use, copy, modify, and distribute this software for any @@ -31,31 +31,32 @@ 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, - 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; + 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; struct environ_entry *envent; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (!args_has(self->args, 'k') && wp->fd != -1) { if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); @@ -64,10 +65,10 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) 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); @@ -75,22 +76,22 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + 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 06102ed0..95fc0cb4 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -30,44 +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, - 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; + struct environ *env; const char *path; char *cause; struct environ_entry *envent; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - 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); @@ -78,18 +79,18 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + 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); @@ -101,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 859ff04a..94eca37d 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -27,27 +27,27 @@ 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_exec + .name = "rotate-window", + .alias = "rotatew", + + .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 17ac0f76..0bd8fc59 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -36,11 +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, - 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 { @@ -75,33 +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; - int cwd; + const char *cwd; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - cwd = wp->cwd; - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - if (cmdq->client != NULL && cmdq->client->session == NULL) - cwd = cmdq->client->cwd; - else if (s != NULL) - cwd = s->cwd; - else - cwd = -1; - } - - ft = format_create(); - format_defaults(ft, NULL, s, wl, 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, cmdq->state.c, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 1e020934..56472565 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -34,19 +34,25 @@ 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, - 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, - 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 @@ -56,10 +62,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *bufname, *bufdata, *start, *end; - char *msg; + const char *path, *bufname, *bufdata, *start, *end, *cwd; + const char *flags; + char *msg, *file, resolved[PATH_MAX]; size_t size, used, msglen, bufsize; - int cwd, fd; FILE *f; if (!args_has(args, 'b')) { @@ -91,31 +97,35 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) goto do_print; } - if (c != NULL && c->session == NULL) + if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; - else if ((s = cmd_find_current(cmdq)) != NULL) + else if ((s = c->session) != NULL && s->cwd != NULL) cwd = s->cwd; else - cwd = AT_FDCWD; + cwd = "."; - f = NULL; - if (args_has(self->args, 'a')) { - fd = openat(cwd, path, O_CREAT|O_RDWR|O_APPEND, 0600); - if (fd != -1) - f = fdopen(fd, "ab"); - } else { - fd = openat(cwd, path, O_CREAT|O_RDWR|O_TRUNC, 0600); - if (fd != -1) - f = fdopen(fd, "wb"); - } - if (f == NULL) { - if (fd != -1) - close(fd); - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + 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); } + 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: fwrite error", path); + cmdq_error(cmdq, "%s: write error", resolved); fclose(f); return (CMD_RETURN_ERROR); } @@ -125,7 +135,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) do_stdout: evbuffer_add(c->stdout_data, bufdata, bufsize); - server_push_stdout(c); + server_client_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 14737dc8..44f01bb7 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -29,43 +29,55 @@ enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { - "select-layout", "selectl", - "nopt:", 0, 1, - "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - 0, - 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, - 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, - cmd_select_layout_exec + .name = "previous-layout", + .alias = "prevl", + + .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); w = wl->window; - server_unzoom_window(w); next = self->entry == &cmd_next_layout_entry; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e76587cc..14d53d48 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -27,38 +27,44 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { - "select-pane", "selectp", - "DdegLlMmP:Rt:U", 0, 0, - "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, - 0, - 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", - "det:", 0, 0, - "[-de] " CMD_TARGET_WINDOW_USAGE, - 0, - cmd_select_pane_exec + .name = "last-pane", + .alias = "lastp", + + .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 *w; - struct session *s; - struct window_pane *wp, *lastwp, *markedwp; + 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); - w = wl->window; - if (w->last == NULL) { + if (wl->window->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } @@ -79,20 +85,16 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) return (CMD_RETURN_NORMAL); - lastwp = marked_window_pane; + 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_window_pane; + markedwp = marked_pane.wp; if (lastwp != NULL) { server_redraw_window_borders(lastwp->window); @@ -120,14 +122,19 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'L')) + if (args_has(self->args, 'L')) { + server_unzoom_window(wp->window); wp = window_pane_find_left(wp); - else if (args_has(self->args, 'R')) + } 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')) + } 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')) + } 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); diff --git a/cmd-select-window.c b/cmd-select-window.c index f530f1fe..78228067 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -29,43 +29,62 @@ 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_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_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_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, - 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 }; 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; @@ -79,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) { @@ -103,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 b1f8d672..92c75ec3 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -30,30 +30,41 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { - "send-keys", "send", - "lRMt:", 0, -1, - "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", - 0, - 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, - 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 = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; struct mouse_event *m = &cmdq->item->mouse; - struct window_pane *wp; - struct session *s; - const u_char *str; - int i, key; + const u_char *keystr; + int i, literal; + key_code key; if (args_has(args, 'M')) { wp = cmd_mouse_pane(m, &s, NULL); @@ -65,30 +76,30 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - 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"); + key = options_get_number(s->options, "prefix2"); else - key = options_get_number(&s->options, "prefix"); + key = options_get_number(s->options, "prefix"); window_pane_key(wp, NULL, s, key, NULL); return (CMD_RETURN_NORMAL); } if (args_has(args, 'R')) - input_reset(wp); + input_reset(wp, 1); for (i = 0; i < args->argc; i++) { - str = args->argv[i]; - - if (!args_has(args, 'l') && - (key = key_string_lookup_string(str)) != KEYC_NONE) { - window_pane_key(wp, NULL, s, key, NULL); - } else { - for (; *str != '\0'; str++) - window_pane_key(wp, NULL, s, *str, NULL); + 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-set-buffer.c b/cmd-set-buffer.c index e7f7627e..1f0cf3d8 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -30,19 +30,25 @@ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { - "set-buffer", "setb", - "ab:n:", 0, 1, - "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", - 0, - 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 = { - "delete-buffer", "deleteb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - cmd_set_buffer_exec + .name = "delete-buffer", + .alias = "deleteb", + + .args = { "b:", 0, 0 }, + .usage = CMD_BUFFER_USAGE, + + .flags = 0, + .exec = cmd_set_buffer_exec }; enum cmd_retval diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 83e63b48..ba295ea6 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -30,20 +30,24 @@ 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, - 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; + const char *name, *value, *target; name = args->argv[0]; if (*name == '\0') { @@ -61,11 +65,17 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) value = args->argv[1]; if (args_has(self->args, 'g')) - env = &global_environ; + env = global_environ; else { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) + if (cmdq->state.tflag.s == NULL) { + target = args_get(args, 't'); + if (target != NULL) + cmdq_error(cmdq, "no such session: %s", target); + else + cmdq_error(cmdq, "no current session"); return (CMD_RETURN_ERROR); - env = &s->environ; + } + env = cmdq->state.tflag.s->environ; } if (args_has(self->args, 'u')) { @@ -79,13 +89,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 e0b07edb..b1771436 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -65,32 +65,42 @@ struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, 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, - 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, - 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; - const char *optstr, *valstr; + struct client *c; + const struct options_table_entry *oe; + struct options *oo; + const char *optstr, *valstr, *target; /* Get the option name and value. */ optstr = args->argv[0]; @@ -108,8 +118,8 @@ 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) { + 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); @@ -124,37 +134,35 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) 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; - else { - wl = cmd_find_window(cmdq, args_get(args, 't'), 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; - } - } else if (table == session_options_table) { + oo = global_w_options; + else if (wl == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such window: %s", + target); + } else + cmdq_error(cmdq, "no current window"); + return (CMD_RETURN_ERROR); + } else + oo = wl->window->options; + } else if (oe->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) { - 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 = global_s_options; + else if (s == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such session: %s", + target); + } else + cmdq_error(cmdq, "no current session"); + return (CMD_RETURN_ERROR); + } else + oo = s->options; } else { cmdq_error(cmdq, "unknown table"); return (CMD_RETURN_ERROR); @@ -179,10 +187,14 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* 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")) + 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(); @@ -205,31 +217,23 @@ 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')) { @@ -276,7 +280,7 @@ cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, return (-1); } - if (args_has(args, 'g') || oo == &global_options) { + 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); @@ -350,7 +354,7 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, /* Set a string option. */ struct options_entry * -cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, +cmd_set_option_string(struct cmd *self, __unused struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -372,7 +376,7 @@ 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, +cmd_set_option_number(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -390,13 +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, +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); } @@ -406,7 +411,7 @@ 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, +cmd_set_option_colour(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -422,7 +427,7 @@ 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, +cmd_set_option_attributes(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -438,7 +443,7 @@ 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, +cmd_set_option_flag(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -466,7 +471,7 @@ 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) { diff --git a/cmd-show-environment.c b/cmd-show-environment.c index af24d91b..29e89274 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -34,11 +34,16 @@ void cmd_show_environment_print(struct cmd *, struct cmd_q *, struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { - "show-environment", "showenv", - "gst:", 0, 1, - "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - 0, - 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 * @@ -86,17 +91,29 @@ 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; + const char *target; + + if ((target = args_get(args, 't')) != NULL) { + if (cmdq->state.tflag.s == NULL) { + cmdq_error(cmdq, "no such session: %s", target); + return (CMD_RETURN_ERROR); + } + } if (args_has(self->args, 'g')) - env = &global_environ; + env = global_environ; else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) + if (cmdq->state.tflag.s == NULL) { + target = args_get(args, 't'); + if (target != NULL) + cmdq_error(cmdq, "no such session: %s", target); + else + cmdq_error(cmdq, "no current session"); return (CMD_RETURN_ERROR); - env = &s->environ; + } + env = cmdq->state.tflag.s->environ; } if (args->argc != 0) { @@ -109,7 +126,10 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - RB_FOREACH(envent, environ, env) + 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 d85baba9..fa796eed 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -31,41 +31,32 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { - "show-messages", "showmsgs", - "IJTt:", 0, 0, - "[-IJT] " CMD_TARGET_CLIENT_USAGE, - 0, - 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 = { - "server-info", "info", - "", 0, 0, - "", - 0, - cmd_show_messages_exec + .name = "server-info", + .alias = "info", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_show_messages_exec }; -int cmd_show_messages_server(struct cmd_q *); int cmd_show_messages_terminals(struct cmd_q *, int); int cmd_show_messages_jobs(struct cmd_q *, int); -int -cmd_show_messages_server(struct cmd_q *cmdq) -{ - char *tim; - - tim = ctime(&start_time); - *strchr(tim, '\n') = '\0'; - - cmdq_print(cmdq, "started %s", tim); - cmdq_print(cmdq, "socket path %s", socket_path); - cmdq_print(cmdq, "debug level %d", debug_level); - cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); - - return (1); -} - int cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { @@ -110,16 +101,12 @@ 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; int done, blank; done = blank = 0; - if (args_has(args, 'I') || self->entry == &cmd_server_info_entry) { - blank = cmd_show_messages_server(cmdq); - done = 1; - } if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { blank = cmd_show_messages_terminals(cmdq, blank); done = 1; @@ -131,9 +118,6 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) if (done) return (CMD_RETURN_NORMAL); - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - 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 a5011e72..322f532c 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -32,63 +32,80 @@ 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, - 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, - 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 options *oo; - int quiet; + struct args *args = self->args; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct options *oo; + enum options_table_scope scope; + int quiet; + const char *target; 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 if (wl == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such window: %s", target); + } else + cmdq_error(cmdq, "no current window"); + return (CMD_RETURN_ERROR); + } 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 if (s == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such session: %s", target); + } else + cmdq_error(cmdq, "no current session"); + return (CMD_RETURN_ERROR); + } 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)); } @@ -99,7 +116,7 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, { struct args *args = self->args; const char *name = args->argv[0]; - const struct options_table_entry *table, *oe; + const struct options_table_entry *oe; struct options_entry *o; const char *optval; @@ -118,14 +135,14 @@ retry: return (CMD_RETURN_NORMAL); } - table = oe = NULL; - if (options_table_find(name, &table, &oe) != 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); + return (CMD_RETURN_NORMAL); cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } @@ -144,30 +161,33 @@ retry: } 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++) { - if (oe->style != NULL) + 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 e5710a0c..9d2d6d68 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_source_file_exec(struct cmd *, 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, - cmd_source_file_exec + .name = "source-file", + .alias = "source", + + .args = { "", 1, 1 }, + .usage = "path", + + .flags = 0, + .exec = cmd_source_file_exec }; enum cmd_retval @@ -45,8 +48,7 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_q *cmdq1; char *cause; - cmdq1 = cmdq_new(NULL); - cmdq1->client = cmdq->client; + cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_source_file_done; cmdq1->data = cmdq; diff --git a/cmd-split-window.c b/cmd-split-window.c index 50799cff..93e23cdd 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -35,44 +35,46 @@ enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { - "split-window", "splitw", - "bc:dF:l:hp:Pt:v", 0, -1, - "[-bdhvP] [-c start-directory] [-F format] [-p percentage|-l size] " - CMD_TARGET_PANE_USAGE " [command]", - 0, - cmd_split_window_exec + .name = "split-window", + .alias = "splitw", + + .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, *path, *shell, *template; + 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 argc, size, percentage, cwd, fd = -1; + int argc, size, percentage; enum layout_type type; struct layout_cell *lc; struct format_tree *ft; 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"); + cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; @@ -85,24 +87,12 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) argv = args->argv; } + to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); - cp = format_expand(ft, args_get(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); - - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else if (cp != NULL) - free(cp); - cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else @@ -134,9 +124,9 @@ 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; @@ -150,13 +140,13 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, &env, + if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env, s->tio, &cause) != 0) goto error; @@ -169,15 +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(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, - 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); @@ -187,19 +176,20 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } notify_window_layout_changed(w); - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - environ_free(&env); + 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 (fd != -1) - close(fd); + + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/cmd-string.c b/cmd-string.c index db1723cb..fdef4a82 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -126,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)); } @@ -303,10 +303,14 @@ cmd_string_variable(const char *s, size_t *p) 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("")); +#ifdef TMATE + if (envent->value == NULL) + return (xstrdup("")); +#endif return (xstrdup(envent->value)); error: @@ -326,7 +330,7 @@ cmd_string_expand_tilde(const char *s, size_t *p) last = cmd_string_getc(s, p); if (last == EOF || last == '/' || last == ' '|| last == '\t') { - envent = environ_find(&global_environ, "HOME"); + envent = environ_find(global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') home = envent->value; else if ((pw = getpwuid(getuid())) != NULL) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index dc2b7246..3539c95c 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -29,55 +29,53 @@ 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_exec + .name = "swap-pane", + .alias = "swapp", + + .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 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_w = dst_wl->window; + dst_w = cmdq->state.tflag.wl->window; + dst_wp = cmdq->state.tflag.wp; + src_w = cmdq->state.sflag.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_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 { - src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, - &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); - src_w = src_wl->window; - } - } else { - src_wl = cmd_find_pane_marked(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_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); +#ifdef TMATE + if (src_w != dst_w) { + cmdq_error(cmdq, "swap pane on different window is not supported with tmate"); + return (CMD_RETURN_ERROR); + } +#endif + if (src_wp == dst_wp) return (CMD_RETURN_NORMAL); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 51682e37..5830a31e 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -29,34 +29,41 @@ 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, - 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 cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - const char *target_src, *target_dst; +#ifdef TMATE + cmdq_error(cmdq, "swap window is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else 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_marked(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); } @@ -82,4 +89,5 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 3a72886a..6e2ee2a0 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -30,34 +30,33 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { - "switch-client", "switchc", - "lc:Enpt:rT:", 0, 0, - "[-Elnpr] [-c target-client] [-t target-session] [-T key-table]", - CMD_READONLY, - cmd_switch_client_exec + .name = "switch-client", + .alias = "switchc", + + .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 = NULL; - struct winlink *wl = NULL; - struct window *w = NULL; - struct window_pane *wp = NULL; - const char *tflag, *tablename, *update; + 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')) { - if (c->flags & CLIENT_READONLY) - c->flags &= ~CLIENT_READONLY; - else - c->flags |= CLIENT_READONLY; - } + if (args_has(args, 'r')) + c->flags ^= CLIENT_READONLY; tablename = args_get(args, 'T'); if (tablename != NULL) { @@ -69,9 +68,9 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) table->references++; key_bindings_unref_table(c->keytable); c->keytable = table; + return (CMD_RETURN_NORMAL); } - tflag = args_get(args, 't'); if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(cmdq, "can't find next session"); @@ -85,48 +84,32 @@ 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 { - if (tflag == NULL) { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - } else if (tflag[strcspn(tflag, ":.")] != '\0') { - if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - } else { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - w = window_find_by_id_str(tflag); - if (w == NULL) { - wp = window_pane_find_by_id_str(tflag); - if (wp != NULL) - w = wp->window; - } - if (w != NULL) - wl = winlink_find_by_window(&s->windows, w); - } - if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); - - if (wl != NULL) { + if (state->tflag.wl != NULL) { + wp = state->tflag.wp; if (wp != NULL) window_set_active_pane(wp->window, wp); - session_set_current(s, wl); + 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); + 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; + server_client_set_key_table(c, NULL); status_timer_start(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); @@ -135,6 +118,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) 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 493f6dde..7452fd9f 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -26,22 +26,26 @@ * Unbind key from command. */ -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 *, 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:T:", 0, 1, - "[-acn] [-t mode-table] [-T key-table] key", - 0, - cmd_unbind_key_exec + .name = "unbind-key", + .alias = "unbind", + + .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; - int key; + key_code key; const char *tablename; if (!args_has(args, 'a')) { @@ -50,7 +54,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) 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); } @@ -59,13 +63,13 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "key given with -a"); return (CMD_RETURN_ERROR); } - key = KEYC_NONE; + key = KEYC_UNKNOWN; } if (args_has(args, 't')) return (cmd_unbind_key_mode_table(self, cmdq, key)); - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { key_bindings_remove_table("root"); @@ -95,7 +99,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_unbind_key_mode_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; @@ -108,7 +112,7 @@ cmd_unbind_key_mode_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-wait-for.c b/cmd-wait-for.c index e38ea8f1..e460599c 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Nicholas Marriott * Copyright (c) 2013 Thiago de Arruda * * Permission to use, copy, modify, and distribute this software for any @@ -23,6 +23,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Block or wake a client on a named wait channel. @@ -31,11 +32,14 @@ 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, - "[-L|-S|-U] channel", - 0, - 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 { @@ -111,7 +115,7 @@ cmd_wait_for_remove(struct wait_channel *wc) } enum cmd_retval -cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *name = args->argv[0]; @@ -130,7 +134,7 @@ cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) } enum cmd_retval -cmd_wait_for_signal(unused 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; @@ -155,12 +159,44 @@ cmd_wait_for_signal(unused struct cmd_q *cmdq, const char *name, return (CMD_RETURN_NORMAL); } +#ifdef TMATE +void signal_waiting_clients(const char *name) +{ + struct wait_channel *wc, wc0; + struct cmd_q *wq, *wq1; + + wc0.name = name; + wc = RB_FIND(wait_channels, &wait_channels, &wc0); + + if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { + return; + } + + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + + if (!wc->locked) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } +} +#endif + 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.tmate_env_ready) + return (CMD_RETURN_NORMAL); +#endif + if (c == NULL || c->session != NULL) { cmdq_error(cmdq, "not able to wait"); return (CMD_RETURN_ERROR); @@ -170,12 +206,11 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, wc = cmd_wait_for_add(name); if (wc->woken) { - log_debug("wait channel %s already woken (client %d)", wc->name, - c->tty.fd); + 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 (client %d)", wc->name, c->tty.fd); + log_debug("wait channel %s not woken (%p)", wc->name, c); TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; diff --git a/cmd.c b/cmd.c index badfcb4a..28efa0c5 100644 --- a/cmd.c +++ b/cmd.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -95,10 +95,12 @@ 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; @@ -182,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, @@ -344,12 +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) + if (entry->args.upper != -1 && args->argc > entry->args.upper) goto usage; cmd = xcalloc(1, sizeof *cmd); @@ -383,21 +387,192 @@ usage: return (NULL); } -size_t -cmd_print(struct cmd *cmd, char *buf, size_t len) +static int +cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, + struct cmd_q *cmdq, struct cmd_q *parent) { - size_t off, used; + int targetflags, error; + struct cmd_find_state *fs = NULL; + struct cmd_find_state *current = NULL; + struct cmd_find_state tmp; - off = xsnprintf(buf, len, "%s ", cmd->entry->name); - if (off + 1 < len) { - used = args_print(cmd->args, buf + off, len - off - 1); - if (used == 0) - off--; + if (flag == CMD_NONE || + flag == CMD_CLIENT || + flag == CMD_CLIENT_CANFAIL) + return (0); + + if (c == 't') + fs = &cmdq->state.tflag; + else if (c == 's') + fs = &cmdq->state.sflag; + + 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); + + targetflags = 0; + switch (flag) { + case CMD_SESSION: + case CMD_SESSION_CANFAIL: + case CMD_SESSION_PREFERUNATTACHED: + if (flag == CMD_SESSION_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_SESSION_PREFERUNATTACHED) + targetflags |= CMD_FIND_PREFER_UNATTACHED; + break; + case CMD_MOVEW_R: + flag = CMD_WINDOW_INDEX; + /* FALLTHROUGH */ + case CMD_WINDOW: + case CMD_WINDOW_CANFAIL: + case CMD_WINDOW_MARKED: + case CMD_WINDOW_INDEX: + 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; + break; + case CMD_PANE: + case CMD_PANE_CANFAIL: + case CMD_PANE_MARKED: + if (flag == CMD_PANE_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_PANE_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + break; + default: + fatalx("unknown %cflag %d", c, flag); + } + + log_debug("%s: flag %c %d %#x", __func__, c, flag, targetflags); + if (parent != NULL) { + if (c == 't') + current = &parent->state.tflag; + else if (c == 's') + current = &parent->state.sflag; + } else { + error = cmd_find_current(&tmp, cmdq, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) + return (-1); + current = &tmp; + } + + 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: + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_SESSION, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) + return (-1); + break; + case CMD_MOVEW_R: + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_SESSION, CMD_FIND_QUIET); + if (error == 0) + break; + /* FALLTHROUGH */ + case CMD_WINDOW: + case CMD_WINDOW_CANFAIL: + case CMD_WINDOW_MARKED: + case CMD_WINDOW_INDEX: + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_WINDOW, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) + return (-1); + break; + case CMD_PANE: + case CMD_PANE_CANFAIL: + case CMD_PANE_MARKED: + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_PANE, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) + return (-1); + break; + default: + fatalx("unknown %cflag %d", c, flag); + } + return (0); +} + +int +cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent) +{ + 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; + } + + s = args_get(cmd->args, 't'); + log_debug("preparing -t state: target %s", s == NULL ? "none" : s); + + error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq, parent); + 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('s', s, entry->sflag, cmdq, parent); + if (error != 0) + return (error); + + return (0); +} + +char * +cmd_print(struct cmd *cmd) +{ + char *out, *s; + + 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); } /* Adjust current mouse position for a pane. */ diff --git a/colour.c b/colour.c index a56ddce9..b349e2a5 100644 --- a/colour.c +++ b/colour.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 diff --git a/compat.h b/compat.h index 8666a1d8..b2267d37 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -21,6 +21,9 @@ #define __attribute__(a) #endif +#ifndef __unused +#define __unused __attribute__ ((__unused__)) +#endif #ifndef __dead #define __dead __attribute__ ((__noreturn__)) #endif @@ -255,13 +258,18 @@ int unsetenv(const char *); #ifndef HAVE_CFMAKERAW /* cfmakeraw.c */ -void cfmakeraw(struct termios *); +void cfmakeraw(struct termios *); #endif #ifndef HAVE_OPENAT /* openat.c */ #define AT_FDCWD -100 -int openat(int, const char *, int, ...); +int openat(int, const char *, int, ...); +#endif + +#ifndef HAVE_REALLOCARRAY +/* reallocarray.c */ +void *reallocarray(void *, size_t, size_t size); #endif #ifdef HAVE_GETOPT diff --git a/compat/asprintf.c b/compat/asprintf.c index 09020b35..5ed1c48c 100644 --- a/compat/asprintf.c +++ b/compat/asprintf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 Nicholas Marriott + * Copyright (c) 2006 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 diff --git a/compat/b64_ntop.c b/compat/b64_ntop.c index 2b4dc2d4..fd8930f5 100644 --- a/compat/b64_ntop.c +++ b/compat/b64_ntop.c @@ -52,6 +52,8 @@ #include #include +#include "compat.h" + #define Assert(Cond) if (!(Cond)) abort() static const char Base64[] = @@ -122,7 +124,7 @@ static const char Pad64 = '='; */ int -b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) { +b64_ntop(const char *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; uint8_t input[3]; uint8_t output[4]; diff --git a/compat/cfmakeraw.c b/compat/cfmakeraw.c index 85b2c9bc..d8794081 100644 --- a/compat/cfmakeraw.c +++ b/compat/cfmakeraw.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013 Dagobert Michelsen - * Copyright (c) 2013 Nicholas Marriott + * 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 diff --git a/compat/closefrom.c b/compat/closefrom.c index 591769dd..8c650aa5 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -14,8 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "tmux.h" - #ifndef HAVE_CLOSEFROM #include @@ -47,6 +45,8 @@ # endif #endif +#include "tmux.h" + #ifndef OPEN_MAX # define OPEN_MAX 256 #endif diff --git a/compat/fgetln.c b/compat/fgetln.c index a5c2489d..0ad6378a 100644 --- a/compat/fgetln.c +++ b/compat/fgetln.c @@ -1,43 +1,26 @@ -/* $NetBSD: fgetln.c,v 1.3 2007/08/07 02:06:58 lukem Exp $ */ - -/*- - * Copyright (c) 1998 The NetBSD Foundation, Inc. - * All rights reserved. +/* + * Copyright (c) 2015 Joerg Jung * - * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. + * 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. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * 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 +/* + * portable fgetln() version, NOT reentrant + */ -#include #include #include -#include +#include #include "tmux.h" @@ -45,41 +28,34 @@ char * fgetln(FILE *fp, size_t *len) { static char *buf = NULL; - static size_t bufsiz = 0; - char *ptr; + static size_t bufsz = 0; + size_t r = 0; + char *p; + int c, e; - - if (buf == NULL) { - bufsiz = BUFSIZ; - if ((buf = malloc(bufsiz)) == NULL) - return NULL; - } - - if (fgets(buf, bufsiz, fp) == NULL) + if (!fp || !len) { + errno = EINVAL; return NULL; - - *len = 0; - while ((ptr = strchr(&buf[*len], '\n')) == NULL) { - size_t nbufsiz = bufsiz + BUFSIZ; - char *nbuf = realloc(buf, nbufsiz); - - if (nbuf == NULL) { - int oerrno = errno; - free(buf); - errno = oerrno; - buf = NULL; - return NULL; - } else - buf = nbuf; - - *len = bufsiz; - if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) - return buf; - - bufsiz = nbufsiz; } - - *len = (ptr - buf) + 1; - return buf; + if (!buf) { + if (!(buf = calloc(1, BUFSIZ))) + return NULL; + bufsz = BUFSIZ; + } + while ((c = getc(fp)) != EOF) { + buf[r++] = c; + if (r == bufsz) { + if (!(p = reallocarray(buf, 2, bufsz))) { + e = errno; + free(buf); + errno = e; + buf = NULL, bufsz = 0; + return NULL; + } + buf = p, bufsz = 2 * bufsz; + } + if (c == '\n') + break; + } + return (*len = r) ? buf : NULL; } - diff --git a/compat/forkpty-aix.c b/compat/forkpty-aix.c index 6894aa44..2557ebf7 100644 --- a/compat/forkpty-aix.c +++ b/compat/forkpty-aix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 diff --git a/compat/forkpty-hpux.c b/compat/forkpty-hpux.c index 59130e1b..09b27d08 100644 --- a/compat/forkpty-hpux.c +++ b/compat/forkpty-hpux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 diff --git a/compat/forkpty-sunos.c b/compat/forkpty-sunos.c index 554e51ac..9abda46c 100644 --- a/compat/forkpty-sunos.c +++ b/compat/forkpty-sunos.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 diff --git a/compat/openat.c b/compat/openat.c index 6b04eedc..d003e53d 100644 --- a/compat/openat.c +++ b/compat/openat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Nicholas Marriott + * 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 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 6c7d29ec..b16b08cf 100644 --- a/compat/setenv.c +++ b/compat/setenv.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2010 Dagobert Michelsen - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 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 diff --git a/compat/vis.h b/compat/vis.h index 6795139c..9f12d236 100644 --- a/compat/vis.h +++ b/compat/vis.h @@ -73,9 +73,6 @@ */ #define UNVIS_END 1 /* no more characters */ -#include - -__BEGIN_DECLS char *vis(char *, int, int, int); int strvis(char *, const char *, int); int stravis(char **, const char *, int); @@ -85,6 +82,4 @@ int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t); -__END_DECLS - #endif /* !_VIS_H_ */ diff --git a/configure.ac b/configure.ac index 93aae20d..028d5559 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ # configure.ac -# Miscellaneous bits. -AC_INIT(tmux, 2.1) +AC_INIT(tmate, 2.4.0) +AM_SILENT_RULES([yes]) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -19,12 +19,13 @@ AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_EGREP AC_PROG_INSTALL +PKG_PROG_PKG_CONFIG # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? -#found_debug=yes +found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, enable debug build flags), @@ -47,7 +48,21 @@ AC_ARG_ENABLE( found_static=$enable_static ) if test "x$found_static" = xyes; then - LDFLAGS="$LDFLAGS -static" + # XXX Static build are only doable with the musl library + PKG_CONFIG="pkg-config --static" + + CFLAGS="$CFLAGS -flto" + LDFLAGS="$LDFLAGS -flto -static -no-pie" + + PKG_CHECK_MODULES([ZLIB], [zlib], [ + CPPFLAGS="$ZLIB_CFLAGS $CPPFLAGS" + LIBS="$ZLIB_LIBS $LIBS" + ]) + + PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto], [ + CPPFLAGS="$LIBCRYPTO_CFLAGS $CPPFLAGS" + LIBS="$LIBCRYPTO_LIBS $LIBS" + ]) fi # Is this gcc? @@ -88,6 +103,7 @@ AC_CHECK_HEADERS( bitstring.h \ curses.h \ dirent.h \ + execinfo.h \ fcntl.h \ inttypes.h \ libutil.h \ @@ -107,9 +123,13 @@ AC_CHECK_HEADERS( # Look for library needed for flock. AC_SEARCH_LIBS(flock, bsd) +# Look for library needed for backtrace +AC_SEARCH_LIBS(backtrace, execinfo) + # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ + backtrace \ dirfd \ flock \ setproctitle \ @@ -143,22 +163,68 @@ if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi -# Look for curses. -AC_SEARCH_LIBS( - setupterm, - [terminfo curses ncurses tinfo], - 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") 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" +AC_CHECK_HEADER(utempter.h, found_utempter=yes, found_utempter=no) +if test "x$found_utempter" = xyes; then + AC_SEARCH_LIBS( + utempter_add_record, + utempter, + found_utempter=yes, + found_utempter=no + ) + if test "x$found_utempter" = xyes; then + AC_DEFINE(HAVE_UTEMPTER) + fi +fi + +PKG_CHECK_MODULES( + MSGPACK, + msgpack >= 1.1.0, + [ + CPPFLAGS="$MSGPACK_CFLAGS $CPPFLAGS" + LIBS="$MSGPACK_LIBS $LIBS" + found_msgpack=yes + ], + found_msgpack=no +) +if test "x$found_msgpack" = xno; then + AC_MSG_ERROR("msgpack >= 1.1.0 not found") +fi + +PKG_CHECK_MODULES( + LIBSSH, + libssh >= 0.8.4, + [ + CPPFLAGS="$LIBSSH_CFLAGS $CPPFLAGS" + LIBS="$LIBSSH_LIBS $LIBS" + found_libssh=yes + ], + found_libssh=no +) +if test "x$found_libssh" = xno; then + AC_MSG_ERROR("libssh >= 0.8.4 not found") fi # Check for b64_ntop. @@ -365,6 +431,13 @@ if test "x$found_openat" = xyes; then 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. AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) @@ -481,6 +554,7 @@ case "$host_os" in *linux*) AC_MSG_RESULT(linux) PLATFORM=linux + AC_DEFINE(IS_LINUX) ;; *freebsd*) AC_MSG_RESULT(freebsd) diff --git a/control-notify.c b/control-notify.c index 943d670c..c28d0fc8 100644 --- a/control-notify.c +++ b/control-notify.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2012 Nicholas Marriott + * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any @@ -19,6 +19,8 @@ #include +#include + #include "tmux.h" #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ @@ -65,6 +67,10 @@ control_notify_window_layout_changed(struct window *w) struct format_tree *ft; struct winlink *wl; const char *template; + char *expanded; + + 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) @@ -81,20 +87,21 @@ 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_defaults(ft, c, NULL, wl, NULL); - control_write(c, "%s", format_expand(ft, template)); + 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; struct session *cs; @@ -112,7 +119,7 @@ control_notify_window_unlinked(unused struct session *s, struct window *w) } 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; @@ -176,7 +183,7 @@ 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; @@ -189,7 +196,7 @@ 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; diff --git a/control.c b/control.c index 11fa2d80..f6dedca8 100644 --- a/control.c +++ b/control.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2012 Nicholas Marriott + * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any @@ -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,12 +46,12 @@ 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; diff --git a/environ.c b/environ.c index 11b8c849..d855f8b5 100644 --- a/environ.c +++ b/environ.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -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); } @@ -146,9 +184,9 @@ environ_update(const char *vars, struct environ *srcenv, 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); } @@ -158,10 +196,10 @@ void environ_push(struct environ *env) { struct environ_entry *envent; - char **vp, *v; + char *v; - for (vp = environ; *vp != NULL; vp++) { - v = xstrdup(*vp); + while (*environ != NULL) { + v = xstrdup(*environ); v[strcspn(v, "=")] = '\0'; unsetenv(v); diff --git a/example_tmux.conf b/example_tmux.conf new file mode 100644 index 00000000..f659a3c2 --- /dev/null +++ b/example_tmux.conf @@ -0,0 +1,66 @@ +# +# Example .tmux.conf +# +# By Nicholas Marriott. Public domain. +# + +# Some tweaks to the status line +set -g status-bg green +set -g status-right "%H:%M" +set -g window-status-current-attr "underscore" + +# No bells at all +set -g bell-action none + +# Lock after 15 minutes +set -g lock-after-time 1800 + +# Keep windows around after they exit +set -g remain-on-exit on + +# Turn on xterm-keys so that additional function keys get escape sequences +set -g xterm-keys on + +# Change the prefix key to C-a +set -g prefix C-a +unbind C-b +bind C-a send-prefix + +# Turn the mouse on, but without copy mode dragging +set -g mouse on +unbind -n MouseDrag1Pane +unbind -temacs-copy MouseDrag1Pane + +# Some extra key bindings to select higher numbered windows +bind F1 selectw -t:10 +bind F2 selectw -t:11 +bind F3 selectw -t:12 +bind F4 selectw -t:13 +bind F5 selectw -t:14 +bind F6 selectw -t:15 +bind F7 selectw -t:16 +bind F8 selectw -t:17 +bind F9 selectw -t:18 +bind F10 selectw -t:19 +bind F11 selectw -t:20 +bind F12 selectw -t:21 + +# Keys to toggle monitoring activity in a window, and synchronize-panes +bind m set monitor-activity +bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,on,off}' + +# Keys to hide and show a window name from the status line +bind '-' set window-status-format '#I'\; set window-status-current-format '#I' +bind '+' set window-status-format '#I:#W#F'\; set window-status-current-format '#I:#W#F' + +# Create a single default session +new -d -s0 -nirssi 'exec irssi' +set -t0:0 monitor-activity on +set -t0:0 aggressive-resize on +neww -d -ntodo 'exec emacs ~/TODO' +setw -t0:1 aggressive-resize on +neww -d -nmutt 'exec mutt' +setw -t0:2 aggressive-resize on +neww -d +neww -d +neww -d diff --git a/examples/bash_completion_tmux.sh b/examples/bash_completion_tmux.sh deleted file mode 100644 index 74728b91..00000000 --- a/examples/bash_completion_tmux.sh +++ /dev/null @@ -1,105 +0,0 @@ -# START tmux completion -# This file is in the public domain -# See: http://www.debian-administration.org/articles/317 for how to write more. -# Usage: Put "source bash_completion_tmux.sh" into your .bashrc -_tmux() -{ - local cur prev opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - opts=" \ - attach-session \ - bind-key \ - break-pane \ - capture-pane \ - choose-client \ - choose-session \ - choose-window \ - clear-history \ - clock-mode \ - command-prompt \ - confirm-before \ - copy-buffer \ - copy-mode \ - delete-buffer \ - detach-client \ - display-message \ - display-panes \ - down-pane \ - find-window \ - has-session \ - if-shell \ - join-pane \ - kill-pane \ - kill-server \ - kill-session \ - kill-window \ - last-window \ - link-window \ - list-buffers \ - list-clients \ - list-commands \ - list-keys \ - list-panes \ - list-sessions \ - list-windows \ - load-buffer \ - lock-client \ - lock-server \ - lock-session \ - move-window \ - new-session \ - new-window \ - next-layout \ - next-window \ - paste-buffer \ - pipe-pane \ - previous-layout \ - previous-window \ - refresh-client \ - rename-session \ - rename-window \ - resize-pane \ - respawn-window \ - rotate-window \ - run-shell \ - save-buffer \ - select-layout \ - select-pane \ - select-prompt \ - select-window \ - send-keys \ - send-prefix \ - server-info \ - set-buffer \ - set-environment \ - set-option \ - set-window-option \ - show-buffer \ - show-environment \ - show-messages \ - show-options \ - show-window-options \ - source-file \ - split-window \ - start-server \ - suspend-client \ - swap-pane \ - swap-window \ - switch-client \ - unbind-key \ - unlink-window \ - up-pane" - - COMPREPLY=($(compgen -W "${opts}" -- ${cur})) - return 0 - -} -complete -F _tmux tmux - -# END tmux completion - - - diff --git a/examples/h-boetes.conf b/examples/h-boetes.conf deleted file mode 100644 index 2aa86dc5..00000000 --- a/examples/h-boetes.conf +++ /dev/null @@ -1,42 +0,0 @@ -# $Id: h-boetes.conf,v 1.2 2009-10-25 21:45:26 nicm Exp $ -# -# From Han Boetes. - -set -g default-command zsh -set -g status-right "#(uptime|awk '{print $11}') #(date)" - -# Statusbar properties. -set -g display-time 3000 -set -g status-bg black -set -g status-fg cyan -set-window-option -g window-status-current-attr bright,reverse -set-window-option -g window-status-current-bg cyan -set-window-option -g window-status-current-fg black - -# Use c-t instead of c-b as the prefix -unbind C-b -set -g prefix C-t -bind C-t send-prefix -bind t send-prefix - -# Bind function keys. -bind -n F1 select-window -t 1 -bind -n F2 select-window -t 2 -bind -n F3 select-window -t 3 -bind -n F4 select-window -t 4 -bind -n F5 select-window -t 5 -bind -n F6 select-window -t 6 -bind -n F7 select-window -t 7 -bind -n F8 select-window -t 8 - -# All new windows started at startup. -new emacs -neww irssi -neww mutt -neww -neww -neww -neww -neww - -select-window -t 1 diff --git a/examples/n-marriott.conf b/examples/n-marriott.conf deleted file mode 100644 index 6a047ec9..00000000 --- a/examples/n-marriott.conf +++ /dev/null @@ -1,110 +0,0 @@ -# $Id: n-marriott.conf,v 1.11 2009-11-24 19:03:59 nicm Exp $ -# -# By Nicholas Marriott. Public domain. - -# Default global options. -set -g status-bg green -set -g status-right "%H:%M" # %d-%b-%y -set -g bell-action none -set -g lock-after-time 1800 - -# Default global window options. -setw -g remain-on-exit on -setw -g window-status-current-attr "underscore" -#setw -g xterm-keys on - -# Prefix key. -set -g prefix C-a -unbind C-b -bind C-a send-prefix - -# Keys to switch session. -bind Q switchc -t0 -bind W switchc -t1 -bind E switchc -t2 - -# Other key bindings. -bind F1 selectw -t:10 -bind F2 selectw -t:11 -bind F3 selectw -t:12 -bind F4 selectw -t:13 -bind F5 selectw -t:14 -bind F6 selectw -t:15 -bind F7 selectw -t:16 -bind F8 selectw -t:17 -bind F9 selectw -t:18 -bind F10 selectw -t:19 -bind F11 selectw -t:20 -bind F12 selectw -t:21 - -bind m setw monitor-activity - -bind y setw force-width 81 -bind u setw force-width 0 - -bind -n F1 run-shell 'mpc toggle >/dev/null 2>&1' -bind -n F2 run-shell 'mpc' -bind -n F3 run-shell 'mpc prev >/dev/null 2>&1' -bind -n F4 run-shell 'mpc next >/dev/null 2>&1' -bind -n F5 run-shell 'mpc volume -5 >/dev/null 2>&1' -bind -n F6 run-shell 'mpc volume +5 >/dev/null 2>&1' - -# Hide and show window name from status line -bind '-' setw window-status-format '#I'\; setw window-status-current-format '#I' -bind '+' setw window-status-format '#I:#W#F'\; setw window-status-current-format '#I:#W#F' - -# First session. -new -d -s0 -nirssi 'exec ssh -t natalya exec sh ~/bin/tmux-start' -setw -t0:0 monitor-activity on -setw -t0:0 aggressive-resize on -set -t0 status-bg green -neww -d -ntodo 'exec emacs ~/TODO' -setw -t0:1 aggressive-resize on -neww -d -ntodo2 'exec emacs ~/TODO2' -setw -t0:2 aggressive-resize on -neww -d -nncmpc 'exec ncmpc -f ~/.ncmpc.conf' -setw -t0:3 aggressive-resize on -neww -d -nmutt 'exec mutt' -setw -t0:4 aggressive-resize on -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d - -# Second session. -new -d -s1 -set -t1 status-bg cyan -linkw -dk -t0 -s0:0 -linkw -dk -t1 -s0:1 -linkw -dk -t2 -s0:2 -linkw -dk -t3 -s0:3 -linkw -dk -t4 -s0:4 -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d - -# Third session. -new -d -s2 -set -t2 status-bg yellow -linkw -dk -t0 -s0:0 -linkw -dk -t1 -s0:1 -linkw -dk -t2 -s0:2 -linkw -dk -t3 -s0:3 -linkw -dk -t4 -s0:4 -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d diff --git a/examples/screen-keys.conf b/examples/screen-keys.conf deleted file mode 100644 index ce149290..00000000 --- a/examples/screen-keys.conf +++ /dev/null @@ -1,102 +0,0 @@ -# $Id: screen-keys.conf,v 1.7 2010-07-31 11:39:13 nicm Exp $ -# -# By Nicholas Marriott. Public domain. -# -# This configuration file binds many of the common GNU screen key bindings to -# appropriate tmux key bindings. Note that for some key bindings there is no -# tmux analogue and also that this set omits binding some commands available in -# tmux but not in screen. -# -# Note this is only a selection of key bindings and they are in addition to the -# normal tmux key bindings. This is intended as an example not as to be used -# as-is. - -# Set the prefix to ^A. -unbind C-b -set -g prefix ^A -bind a send-prefix - -# Bind appropriate commands similar to screen. -# lockscreen ^X x -unbind ^X -bind ^X lock-server -unbind x -bind x lock-server - -# screen ^C c -unbind ^C -bind ^C new-window -unbind c -bind c new-window - -# detach ^D d -unbind ^D -bind ^D detach - -# displays * -unbind * -bind * list-clients - -# next ^@ ^N sp n -unbind ^@ -bind ^@ next-window -unbind ^N -bind ^N next-window -unbind " " -bind " " next-window -unbind n -bind n next-window - -# title A -unbind A -bind A command-prompt "rename-window %%" - -# other ^A -unbind ^A -bind ^A last-window - -# prev ^H ^P p ^? -unbind ^H -bind ^H previous-window -unbind ^P -bind ^P previous-window -unbind p -bind p previous-window -unbind BSpace -bind BSpace previous-window - -# windows ^W w -unbind ^W -bind ^W list-windows -unbind w -bind w list-windows - -# quit \ -unbind '\' -bind '\' confirm-before "kill-server" - -# kill K k -unbind K -bind K confirm-before "kill-window" -unbind k -bind k confirm-before "kill-window" - -# redisplay ^L l -unbind ^L -bind ^L refresh-client -unbind l -bind l refresh-client - -# split -v | -unbind | -bind | split-window - -# :kB: focus up -unbind Tab -bind Tab select-pane -t:.+ -unbind BTab -bind BTab select-pane -t:.- - -# " windowlist -b -unbind '"' -bind '"' choose-window diff --git a/examples/t-williams.conf b/examples/t-williams.conf deleted file mode 100644 index 0a2cc3f5..00000000 --- a/examples/t-williams.conf +++ /dev/null @@ -1,104 +0,0 @@ -# $Id: t-williams.conf,v 1.1 2009-11-02 18:59:28 nicm Exp $ -# -# ~/.tmux.conf - tmux terminal multiplexer config -# Thayer Williams (http://cinderwick.ca) -# "Feel free to do whatever you like with it." - -# I typically start tmux from ~/.xinitrc with the following: -# -# urxvt -e bash -c "tmux attach -d -t mysession" & -# -# and recall it any time thereafter with xbindkeys (Mod4+s): -# -# "urxvt -e bash -c 'tmux attach -d -t mysession'" -# m:0x50 + c:39 - - -# set prefix key to ctrl+a until I have time to adapt -unbind C-b -set -g prefix C-a - -# send the prefix to client inside window (ala nested sessions) -bind-key a send-prefix - -# toggle last window like screen -bind-key C-a last-window - -# confirm before killing a window or the server -bind-key k confirm kill-window -bind-key K confirm kill-server - -# toggle statusbar -bind-key b set-option status - -# ctrl+left/right cycles thru windows -bind-key -n C-right next -bind-key -n C-left prev - -# open a man page in new window -bind / command-prompt "split-window 'exec man %%'" - -# quick view of processes -bind '~' split-window "exec htop" - -# scrollback buffer n lines -set -g history-limit 5000 - -# listen for activity on all windows -set -g bell-action any - -# on-screen time for display-panes in ms -set -g display-panes-time 2000 - -# start window indexing at one instead of zero -set -g base-index 1 - -# enable wm window titles -set -g set-titles on - -# wm window title string (uses statusbar variables) -set -g set-titles-string "tmux.#I.#W" - -# session initialization -new -s mysession mutt -neww -t 2 -neww -d -t 3 -neww -d -t 5 mocp -neww -d -t 6 rtorrent -selectw -t 1 - -# statusbar -------------------------------------------------------------- - -set -g display-time 2000 - -# default statusbar colors -set -g status-fg white -set -g status-bg default -set -g status-attr default - -# default window title colors -set-window-option -g window-status-fg cyan -set-window-option -g window-status-bg default -set-window-option -g window-status-attr dim - -# active window title colors -set-window-option -g window-status-current-fg white -set-window-option -g window-status-current-bg default -set-window-option -g window-status-current-attr bright - -# command/message line colors -set -g message-fg white -set -g message-bg black -set -g message-attr bright - -# center align the window list -set -g status-justify centre - -# show some useful stats but only when tmux is started -# outside of Xorg, otherwise dwm statusbar shows these already -set -g status-right "" -set -g status-left "" -if '[ -z "$DISPLAY" ]' 'set -g status-left "[#[fg=green] #H #[default]]"' -if '[ -z "$DISPLAY" ]' 'set -g status-right "[ #[fg=magenta]#(cat /proc/loadavg | cut -d \" \" -f 1,2,3)#[default] ][ #[fg=cyan,bright]%a %Y-%m-%d %H:%M #[default]]"' -if '[ -z "$DISPLAY" ]' 'set -g status-right-length 50' - diff --git a/examples/tmux.vim b/examples/tmux.vim deleted file mode 100644 index 6f85db5b..00000000 --- a/examples/tmux.vim +++ /dev/null @@ -1,289 +0,0 @@ -" Vim syntax file -" Language: tmux(1) configuration file -" Maintainer: Tiago Cunha -" Last Change: $Date: 2010-07-27 18:29:07 $ -" License: This file is placed in the public domain. -" -" To install this file: -" -" - Drop the file in the syntax directory into runtimepath (such as -" ~/.vim/syntax/tmux.vim). -" - Make the filetype recognisable by adding the following to filetype.vim -" (~/.vim/filetype.vim): -" -" augroup filetypedetect -" au BufNewFile,BufRead .tmux.conf*,tmux.conf* setf tmux -" augroup END -" -" - Switch on syntax highlighting by adding "syntax enable" to .vimrc. -" - -if version < 600 - syntax clear -elseif exists("b:current_syntax") - finish -endif - -setlocal iskeyword+=- -syntax case match - -syn keyword tmuxAction any current none -syn keyword tmuxBoolean off on - -syn keyword tmuxCmds - \ attach[-session] - \ bind[-key] - \ break-pane - \ breakp - \ capture-pane - \ capturep - \ choose-buffer - \ choose-client - \ choose-list - \ choose-session - \ choose-tree - \ choose-window - \ clear-history - \ clearhist - \ clock-mode - \ command-prompt - \ confirm[-before] - \ copy-mode - \ delete-buffer - \ deleteb - \ detach[-client] - \ display[-message] - \ display-panes - \ displayp - \ find-window - \ findw - \ has[-session] - \ if[-shell] - \ join-pane - \ joinp - \ kill-pane - \ killp - \ kill-server - \ kill-session - \ kill-window - \ killw - \ last-pane - \ lastp - \ last[-window] - \ link-window - \ linkw - \ list-buffers - \ lsb - \ list-clients - \ lsc - \ list-commands - \ lscm - \ list-keys - \ lsk - \ list-panes - \ lsp - \ list-sessions - \ ls - \ list-windows - \ lsw - \ load-buffer - \ loadb - \ lock-client - \ lockc - \ lock[-server] - \ lock-session - \ locks - \ move-pane - \ movep - \ move-window - \ movew - \ new[-session] - \ next-layout - \ nextl - \ next[-window] - \ paste-buffer - \ pasteb - \ pipe-pane - \ pipep - \ previous-layout - \ prevl - \ prev[ious-window] - \ refresh[-client] - \ rename[-session] - \ rename-window - \ renamew - \ resize-pane - \ resizep - \ respawn-pane - \ respawnp - \ respawn-window - \ respawnw - \ rotate-window - \ rotatew - \ run[-shell] - \ save-buffer - \ saveb - \ select-layout - \ selectl - \ select-pane - \ selectp - \ select-window - \ selectw - \ send[-keys] - \ send-prefix - \ server-info - \ info - \ set-buffer - \ setb - \ set-environment - \ setenv - \ set[-option] - \ set-window-option - \ setw - \ show-buffer - \ showb - \ show-environment - \ showenv - \ show-messages - \ showmsgs - \ show[-options] - \ show-window-options - \ showw - \ source[-file] - \ split-window - \ splitw - \ start[-server] - \ suspend-client - \ suspendc - \ swap-pane - \ swapp - \ swap-window - \ swapw - \ switch-client - \ switchc - \ unbind[-key] - \ unlink-window - \ unlinkw - \ wait[-for] - -syn keyword tmuxOptsSet - \ 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 - \ lock-server - \ message-command-style - \ message-limit - \ message-style - \ mouse - \ mouse-utf8 - \ pane-active-border-style - \ pane-border-style - \ prefix - \ prefix2 - \ quiet - \ renumber-windows - \ repeat-time - \ set-clipboard - \ 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-utf8 - \ staus-right-style - \ terminal-overrides - \ update-environment - \ visual-activity - \ visual-bell - \ visual-silence - \ word-separators - -syn keyword tmuxOptsSetw - \ aggressive-resize - \ allow-rename - \ alternate-screen - \ automatic-rename - \ 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-base-index - \ 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 - -syn match tmuxKey /\(C-\|M-\|\^\)\+\S\+/ display -syn match tmuxNumber /\d\+/ display -syn match tmuxOptions /\s-\a\+/ display -syn match tmuxVariable /\w\+=/ display -syn match tmuxVariableExpansion /\${\=\w\+}\=/ display - -" Comments can span multiple lines, when the newline is escaped -" (with a single) backslash at the end. -syn region tmuxComment start=/#/ skip=/\\\@ resize-pane -R 1 -bind - resize-pane -D 1 -bind + resize-pane -U 1 - -# bind : to command-prompt like vim -# this is the default in tmux already -bind : command-prompt - -# vi-style controls for copy mode -setw -g mode-keys vi diff --git a/examples/xterm-keys.vim b/examples/xterm-keys.vim deleted file mode 100644 index 5672c26a..00000000 --- a/examples/xterm-keys.vim +++ /dev/null @@ -1,49 +0,0 @@ -" tmux.vim - Set xterm input codes passed by tmux -" Author: Mark Oteiza -" License: Public domain -" Description: Simple plugin that assigns some xterm(1)-style keys to escape -" sequences passed by tmux when "xterm-keys" is set to "on". Inspired by an -" example given by Chris Johnsen at: -" https://stackoverflow.com/a/15471820 -" -" Documentation: help:xterm-modifier-keys man:tmux(1) - -if exists("g:loaded_tmux") || &cp - finish -endif -let g:loaded_tmux = 1 - -function! s:SetXtermCapabilities() - set ttymouse=sgr - - execute "set =\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 acfcb531..f771c85f 100644 --- a/format.c +++ b/format.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2011 Nicholas Marriott + * Copyright (c) 2011 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 @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Build a list of key-value pairs and use them to expand #{key} entries in a @@ -40,7 +42,7 @@ struct format_entry; typedef void (*format_cb)(struct format_tree *, struct format_entry *); void format_job_callback(struct job *); -const char *format_job_get(struct format_tree *, const char *); +char *format_job_get(struct format_tree *, const char *); void format_job_timer(int, short, void *); void format_cb_host(struct format_tree *, struct format_entry *); @@ -48,13 +50,17 @@ 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); @@ -92,10 +98,17 @@ 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; }; @@ -214,7 +227,7 @@ format_job_callback(struct job *job) } /* Find a job. */ -const char * +char * format_job_get(struct format_tree *ft, const char *cmd) { struct format_job fj0, *fj; @@ -232,7 +245,7 @@ format_job_get(struct format_tree *ft, const char *cmd) t = time(NULL); if (fj->job == NULL && ((ft->flags & FORMAT_FORCE) || fj->last != t)) { - fj->job = job_run(fj->cmd, NULL, -1, format_job_callback, + fj->job = job_run(fj->cmd, NULL, NULL, format_job_callback, NULL, fj); if (fj->job == NULL) { free(fj->out); @@ -244,12 +257,12 @@ format_job_get(struct format_tree *ft, const char *cmd) if (ft->flags & FORMAT_STATUS) fj->status = 1; - return (fj->out); + return (format_expand(ft, fj->out)); } /* Remove old jobs. */ void -format_job_timer(unused int fd, unused short events, unused void *arg) +format_job_timer(__unused int fd, __unused short events, __unused void *arg) { struct format_job *fj, *fj1; time_t now; @@ -278,7 +291,7 @@ format_job_timer(unused int fd, unused short events, unused void *arg) /* Callback for host. */ void -format_cb_host(unused struct format_tree *ft, struct format_entry *fe) +format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1]; @@ -290,7 +303,7 @@ format_cb_host(unused struct format_tree *ft, struct format_entry *fe) /* Callback for host_short. */ void -format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) +format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1], *cp; @@ -305,7 +318,7 @@ format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) /* Callback for pid. */ void -format_cb_pid(unused struct format_tree *ft, struct format_entry *fe) +format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) { xasprintf(&fe->value, "%ld", (long)getpid()); } @@ -355,6 +368,18 @@ format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) 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) @@ -423,6 +448,7 @@ format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) 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; @@ -457,14 +483,7 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) /* Create a new tree. */ struct format_tree * -format_create(void) -{ - return (format_create_flags(0)); -} - -/* Create a new tree for the status line. */ -struct format_tree * -format_create_flags(int flags) +format_create(struct cmd_q *cmdq, int flags) { struct format_tree *ft; @@ -480,6 +499,11 @@ format_create_flags(int 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); } @@ -520,12 +544,37 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) } fe->cb = NULL; + fe->t = 0; va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); } +/* 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) @@ -545,57 +594,101 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb) } 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[16]; + static char s[64]; + const char *found; + char *copy, *saved; - 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: - return (o->str); - case OPTIONS_NUMBER: - xsnprintf(s, sizeof s, "%lld", o->num); - return (s); - case OPTIONS_STYLE: - return (style_tostring(&o->style)); + 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_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); - return (fe->value); + found = fe->value; + goto found; } - 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) - return (envent->value); + 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); + 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); } /* @@ -606,10 +699,11 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr, *saved, *trimmed; - const char *value; - size_t valuelen; - u_long limit = 0; + char *copy, *copy0, *endptr, *ptr, *found, *new, *value; + char *from = NULL, *to = NULL; + size_t valuelen, newlen, fromlen, tolen, used; + long limit = 0; + int modifiers = 0, brackets; /* Make a copy of the key. */ copy0 = copy = xmalloc(keylen + 1); @@ -617,24 +711,57 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, copy[keylen] = '\0'; /* Is there a length limit or whatnot? */ - if (!isalpha((u_char) *copy) && *copy != '@' && *copy != '?') { - while (*copy != ':' && *copy != '\0') { - switch (*copy) { - case '=': - errno = 0; - limit = strtoul(copy + 1, &endptr, 10); - if (errno == ERANGE && limit == ULONG_MAX) - goto fail; - copy = endptr; - break; - default: - copy++; - break; - } + switch (copy[0]) { + case '=': + errno = 0; + limit = strtol(copy + 1, &endptr, 10); + if (errno == ERANGE && (limit == LONG_MIN || limit == LONG_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; } - if (*copy != ':') - goto fail; - copy++; + 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; } /* @@ -647,38 +774,75 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; *ptr = '\0'; - value = format_find(ft, copy + 1); - if (value != NULL && *value != '\0' && - (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; } - saved = format_expand(ft, value); - value = saved; + 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 = ""; - saved = NULL; + 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) { - value = trimmed = utf8_trimcstr(value, limit); - free(saved); - saved = trimmed; + if (limit > 0) { + new = utf8_trimcstr(value, limit); + free(value); + value = new; + } else if (limit < 0) { + new = utf8_rtrimcstr(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 = xreallocarray(*buf, 2, *len); *len *= 2; @@ -686,7 +850,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, memcpy(*buf + *off, value, valuelen); *off += valuelen; - free(saved); + free(value); free(copy0); return (0); @@ -726,14 +890,18 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *tmp, *cmd; + char *buf, *tmp, *cmd, *out; const char *ptr, *s, *saved = fmt; - size_t off, len, n, slen; + size_t off, len, n, outlen; int ch, brackets; if (fmt == NULL) return (xstrdup("")); +#ifdef TMATE + tmate_format(ft); +#endif + len = 64; buf = xmalloc(len); off = 0; @@ -768,18 +936,20 @@ format_expand(struct format_tree *ft, const char *fmt) tmp[n] = '\0'; cmd = format_expand(ft, tmp); - s = format_job_get(ft, cmd); - slen = strlen(s); + out = format_job_get(ft, cmd); + outlen = strlen(out); free(cmd); free(tmp); - while (len - off < slen + 1) { + while (len - off < outlen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; } - memcpy(buf + off, s, slen); - off += slen; + memcpy(buf + off, out, outlen); + off += outlen; + + free(out); fmt += n + 1; continue; @@ -835,18 +1005,6 @@ format_expand(struct format_tree *ft, const char *fmt) return (buf); } -/* Get time as a string. */ -char * -format_time_string(time_t t) -{ - char *tim; - - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - - return (tim); -} - /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, @@ -874,7 +1032,6 @@ void format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; - time_t t; ft->s = s; @@ -889,20 +1046,9 @@ format_defaults_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", "%lld", (long long) t); - format_add(ft, "session_created_string", "%s", format_time_string(t)); - - t = s->last_attached_time.tv_sec; - if (t != 0) { /* zero if never attached */ - format_add(ft, "session_last_attached", "%lld", (long long) t); - format_add(ft, "session_last_attached_string", "%s", - format_time_string(t)); - } - - t = s->activity_time.tv_sec; - format_add(ft, "session_activity", "%lld", (long long) t); - format_add(ft, "session_activity_string", "%s", format_time_string(t)); + 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); format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); @@ -915,7 +1061,7 @@ void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; - time_t t; + const char *name; if (ft->s == NULL) ft->s = c->session; @@ -930,15 +1076,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); - t = c->creation_time.tv_sec; - format_add(ft, "client_created", "%lld", (long long) t); - format_add(ft, "client_created_string", "%s", format_time_string(t)); + 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", "%lld", (long long) t); - format_add(ft, "client_activity_string", "%s", format_time_string(t)); - - if (strcmp(c->keytable->name, "root") == 0) + 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); @@ -966,19 +1108,16 @@ format_defaults_client(struct format_tree *ft, struct client *c) void format_defaults_window(struct format_tree *ft, struct window *w) { - time_t t; - ft->w = w; - t = w->activity_time.tv_sec; - format_add(ft, "window_activity", "%lld", (long long) t); - format_add(ft, "window_activity_string", "%s", format_time_string(t)); - + format_add_tv(ft, "window_activity", &w->activity_time); format_add(ft, "window_id", "@%u", w->id); 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_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)); format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); @@ -1022,7 +1161,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { struct grid *gd = wp->base.grid; u_int idx; - int status; + int status, scroll_position; if (ft->w == NULL) ft->w = wp->window; @@ -1057,7 +1196,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) 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")); + !!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); @@ -1070,6 +1209,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) 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", "%u", wp->saved_cx); format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); @@ -1091,16 +1234,13 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", !!(wp->base.mode & MODE_MOUSE_BUTTON)); - format_add(ft, "mouse_utf8_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_UTF8)); format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } /* Set default format keys for paste buffer. */ void -format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, - int utf8flag) +format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { size_t bufsize; char *s; @@ -1109,7 +1249,7 @@ format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, format_add(ft, "buffer_size", "%zu", bufsize); format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); - s = paste_make_sample(pb, utf8flag); + 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 badabd56..0989f800 100644 --- a/grid-view.c +++ b/grid-view.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -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); } diff --git a/grid.c b/grid.c index 99dafab2..0be0254f 100644 --- a/grid.c +++ b/grid.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -36,19 +36,17 @@ */ /* Default grid cell data. */ -const struct grid_cell grid_default_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, { .fg = 8 }, { .bg = 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); +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); @@ -58,6 +56,13 @@ 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) +{ + gd->linedata[py].celldata[px] = grid_default_entry; +} + /* Check grid y position. */ int grid_check_y(struct grid *gd, u_int py) @@ -99,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); @@ -111,7 +117,7 @@ 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 != gb->sy) @@ -122,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); } } @@ -228,7 +234,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx) 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; } @@ -242,37 +248,77 @@ 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) { + struct grid_line *gl; + struct grid_cell_entry *gce; + struct grid_cell *gcp; + int extended; + 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]; + + extended = (gce->flags & GRID_FLAG_EXTENDED); + if (!extended && (gc->data.size != 1 || gc->data.width != 1)) + extended = 1; + if (!extended && (gc->flags & (GRID_FLAG_FGRGB|GRID_FLAG_BGRGB))) + extended = 1; + if (extended) { + 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. */ @@ -304,7 +350,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); } } } @@ -327,6 +373,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); } } @@ -390,7 +437,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) 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); } } @@ -405,6 +452,12 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values) values[n++] = 38; values[n++] = 5; values[n++] = gc->fg; + } else if (gc->flags & GRID_FLAG_FGRGB) { + values[n++] = 38; + values[n++] = 2; + values[n++] = gc->fg_rgb.r; + values[n++] = gc->fg_rgb.g; + values[n++] = gc->fg_rgb.b; } else { switch (gc->fg) { case 0: @@ -446,6 +499,12 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values) values[n++] = 48; values[n++] = 5; values[n++] = gc->bg; + } else if (gc->flags & GRID_FLAG_BGRGB) { + values[n++] = 48; + values[n++] = 2; + values[n++] = gc->bg_rgb.r; + values[n++] = gc->bg_rgb.g; + values[n++] = gc->bg_rgb.b; } else { switch (gc->bg) { case 0: @@ -485,7 +544,7 @@ void grid_string_cells_code(const struct grid_cell *lastgc, const struct grid_cell *gc, char *buf, size_t len, int escape_c0) { - int oldc[16], newc[16], s[32]; + int oldc[64], newc[64], s[128]; size_t noldc, nnewc, n, i; u_int attr = gc->attr; u_int lastattr = lastgc->attr; @@ -572,9 +631,8 @@ 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; char *buf, code[128]; size_t len, off, size, codelen; @@ -594,21 +652,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, for (xx = px; xx < px + nx; xx++) { if (gl == NULL || xx >= gl->cellsize) break; - gc = &gl->celldata[xx]; - if (gc->flags & GRID_FLAG_PADDING) + 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; @@ -667,11 +724,44 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, } 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++; dy++; } } +/* 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, @@ -696,8 +786,7 @@ grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, 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) { @@ -736,8 +825,7 @@ grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, 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; @@ -767,6 +855,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; } /* @@ -796,7 +885,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 123044fe..9538e876 100644 --- a/input-keys.c +++ b/input-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -33,7 +33,7 @@ void input_key_mouse(struct window_pane *, struct mouse_event *); struct input_key_ent { - int key; + key_code key; const char *data; int flags; @@ -134,17 +134,32 @@ const struct input_key_ent input_keys[] = { { KEYC_KP_PERIOD, ".", 0 }, }; +/* Split a character into two UTF-8 bytes. */ +static size_t +input_split2(u_int c, u_char *dst) +{ + if (c > 0x7f) { + dst[0] = (c >> 6) | 0xc0; + dst[1] = (c & 0x3f) | 0x80; + return (2); + } + dst[0] = c; + return (1); +} + /* Translate a key code into an output key sequence. */ void -input_key(struct window_pane *wp, int key, struct mouse_event *m) +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_debug("writing key 0x%x (%s)", key, key_string_lookup_key(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)) { @@ -155,13 +170,22 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) /* * 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; } @@ -169,7 +193,7 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) * 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); @@ -194,11 +218,11 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) break; } if (i == nitems(input_keys)) { - log_debug("key 0x%x missing", key); + log_debug("key 0x%llx missing", key); return; } dlen = strlen(ike->data); - log_debug("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) @@ -238,10 +262,12 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) 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) { + if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) + return; 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]); + len += input_split2(m->b + 32, &buf[len]); + len += input_split2(x + 33, &buf[len]); + len += input_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; @@ -250,6 +276,6 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) buf[len++] = x + 33; buf[len++] = y + 33; } - log_debug("writing mouse %.*s", (int)len, buf); + 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 ab56fc38..18c8eb9a 100644 --- a/input.c +++ b/input.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -100,7 +100,7 @@ struct input_ctx { 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 printflike(2, 3) 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 *); @@ -446,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 } }; @@ -762,24 +762,12 @@ input_init(struct window_pane *wp) ictx = wp->ictx = xcalloc(1, sizeof *ictx); - input_reset_cell(ictx); - - *ictx->interm_buf = '\0'; - ictx->interm_len = 0; - - *ictx->param_buf = '\0'; - ictx->param_len = 0; - ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); - *ictx->input_buf = '\0'; - ictx->input_len = 0; - - ictx->state = &input_state_ground; - ictx->flags = 0; - ictx->since_ground = evbuffer_new(); + + input_reset(wp, 0); } /* Destroy input parser. */ @@ -797,18 +785,32 @@ input_free(struct window_pane *wp) /* Reset input state and clear screen. */ void -input_reset(struct window_pane *wp) +input_reset(struct window_pane *wp, int clear) { struct input_ctx *ictx = wp->ictx; input_reset_cell(ictx); - 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); + 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; + + *ictx->param_buf = '\0'; + ictx->param_len = 0; + + *ictx->input_buf = '\0'; + ictx->input_len = 0; + + ictx->state = &input_state_ground; + ictx->flags = 0; } /* Return pending data. */ @@ -1006,7 +1008,7 @@ input_print(struct input_ctx *ictx) else ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; - grid_cell_one(&ictx->cell.cell, ictx->ch); + utf8_set(&ictx->cell.cell.data, ictx->ch); screen_write_cell(&ictx->ctx, &ictx->cell.cell); ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; @@ -1627,18 +1629,20 @@ input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) c = input_get(ictx, *i, 0, -1); if (c == -1) { if (fgbg == 38) { - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = 8; } else if (fgbg == 48) { - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = 8; } } else { if (fgbg == 38) { gc->flags |= GRID_FLAG_FG256; + gc->flags &= ~GRID_FLAG_FGRGB; gc->fg = c; } else if (fgbg == 48) { gc->flags |= GRID_FLAG_BG256; + gc->flags &= ~GRID_FLAG_BGRGB; gc->bg = c; } } @@ -1649,7 +1653,7 @@ 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; + int r, g, b; (*i)++; r = input_get(ictx, *i, 0, -1); @@ -1664,13 +1668,18 @@ input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) if (b == -1 || b > 255) return; - c = colour_find_rgb(r, g, b); if (fgbg == 38) { - gc->flags |= GRID_FLAG_FG256; - gc->fg = c; + gc->flags &= ~GRID_FLAG_FG256; + gc->flags |= GRID_FLAG_FGRGB; + gc->fg_rgb.r = r; + gc->fg_rgb.g = g; + gc->fg_rgb.b = b; } else if (fgbg == 48) { - gc->flags |= GRID_FLAG_BG256; - gc->bg = c; + gc->flags &= ~GRID_FLAG_BG256; + gc->flags |= GRID_FLAG_BGRGB; + gc->bg_rgb.r = r; + gc->bg_rgb.g = g; + gc->bg_rgb.b = b; } } @@ -1752,11 +1761,11 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 35: case 36: case 37: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = n - 30; break; case 39: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = 8; break; case 40: @@ -1767,11 +1776,11 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 45: case 46: case 47: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = n - 40; break; case 49: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = 8; break; case 90: @@ -1782,7 +1791,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 95: case 96: case 97: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = n; break; case 100: @@ -1793,7 +1802,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 105: case 106: case 107: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = n - 10; break; } @@ -1907,12 +1916,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); } @@ -1921,14 +1930,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); } @@ -1936,9 +1944,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); } @@ -1946,11 +1958,21 @@ 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) { + /* + * An error here could be invalid UTF-8 or it could be a + * nonprintable character for which we can't get the + * width. Drop it. + */ + return (0); + } - grid_cell_set(&ictx->cell.cell, &ictx->utf8data); + 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 dc11c4c8..957897f3 100644 --- a/job.c +++ b/job.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -40,37 +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, int cwd, +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); - if (cwd != -1 && fchdir(cwd) != 0) - chdir("/"); + if (cwd == NULL || chdir(cwd) != 0) { + if ((home = find_home()) == NULL || chdir(home) != 0) + chdir("/"); + } - environ_push(&env); - environ_free(&env); + environ_push(env); + environ_free(env); if (dup2(out[1], STDIN_FILENO) == -1) fatal("dup2 failed"); @@ -95,7 +98,7 @@ job_run(const char *cmd, struct session *s, int cwd, } /* parent */ - environ_free(&env); + environ_free(env); close(out[1]); job = xmalloc(sizeof *job); @@ -146,7 +149,7 @@ 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)); @@ -162,7 +165,8 @@ 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; diff --git a/key-bindings.c b/key-bindings.c index 3dc3a054..0d13385d 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -37,7 +37,11 @@ key_table_cmp(struct key_table *e1, struct key_table *e2) int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { - return (bd1->key - bd2->key); + if (bd1->key < bd2->key) + return (-1); + if (bd1->key > bd2->key) + return (1); + return (0); } struct key_table * @@ -64,12 +68,12 @@ void key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; + struct key_binding *bd1; if (--table->references != 0) return; - while (!RB_EMPTY(&table->key_bindings)) { - bd = RB_ROOT(&table->key_bindings); + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); @@ -80,7 +84,7 @@ key_bindings_unref_table(struct key_table *table) } void -key_bindings_add(const char *name, int key, int can_repeat, +key_bindings_add(const char *name, key_code key, int can_repeat, struct cmd_list *cmdlist) { struct key_table *table; @@ -105,7 +109,7 @@ key_bindings_add(const char *name, int key, int can_repeat, } void -key_bindings_remove(const char *name, int key) +key_bindings_remove(const char *name, key_code key) { struct key_table *table; struct key_binding bd_find, *bd; @@ -223,8 +227,11 @@ key_bindings_init(void) "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 select-pane -mt=", + "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_list *cmdlist; diff --git a/key-string.c b/key-string.c index b6474c4f..119035a0 100644 --- a/key-string.c +++ b/key-string.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -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 }, @@ -93,12 +93,15 @@ const struct { KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), + KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1), + KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2), + KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), 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; @@ -107,14 +110,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] == '-') { @@ -138,18 +141,27 @@ 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; + wchar_t wc; + + /* 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); } @@ -161,18 +173,31 @@ 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); + if (utf8_combine(&ud, &wc) != UTF8_DONE) + return (KEYC_UNKNOWN); + return (wc | 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. */ @@ -186,7 +211,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; } @@ -195,19 +220,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 (""); + return ("None"); + + /* Handle special keys. */ + if (key == KEYC_UNKNOWN) + return ("Unknown"); if (key == KEYC_MOUSE) - return (""); + return ("Mouse"); /* * Special case: display C-@ as C-Space. Could do this below in @@ -237,21 +267,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 57503518..99c6c3ce 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 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 diff --git a/layout-set.c b/layout-set.c index da94cff2..bd1304ce 100644 --- a/layout-set.c +++ b/layout-set.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -245,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 @@ -366,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 266d1f39..31dce95c 100644 --- a/layout.c +++ b/layout.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -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; @@ -521,8 +521,8 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) /* 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; @@ -562,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; @@ -605,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; diff --git a/log.c b/log.c index 536af173..45aaea46 100644 --- a/log.c +++ b/log.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -22,41 +22,78 @@ #include #include #include +#include #include "tmux.h" -FILE *log_file; +static FILE *log_file; +static int log_level; -void log_event_cb(int, const char *); -void log_vwrite(const char *, va_list); +static void log_event_cb(int, const char *); +static void log_vwrite(const char *, va_list); + +static int is_log_stdout(void) +{ + return fileno(log_file) <= 2; +} /* 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_debug("%s", msg); } -/* Open logging to file. */ +/* Increment log level. */ void -log_open(const char *path) +log_add_level(void) { - if (log_file != NULL) + log_level++; +} + +/* Get log level. */ +int +log_get_level(void) +{ + return (log_level); +} + +void +log_open_fp(FILE *f) +{ + if (log_file == f) + return; + + if (log_file != NULL && !is_log_stdout()) fclose(log_file); - log_file = fopen(path, "w"); - if (log_file == NULL) - return; + log_file = f; setvbuf(log_file, NULL, _IOLBF, 0); event_set_log_callback(log_event_cb); } +/* Open logging to file. */ +void +log_open(const char *name) +{ + char *path; + + if (log_level == 0) + return; + + xasprintf(&path, "tmate-%s-%ld.log", name, (long)getpid()); + FILE *f = fopen(path, "w"); + free(path); + if (f) + log_open_fp(f); +} + /* Close logging. */ void log_close(void) { - if (log_file != NULL) + if (log_file != NULL && !is_log_stdout()) fclose(log_file); log_file = NULL; @@ -64,7 +101,8 @@ log_close(void) } /* Write a log message. */ -void +__attribute__((__format__(__printf__, 1, 0))) +static void log_vwrite(const char *msg, va_list ap) { char *fmt, *out; @@ -79,9 +117,16 @@ log_vwrite(const char *msg, va_list ap) 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); + + if (is_log_stdout()) { + if (fprintf(log_file, "%s\n", out) == -1) + exit(1); + } else { + 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); @@ -90,18 +135,22 @@ log_vwrite(const char *msg, va_list ap) /* Log a debug message. */ void -log_debug(const char *msg, ...) +log_emit(int level, const char *msg, ...) { va_list ap; + if (log_level < level) + return; + va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); } /* Log a critical error with error string and die. */ +__attribute__((__format__(__printf__, 1, 0))) __dead void -log_fatal(const char *msg, ...) +fatal(const char *msg, ...) { char *fmt; va_list ap; @@ -109,13 +158,15 @@ log_fatal(const char *msg, ...) va_start(ap, msg); if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) exit(1); - log_vwrite(fmt, ap); + msg = fmt; + log_vwrite(msg, ap); exit(1); } /* Log a critical error and die. */ +__attribute__((__format__(__printf__, 1, 0))) __dead void -log_fatalx(const char *msg, ...) +fatalx(const char *msg, ...) { char *fmt; va_list ap; @@ -123,6 +174,7 @@ log_fatalx(const char *msg, ...) va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); - log_vwrite(fmt, ap); + msg = fmt; + log_vwrite(msg, ap); exit(1); } diff --git a/mode-key.c b/mode-key.c index 5ed45bd8..aed161bb 100644 --- a/mode-key.c +++ b/mode-key.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -40,7 +40,7 @@ /* Entry in the default mode key tables. */ struct mode_key_entry { - int key; + key_code key; /* * Editing mode for vi: 0 is edit mode, keys not in the table are @@ -347,6 +347,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, + { KEYC_MOUSEDRAGEND1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; @@ -495,6 +496,7 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, + { KEYC_MOUSEDRAGEND1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; @@ -523,9 +525,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 * @@ -588,7 +596,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; diff --git a/names.c b/names.c index e880c577..a03f6f5b 100644 --- a/names.c +++ b/names.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -29,7 +29,7 @@ void name_time_callback(int, short, void *); int name_time_expired(struct window *, struct timeval *); void -name_time_callback(unused int fd, unused short events, void *arg) +name_time_callback(__unused int fd, __unused short events, void *arg) { struct window *w = arg; @@ -58,7 +58,7 @@ check_window_name(struct window *w) if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) + if (!options_get_number(w->options, "automatic-rename")) return; if (~w->active->flags & PANE_CHANGED) { @@ -118,11 +118,11 @@ format_window_name(struct window *w) struct format_tree *ft; char *fmt, *name; - ft = format_create(); + 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"); + fmt = options_get_string(w->options, "automatic-rename-format"); name = format_expand(ft, fmt); format_free(ft); diff --git a/options-table.c b/options-table.c index e901e249..e3362ba8 100644 --- a/options-table.c +++ b/options-table.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2011 Nicholas Marriott + * Copyright (c) 2011 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 @@ -52,9 +52,10 @@ const char *options_table_bell_action_list[] = { }; /* 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 @@ -62,11 +63,17 @@ const struct options_table_entry server_options_table[] = { { .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 @@ -74,50 +81,57 @@ 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, - .default_str = NULL + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" }, { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, +#ifdef TMATE + .default_num = 500 +#else .default_num = 100 +#endif }, { .name = "quiet", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "set-clipboard", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 1 }, { .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" }, - { .name = NULL } -}; - -/* 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, @@ -125,6 +139,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 @@ -132,47 +147,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, + .scope = OPTIONS_TABLE_SESSION, .default_str = "" }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = _PATH_BSHELL }, { .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 @@ -180,20 +204,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 @@ -201,82 +234,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 = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 3, .style = "message-style" }, { .name = "message-command-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-fg", .type = OPTIONS_TABLE_COLOUR, + .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, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black" }, { .name = "mouse", .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "mouse-utf8", - .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .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 @@ -284,44 +326,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, + .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, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-style" }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 2, .style = "status-style" }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, + .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 @@ -329,41 +379,48 @@ 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, + .scope = OPTIONS_TABLE_SESSION, .default_str = "[#S] " }, { .name = "status-left-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-left-style" }, { .name = "status-left-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-left-style" }, { .name = "status-left-fg", .type = OPTIONS_TABLE_COLOUR, + .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 @@ -371,40 +428,47 @@ const struct options_table_entry session_options_table[] = { { .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, + .scope = OPTIONS_TABLE_SESSION, .default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-right-style" }, { .name = "status-right-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-right-style" }, { .name = "status-right-fg", .type = OPTIONS_TABLE_COLOUR, + .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 @@ -412,21 +476,19 @@ const struct options_table_entry session_options_table[] = { { .name = "status-right-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=green,fg=black" }, - { .name = "status-utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ - }, - { .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" @@ -434,68 +496,75 @@ 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, + .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 = NULL } -}; - -/* Window options. */ -const struct options_table_entry window_options_table[] = { { .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 @@ -503,6 +572,7 @@ const struct options_table_entry window_options_table[] = { { .name = "force-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -510,6 +580,7 @@ const struct options_table_entry window_options_table[] = { { .name = "main-pane-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 24 @@ -517,6 +588,7 @@ const struct options_table_entry window_options_table[] = { { .name = "main-pane-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 80 @@ -524,40 +596,47 @@ const struct options_table_entry window_options_table[] = { { .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 @@ -565,6 +644,7 @@ const struct options_table_entry window_options_table[] = { { .name = "other-pane-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -572,6 +652,7 @@ const struct options_table_entry window_options_table[] = { { .name = "other-pane-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -579,23 +660,27 @@ const struct options_table_entry window_options_table[] = { { .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 @@ -603,197 +688,331 @@ const struct options_table_entry window_options_table[] = { { .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 = "utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ - }, - { .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-identity", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-server-host", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "ssh.tmate.io" + }, + + { .name = "tmate-server-port", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, + .minimum = 1, + .maximum = 65535, + .default_num = 22 + }, + + { .name = "tmate-server-dsa-fingerprint", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "obsolete" + }, + + { .name = "tmate-server-rsa-fingerprint", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "SHA256:Hthk2T/M/Ivqfk1YYUn5ijC2Att3+UPzD7Rn72P5VWs" + }, + + { .name = "tmate-server-ecdsa-fingerprint", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "SHA256:8GmKHYHEJ6n0TEdciHeEGkKOigQfCFuBULdt6vZIhDc" + }, + + { .name = "tmate-server-ed25519-fingerprint", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "SHA256:jfttvoypkHiQYUqUCwKeqd9d1fJj/ZiQlFOHVl6E9sI" + }, + + { .name = "tmate-display-time", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 15000 + }, + + { .name = "tmate-webhook-userdata", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-webhook-url", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-api-key", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-session-name", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-session-name-ro", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-authorized-keys", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-set", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-foreground-restart", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, + .minimum = 0, + .maximum = 1, + .default_num = 1 + }, +#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++) { + 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); @@ -859,33 +1078,22 @@ options_table_print_entry(const struct options_table_entry *oe, /* 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 030cfd51..df79ac4b 100644 --- a/options.c +++ b/options.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -29,34 +29,64 @@ * 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,14 +119,8 @@ 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 * @@ -126,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); } @@ -156,9 +180,9 @@ 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); } @@ -196,8 +220,8 @@ options_get_style(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_STYLE) - fatalx("option not a style"); + fatalx("option %s not a style", name); return (&o->style); } diff --git a/osdep-aix.c b/osdep-aix.c index 0a3d12e4..e1ce4918 100644 --- a/osdep-aix.c +++ b/osdep-aix.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2011 Nicholas Marriott + * Copyright (c) 2011 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 @@ -26,7 +26,7 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, char *tty) +osdep_get_name(__unused int fd, char *tty) { struct psinfo p; char *path; diff --git a/osdep-cygwin.c b/osdep-cygwin.c index 91bc5fc9..60630b33 100644 --- a/osdep-cygwin.c +++ b/osdep-cygwin.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -27,7 +27,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; diff --git a/osdep-darwin.c b/osdep-darwin.c index 7be70375..53b48138 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -28,10 +28,12 @@ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); -#define unused __attribute__ ((unused)) +#ifndef __unused +#define __unused __attribute__ ((__unused__)) +#endif char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { struct proc_bsdinfo bsdinfo; pid_t pgrp; diff --git a/osdep-dragonfly.c b/osdep-dragonfly.c index f9b0efcf..879034e8 100644 --- a/osdep-dragonfly.c +++ b/osdep-dragonfly.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 diff --git a/osdep-freebsd.c b/osdep-freebsd.c index d7f419b8..067ba565 100644 --- a/osdep-freebsd.c +++ b/osdep-freebsd.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 diff --git a/osdep-hpux.c b/osdep-hpux.c index 4baa6d49..16993b93 100644 --- a/osdep-hpux.c +++ b/osdep-hpux.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -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 ad4c11cd..42712dea 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -28,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; diff --git a/osdep-netbsd.c b/osdep-netbsd.c index 15686860..d8aa41b5 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 diff --git a/osdep-openbsd.c b/osdep-openbsd.c index 414228b7..1de876a4 100644 --- a/osdep-openbsd.c +++ b/osdep-openbsd.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 diff --git a/osdep-unknown.c b/osdep-unknown.c index 4de1ee1e..bc59f569 100644 --- a/osdep-unknown.c +++ b/osdep-unknown.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -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 157aff51..e4eae877 100644 --- a/paste.c +++ b/paste.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -150,7 +150,7 @@ paste_add(char *data, size_t size) if (size == 0) return; - limit = options_get_number(&global_options, "buffer-limit"); + 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; @@ -274,7 +274,7 @@ paste_set(char *data, size_t size, const char *name, char **cause) /* Convert start of buffer into a nice string. */ char * -paste_make_sample(struct paste_buffer *pb, int utf8flag) +paste_make_sample(struct paste_buffer *pb) { char *buf; size_t len, used; @@ -286,10 +286,7 @@ paste_make_sample(struct paste_buffer *pb, int utf8flag) len = width; buf = xreallocarray(NULL, len, 4 + 4); - if (utf8flag) - used = utf8_strvis(buf, pb->data, len, flags); - else - used = strvisx(buf, pb->data, len, flags); + used = utf8_strvis(buf, pb->data, len, flags); if (pb->size > width || used > width) strlcpy(buf + width, "...", 4); return (buf); diff --git a/proc.c b/proc.c new file mode 100644 index 00000000..edd4a6f8 --- /dev/null +++ b/proc.c @@ -0,0 +1,274 @@ +/* $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 && !tmate_foreground) { + 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"); + } + + if (tmate_foreground) { + if (forkflag) + clear_signals(0); + log_open_fp(stdout); + } else { + 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 3606bfeb..04b95c1f 100644 --- a/resize.c +++ b/resize.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -21,6 +21,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Recalculate window and session sizes. @@ -51,9 +52,13 @@ recalculate_sizes(void) struct window_pane *wp; u_int ssx, ssy, has, limit; int flag, has_status, is_zoomed, forced; +#ifdef TMATE + int tmate_sx = tmate_session.min_sx; + int tmate_sy = tmate_session.min_sy; +#endif 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; @@ -61,6 +66,10 @@ recalculate_sizes(void) if (c->flags & CLIENT_SUSPENDED) continue; if (c->session == s) { +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) + has_status = 1; +#endif if (c->tty.sx < ssx) ssx = c->tty.sx; if (has_status && @@ -72,6 +81,17 @@ recalculate_sizes(void) s->attached++; } } + +#ifdef TMATE + /* We assume a single session */ + if (tmate_sx > 0 && tmate_sy > 0) { + if ((u_int)tmate_sx < ssx) + ssx = tmate_sx; + if ((u_int)tmate_sy < ssy) + ssy = tmate_sy; + } +#endif + if (ssx == UINT_MAX || ssy == UINT_MAX) { s->flags |= SESSION_UNATTACHED; continue; @@ -94,7 +114,7 @@ recalculate_sizes(void) 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) { @@ -115,12 +135,12 @@ recalculate_sizes(void) continue; forced = 0; - limit = options_get_number(&w->options, "force-width"); + limit = options_get_number(w->options, "force-width"); if (limit >= PANE_MINIMUM && ssx > limit) { ssx = limit; forced |= WINDOW_FORCEWIDTH; } - limit = options_get_number(&w->options, "force-height"); + limit = options_get_number(w->options, "force-height"); if (limit >= PANE_MINIMUM && ssy > limit) { ssy = limit; forced |= WINDOW_FORCEHEIGHT; diff --git a/screen-redraw.c b/screen-redraw.c index 799f5c55..4201b022 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -222,7 +222,7 @@ void screen_redraw_screen(struct client *c, int draw_panes, int draw_status, int draw_borders) { - struct options *oo = &c->session->options; + struct options *oo = c->session->options; struct tty *tty = &c->tty; u_int top; int status, spos; @@ -276,9 +276,9 @@ 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 options *oo = w->options; struct tty *tty = &c->tty; - struct window_pane *wp; + struct window_pane *wp = NULL; 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; @@ -331,9 +331,9 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) continue; active = screen_redraw_check_is(i, j, type, w, w->active, wp); - if (server_is_marked(s, s->curw, marked_window_pane) && + if (server_is_marked(s, s->curw, marked_pane.wp) && screen_redraw_check_is(i, j, type, w, - marked_window_pane, wp)) { + marked_pane.wp, wp)) { if (active) tty_attributes(tty, &m_active_gc, NULL); else @@ -392,7 +392,7 @@ 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; diff --git a/screen-write.c b/screen-write.c index f80048b6..e58d744c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -42,7 +42,7 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, /* Finish writing. */ void -screen_write_stop(unused struct screen_write_ctx *ctx) +screen_write_stop(__unused struct screen_write_ctx *ctx) { } @@ -67,13 +67,13 @@ void 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 -screen_write_cstrlen(int utf8flag, const char *fmt, ...) +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); @@ -108,13 +108,14 @@ screen_write_cstrlen(int utf8flag, const char *fmt, ...) /* Calculate string length. */ size_t -screen_write_strlen(int utf8flag, const char *fmt, ...) +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++; } } @@ -151,64 +154,67 @@ screen_write_puts(struct screen_write_ctx *ctx, struct grid_cell *gc, 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 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, int utf8flag, const char *fmt, ...) + 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); } @@ -221,15 +227,16 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, /* Write string, similar to nputs, but with embedded formatting (#[]). */ void -screen_write_cnputs(struct screen_write_ctx *ctx, - ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) +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); @@ -253,34 +260,38 @@ screen_write_cnputs(struct screen_write_ctx *ctx, 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++; } } @@ -296,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; @@ -321,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); @@ -339,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; @@ -359,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. */ @@ -504,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++) @@ -761,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; @@ -868,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); } @@ -901,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 @@ -925,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); } @@ -961,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); @@ -987,11 +991,12 @@ 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 = gc->flags; + tmp_gc.flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_BGRGB); + tmp_gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmp_gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); ttyctx.cell = &tmp_gc; @@ -1008,8 +1013,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) @@ -1020,17 +1024,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); } @@ -1049,11 +1054,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 @@ -1061,8 +1066,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); } @@ -1077,8 +1082,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 f487cc67..e002b96e 100644 --- a/screen.c +++ b/screen.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -94,10 +94,10 @@ screen_set_cursor_style(struct screen *s, u_int style) /* Set screen cursor colour. */ void -screen_set_cursor_colour(struct screen *s, const char *colour_string) +screen_set_cursor_colour(struct screen *s, const char *colour) { free(s->ccolour); - s->ccolour = xstrdup(colour_string); + s->ccolour = xstrdup(colour); } /* Set screen title. */ @@ -194,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) diff --git a/server-client.c b/server-client.c index 524bddb9..21cccc85 100644 --- a/server-client.c +++ b/server-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -28,23 +29,23 @@ #include #include "tmux.h" +#include "tmate.h" -void server_client_key_table(struct client *, const char *); -void server_client_free(int, short, void *); -void server_client_check_focus(struct window_pane *); -void server_client_check_resize(struct window_pane *); -int 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 *); +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 imsg *); -void server_client_msg_identify(struct client *, struct imsg *); -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 @@ -56,7 +57,7 @@ server_client_check_nested(struct client *c) if (c->tty.path == NULL) return (0); - envent = environ_find(&c->environ, "TMUX"); + envent = environ_find(c->environ, "TMUX"); if (envent == NULL || *envent->value == '\0') return (0); @@ -69,13 +70,32 @@ server_client_check_nested(struct client *c) /* Set client key table. */ void -server_client_key_table(struct client *c, const char *name) +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) @@ -86,17 +106,16 @@ server_client_create(int fd) c = xcalloc(1, sizeof *c); c->references = 1; - imsg_init(&c->ibuf, fd); - server_update_event(c); + 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); - environ_init(&c->environ); + c->environ = environ_create(); c->fd = -1; - c->cwd = -1; + c->cwd = NULL; c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; @@ -130,7 +149,7 @@ server_client_create(int fd) evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); TAILQ_INSERT_TAIL(&clients, c, entry); - log_debug("new client %d", fd); + log_debug("new client %p", c); } /* Open client terminal if needed. */ @@ -171,7 +190,7 @@ server_client_lost(struct client *c) c->stdin_callback(c, 1, c->stdin_callback_data); TAILQ_REMOVE(&clients, c, entry); - log_debug("lost client %d", c->ibuf.fd); + log_debug("lost client %p", c); /* * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called @@ -192,7 +211,7 @@ server_client_lost(struct client *c) screen_free(&c->status); free(c->title); - close(c->cwd); + free((void *)c->cwd); evtimer_del(&c->repeat_timer); @@ -217,12 +236,10 @@ server_client_lost(struct client *c) 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; server_client_unref(c); @@ -237,7 +254,7 @@ server_client_lost(struct client *c) void server_client_unref(struct client *c) { - log_debug("unref client %d (%d references)", c->ibuf.fd, c->references); + log_debug("unref client %p (%d references)", c, c->references); c->references--; if (c->references == 0) @@ -246,52 +263,31 @@ server_client_unref(struct client *c) /* Free dead client. */ void -server_client_free(unused int fd, unused short events, void *arg) +server_client_free(__unused int fd, __unused short events, void *arg) { struct client *c = arg; - log_debug("free client %d (%d references)", c->ibuf.fd, c->references); + log_debug("free client %p (%d references)", c, c->references); if (c->references == 0) free(c); } -/* Process a single client event. */ +/* Detach a client. */ void -server_client_callback(int fd, short events, void *data) +server_client_detach(struct client *c, enum msgtype msgtype) { - struct client *c = data; + struct session *s = c->session; - if (c->flags & CLIENT_DEAD) + if (s == NULL) return; - if (fd == c->ibuf.fd) { - if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) <= 0 && - errno != EAGAIN) - 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); + hooks_run(c->session->hooks, c, NULL, "client-detached"); + proc_send_s(c->peer, msgtype, s->name); } /* Check for mouse keys. */ -int +key_code server_client_check_mouse(struct client *c) { struct session *s = c->session; @@ -301,7 +297,7 @@ server_client_check_mouse(struct client *c) enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE; enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; u_int x, y, b; - int key; + key_code key; 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); @@ -330,7 +326,7 @@ server_client_check_mouse(struct client *c) log_debug("down at %u,%u", x, y); } if (type == NOTYPE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Always save the session. */ m->s = s->id; @@ -340,7 +336,7 @@ server_client_check_mouse(struct client *c) if (m->statusat != -1 && y == (u_int)m->statusat) { w = status_get_window_at(c, x); if (w == NULL) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->w = w->id; where = STATUS; } else @@ -366,11 +362,14 @@ server_client_check_mouse(struct client *c) where = BORDER; else { wp = window_get_active_at(s->curw->window, x, y); - if (wp != NULL) + if (wp != NULL) { where = PANE; + log_debug("mouse at %u,%u is on pane %%%u", + x, y, wp->id); + } } if (where == NOWHERE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->wp = wp->id; m->w = wp->window->id; } else @@ -384,12 +383,46 @@ server_client_check_mouse(struct client *c) c->tty.mouse_drag_update = NULL; c->tty.mouse_drag_release = NULL; + /* + * End a mouse drag by passing a MouseDragEnd key corresponding + * to the button that started the drag. + */ + switch (c->tty.mouse_drag_flag) { + case 1: + if (where == PANE) + key = KEYC_MOUSEDRAGEND1_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND1_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEDRAGEND2_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND2_BORDER; + break; + case 3: + if (where == PANE) + key = KEYC_MOUSEDRAGEND3_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAGEND3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAGEND3_BORDER; + break; + default: + key = KEYC_MOUSE; + break; + } c->tty.mouse_drag_flag = 0; - return (KEYC_MOUSE); /* not a key, but still may want to pass */ + + return (key); } /* Convert to a key binding. */ - key = KEYC_NONE; + key = KEYC_UNKNOWN; switch (type) { case NOTYPE: break; @@ -425,7 +458,11 @@ server_client_check_mouse(struct client *c) } } - c->tty.mouse_drag_flag = 1; + /* + * Begin a drag by setting the flag to a non-zero value that + * corresponds to the mouse button in use. + */ + c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1; break; case WHEEL: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { @@ -501,8 +538,8 @@ server_client_check_mouse(struct client *c) } break; } - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) @@ -522,25 +559,33 @@ 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 mouse_event *m = &c->tty.mouse; struct session *s = c->session; struct window *w; struct window_pane *wp; struct timeval tv; - struct key_table *table = c->keytable; + struct key_table *table; struct key_binding bd_find, *bd; int xtimeout; @@ -548,7 +593,6 @@ server_client_handle_key(struct client *c, int key) if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; w = s->curw->window; - wp = w->active; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) @@ -569,6 +613,9 @@ server_client_handle_key(struct client *c, int key) /* Handle status line. */ if (!(c->flags & CLIENT_READONLY)) { +#ifdef TMATE + if (!(c->flags & CLIENT_FORCE_STATUS)) +#endif status_message_clear(c); server_clear_identify(c); } @@ -583,30 +630,25 @@ server_client_handle_key(struct client *c, int key) if (c->flags & CLIENT_READONLY) return; key = server_client_check_mouse(c); - if (key == KEYC_NONE) + if (key == KEYC_UNKNOWN) return; m->valid = 1; m->key = key; - if (!options_get_number(&s->options, "mouse")) { - window_pane_key(wp, c, s, key, m); - return; - } + if (!options_get_number(s->options, "mouse")) + goto forward; } else m->valid = 0; /* Treat everything as a regular key when pasting is detected. */ - if (server_client_assume_paste(s)) { - if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); - return; - } + 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, &table->key_bindings, &bd_find); + 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 @@ -614,7 +656,7 @@ retry: * again in the root table. */ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; @@ -624,13 +666,14 @@ 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"); + xtimeout = options_get_number(s->options, "repeat-time"); if (xtimeout != 0 && bd->can_repeat) { c->flags |= CLIENT_REPEAT; @@ -640,7 +683,7 @@ retry: evtimer_add(&c->repeat_timer, &tv); } else { c->flags &= ~CLIENT_REPEAT; - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); } server_status_client(c); @@ -655,15 +698,15 @@ retry: * root table and try again. */ if (c->flags & CLIENT_REPEAT) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; 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, "root") != 0) { - server_client_key_table(c, "root"); + if (strcmp(c->keytable->name, server_client_get_key_table(c)) != 0) { + server_client_set_key_table(c, NULL); server_status_client(c); return; } @@ -672,11 +715,21 @@ retry: * No match, but in the root table. Prefix switches to the prefix table * and everything else is passed through. */ - if (key == options_get_number(&s->options, "prefix") || - key == options_get_number(&s->options, "prefix2")) { - server_client_key_table(c, "prefix"); + 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); - } else if (!(c->flags & CLIENT_READONLY)) + return; + } + +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); } @@ -687,6 +740,9 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; +#ifdef TMATE + int tmate_should_sync_layout = 0; +#endif TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); @@ -701,6 +757,13 @@ server_client_loop(void) * their flags now. Also check pane focus and resize. */ RB_FOREACH(w, windows, &windows) { +#ifdef TMATE + if (w->flags & WINDOW_REDRAW) + tmate_should_sync_layout = 1; + if (w->tmate_last_sync_active_pane != w->active) + tmate_should_sync_layout = 1; +#endif + w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { @@ -711,6 +774,11 @@ server_client_loop(void) } check_window_name(w); } + +#ifdef TMATE + if (tmate_should_sync_layout) + tmate_sync_layout(); +#endif } /* Check if pane should be resized. */ @@ -750,7 +818,7 @@ server_client_check_focus(struct window_pane *wp) int push; /* Are focus events off? */ - if (!options_get_number(&global_options, "focus-events")) + if (!options_get_number(global_options, "focus-events")) return; /* Do we need to push the focus state? */ @@ -810,7 +878,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 *oo = c->session->options; int status, mode, o; if (c->flags & CLIENT_SUSPENDED) @@ -822,6 +890,10 @@ server_client_reset_state(struct client *c) tty_region(&c->tty, 0, c->tty.sy - 1); status = options_get_number(oo, "status"); +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) + status = 1; +#endif if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) tty_cursor(&c->tty, 0, 0); else { @@ -837,19 +909,6 @@ server_client_reset_state(struct client *c) if (options_get_number(oo, "mouse")) mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; - /* - * 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; - /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode, s); tty_reset(&c->tty); @@ -857,12 +916,12 @@ 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) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); } @@ -882,7 +941,7 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; - server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval); + proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); c->flags &= ~CLIENT_EXIT; } @@ -899,7 +958,7 @@ server_client_check_redraw(struct client *c) return; 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) @@ -959,9 +1018,9 @@ server_client_set_title(struct client *c) char *title; struct format_tree *ft; - template = options_get_string(&s->options, "set-titles-string"); + template = options_get_string(s->options, "set-titles-string"); - ft = format_create(); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); title = format_expand_time(ft, template, time(NULL)); @@ -976,123 +1035,111 @@ server_client_set_title(struct client *c) } /* 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 client *c = arg; struct msg_stdin_data stdindata; const char *data; - ssize_t n, datalen; + 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); + if (imsg == NULL) { + server_client_lost(c); + return; + } - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - if (imsg.hdr.peerid != PROTOCOL_VERSION) { - server_write_client(c, MSG_VERSION, NULL, 0); - c->flags |= CLIENT_BAD; - if (imsg.fd != -1) - close(imsg.fd); - imsg_free(&imsg); - continue; + 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 %u from client %d", imsg.hdr.type, c->ibuf.fd); - 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_msg_identify(c, &imsg); + if (c->flags & CLIENT_CONTROL) break; - case MSG_COMMAND: - server_client_msg_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"); - - 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 (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); + if (tty_resize(&c->tty)) { recalculate_sizes(); - break; - case MSG_SHELL: - if (datalen != 0) - fatalx("bad MSG_SHELL size"); - - server_client_msg_shell(c); - break; + 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 imsg *imsg) +server_client_dispatch_command(struct client *c, struct imsg *imsg) { struct msg_command_data data; char *buf; @@ -1145,9 +1192,9 @@ error: /* Handle identify message. */ void -server_client_msg_identify(struct client *c, struct imsg *imsg) +server_client_dispatch_identify(struct client *c, struct imsg *imsg) { - const char *data; + const char *data, *home; size_t datalen; int flags; @@ -1163,37 +1210,49 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) 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) - fatalx("bad MSG_IDENTIFY_CWD size"); - c->cwd = imsg->fd; + 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); + 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; @@ -1205,7 +1264,6 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) #ifdef __CYGWIN__ c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); - c->cwd = open(".", O_RDONLY); #endif if (c->flags & CLIENT_CONTROL) { @@ -1216,10 +1274,9 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) 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(c->fd); c->fd = -1; @@ -1247,14 +1304,101 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) /* Handle shell message. */ void -server_client_msg_shell(struct client *c) +server_client_dispatch_shell(struct client *c) { const char *shell; - shell = options_get_string(&global_s_options, "default-shell"); + shell = options_get_string(global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1); + proc_send_s(c->peer, MSG_SHELL, shell); - 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 bc2b7002..78bc2bea 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -31,57 +32,19 @@ void server_callback_identify(int, short, void *); void server_fill_environ(struct session *s, struct environ *env) { - char var[PATH_MAX], *term; - u_int idx; - long pid; + char *term; + u_int idx; + long pid; if (s != NULL) { - term = options_get_string(&global_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 = (u_int)-1; pid = getpid(); - xsnprintf(var, sizeof var, "%s,%ld,%u", 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; - - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == s) - server_write_client(c, type, buf, len); - } + environ_set(env, "TMUX", "%s,%ld,%u", socket_path, pid, idx); } void @@ -217,7 +180,7 @@ server_lock_client(struct client *c) if (c->flags & CLIENT_SUSPENDED) return; - cmd = options_get_string(&c->session->options, "lock-command"); + cmd = options_get_string(c->session->options, "lock-command"); if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) return; @@ -227,7 +190,7 @@ 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, cmd, strlen(cmd) + 1); + proc_send_s(c->peer, MSG_LOCK, cmd); } void @@ -253,7 +216,7 @@ server_kill_window(struct window *w) server_redraw_session_group(s); } - if (options_get_number(&s->options, "renumber-windows")) { + 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); @@ -306,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); @@ -328,12 +291,13 @@ 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) { @@ -345,7 +309,7 @@ server_destroy_pane(struct window_pane *wp) 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); @@ -357,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; } @@ -364,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 @@ -408,7 +378,7 @@ server_destroy_session(struct session *s) struct client *c; struct session *s_new; - 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; @@ -422,11 +392,13 @@ 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, NULL); gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); + alerts_check_session(s_new); } } recalculate_sizes(); @@ -444,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); } } @@ -455,7 +427,7 @@ 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; @@ -480,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, @@ -573,7 +485,7 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, if (c->stdin_closed) 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); } diff --git a/server.c b/server.c index 0d5f40f9..05706769 100644 --- a/server.c +++ b/server.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -35,29 +35,27 @@ #include #include "tmux.h" +#include "tmate.h" /* * Main server functions. */ -struct clients clients; +struct clients clients; -int server_fd; -int server_shutdown; -struct event server_ev_accept; +struct tmuxproc *server_proc; +int server_fd; +int server_exit; +struct event server_ev_accept; -struct session *marked_session; -struct winlink *marked_winlink; -struct window *marked_window; -struct window_pane *marked_window_pane; -struct layout_cell *marked_layout_cell; +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_accept_callback(int, short, void *); -void server_signal_callback(int, short, 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); @@ -66,22 +64,18 @@ void server_child_stopped(pid_t, int); void server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { - marked_session = s; - marked_winlink = wl; - marked_window = wl->window; - marked_window_pane = wp; - marked_layout_cell = wp->layout_cell; + 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) { - marked_session = NULL; - marked_winlink = NULL; - marked_window = NULL; - marked_window_pane = NULL; - marked_layout_cell = NULL; + cmd_find_clear_state(&marked_pane, NULL, 0); } /* Is this the marked pane? */ @@ -90,9 +84,9 @@ server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { if (s == NULL || wl == NULL || wp == NULL) return (0); - if (marked_session != s || marked_winlink != wl) + if (marked_pane.s != s || marked_pane.wl != wl) return (0); - if (marked_window_pane != wp) + if (marked_pane.wp != wp) return (0); return (server_check_marked()); } @@ -101,25 +95,7 @@ server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) int server_check_marked(void) { - struct winlink *wl; - - if (marked_window_pane == NULL) - return (0); - if (marked_layout_cell != marked_window_pane->layout_cell) - return (0); - - if (!session_alive(marked_session)) - return (0); - RB_FOREACH(wl, winlinks, &marked_session->windows) { - if (wl->window == marked_window && wl == marked_winlink) - break; - } - if (wl == NULL) - return (0); - - if (!window_has_pane(marked_window, marked_window_pane)) - return (0); - return (window_pane_visible(marked_window_pane)); + return (cmd_find_valid_state(&marked_pane)); } /* Create server socket. */ @@ -155,42 +131,47 @@ server_create_socket(void) return (fd); } +#ifdef TMATE +static void tmate_set_editor_mode(void) +{ + switch (options_get_number(global_s_options, "status-keys")) { + case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "emacs"}); break; + case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "vi"}); break; + } + + switch (options_get_number(global_w_options, "mode-keys")) { + case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "emacs"}); break; + case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "vi"}); break; + } +} +#endif + /* Fork new server. */ int server_start(struct event_base *base, int lockfd, char *lockfile) { 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"); - log_debug("starting server"); + if (!tmate_foreground) + 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. */ - clear_signals(0); - if (event_reinit(base) != 0) - fatal("event_reinit failed"); - - logfile("server"); - log_debug("server started, pid %ld", (long) getpid()); +#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); @@ -198,57 +179,58 @@ server_start(struct event_base *base, int lockfd, char *lockfile) RB_INIT(&sessions); TAILQ_INIT(&session_groups); 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 TMATE + tmate_session_init(base); #endif + 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]); + if (!tmate_foreground) + server_client_create(pair[1]); - unlink(lockfile); - free(lockfile); - close(lockfd); + if (lockfd >= 0) { + unlink(lockfile); + free(lockfile); + close(lockfd); + } +#ifdef TMATE + tmate_set_editor_mode(); +#endif start_cfg(); status_prompt_load_history(); server_add_accept(0); - set_signals(server_signal_callback); - server_loop(); + if (tmate_foreground) + run_initial_client_cmd(); + + proc_loop(server_proc, server_loop); status_prompt_save_history(); +#ifdef TMATE + unlink(socket_path); +#endif + exit(0); } -/* Main server loop. */ -void -server_loop(void) -{ - while (!server_should_shutdown()) { - log_debug("event dispatch enter"); - event_loop(EVLOOP_ONCE); - log_debug("event dispatch exit"); - - server_client_loop(); - } -} - -/* Check if the server should exit (no more clients or sessions). */ +/* Server loop callback. */ int -server_should_shutdown(void) +server_loop(void) { struct client *c; - if (!options_get_number(&global_options, "exit-unattached")) { + server_client_loop(); + + if (!options_get_number(global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } @@ -269,9 +251,9 @@ server_should_shutdown(void) 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, *c1; struct session *s, *s1; @@ -279,10 +261,10 @@ server_send_shutdown(void) cmd_wait_for_flush(); TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { - if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) + if (c->flags & CLIENT_SUSPENDED) server_client_lost(c); else - server_write_client(c, MSG_SHUTDOWN, NULL, 0); + proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); c->session = NULL; } @@ -328,7 +310,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; @@ -349,7 +331,7 @@ server_accept_callback(int fd, short events, unused void *data) } fatal("accept failed"); } - if (server_shutdown) { + if (server_exit) { close(newfd); return; } @@ -369,26 +351,27 @@ 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 SIGINT: case SIGTERM: - server_shutdown = 1; - server_send_shutdown(); + server_exit = 1; + server_send_exit(); break; case SIGCHLD: server_child_signal(); @@ -441,7 +424,7 @@ server_child_exited(pid_t pid, int status) TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { wp->status = status; - server_destroy_pane(wp); + server_destroy_pane(wp, 1); break; } } diff --git a/session.c b/session.c index 80089e0d..879eb657 100644 --- a/session.c +++ b/session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -25,6 +25,7 @@ #include #include "tmux.h" +#include "tmate.h" struct sessions sessions; u_int next_session_id; @@ -103,26 +104,35 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * session_create(const char *name, int argc, char **argv, const char *path, - int cwd, struct environ *env, struct termios *tio, int idx, u_int sx, - u_int sy, char **cause) + 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) { + xasprintf(cause, "multi sessions is not supported with tmate"); + return NULL; + } +#endif + s = xcalloc(1, sizeof *s); s->references = 1; s->flags = 0; - s->cwd = dup(cwd); + 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) { @@ -130,10 +140,6 @@ session_create(const char *name, int argc, char **argv, const char *path, memcpy(s->tio, tio, sizeof *s->tio); } - if (gettimeofday(&s->creation_time, NULL) != 0) - fatal("gettimeofday failed"); - session_update_activity(s, &s->creation_time); - s->sx = sx; s->sy = sy; @@ -150,6 +156,8 @@ session_create(const char *name, int argc, char **argv, const char *path, } RB_INSERT(sessions, &sessions, s); + 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); @@ -182,18 +190,53 @@ session_unref(struct session *s) /* Free session. */ void -session_free(unused int fd, unused short events, void *arg) +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); } } +static void maybe_restart_session(void) +{ + int fg_restart = options_get_number(global_options, "tmate-foreground-restart"); + if (!fg_restart) + return; + + /* + * throttle restarts. This is a blocking sleep. It's + * simpler than using a timer, but fairly harmless + * from a blocking perspective. + */ + usleep(500*1000); + next_session_id = 0; + run_initial_client_cmd(); + + tmate_info("Session shell restarted"); + + struct session *s; + s = RB_MIN(sessions, &sessions); + if (!s) + return; + + struct window_pane *wp; + wp = s->curw->window->active; + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_for_output(wp); + window_copy_add(wp, "%s", "Session shell restarted"); + window_copy_add(wp, "%s", "Note: press the following sequence to disconnect from SSH: ~."); +} + /* Destroy a session. */ void session_destroy(struct session *s) @@ -211,8 +254,6 @@ session_destroy(struct session *s) 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)); @@ -222,9 +263,18 @@ session_destroy(struct session *s) winlink_remove(&s->windows, wl); } - close(s->cwd); + free((void *)s->cwd); session_unref(s); + +#ifdef TMATE + if (tmate_foreground && !server_exit) { + maybe_restart_session(); + } else { + tmate_info("Session closed"); + tmate_write_fin(); + } +#endif } /* Check a session name is valid: not empty and no colons or periods. */ @@ -236,7 +286,7 @@ session_check_name(const char *name) /* Lock session if it has timed out. */ void -session_lock_timer(unused int fd, unused short events, void *arg) +session_lock_timer(__unused int fd, __unused short events, void *arg) { struct session *s = arg; @@ -263,6 +313,10 @@ session_update_activity(struct session *s, struct timeval *from) 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 @@ -270,7 +324,7 @@ session_update_activity(struct session *s, struct timeval *from) if (~s->flags & SESSION_UNATTACHED) { timerclear(&tv); - tv.tv_sec = options_get_number(&s->options, "lock-after-time"); + tv.tv_sec = options_get_number(s->options, "lock-after-time"); if (tv.tv_sec != 0) evtimer_add(&s->lock_timer, &tv); } @@ -313,11 +367,11 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * session_new(struct session *s, const char *name, int argc, char **argv, - const char *path, int cwd, int idx, char **cause) + 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; @@ -326,29 +380,29 @@ session_new(struct session *s, const char *name, int argc, char **argv, 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, argc, argv, path, shell, cwd, &env, s->tio, + 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); @@ -367,6 +421,10 @@ session_attach(struct session *s, struct window *w, int idx, char **cause) winlink_set_window(wl, w); notify_window_linked(s, w); +#ifdef TMATE + tmate_sync_layout(); +#endif + session_group_synchronize_from(s); return (wl); } @@ -383,6 +441,11 @@ session_detach(struct session *s, struct winlink *wl) notify_window_unlinked(s, wl->window); winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); + +#ifdef TMATE + tmate_sync_layout(); +#endif + session_group_synchronize_from(s); if (RB_EMPTY(&s->windows)) { session_destroy(s); @@ -518,6 +581,11 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_push(&s->lastw, s->curw); s->curw = wl; winlink_clear_flags(wl); + +#ifdef TMATE + tmate_sync_layout(); +#endif + window_update_activity(wl->window); return (0); } @@ -553,6 +621,7 @@ session_group_index(struct session_group *sg) } fatalx("session group not found"); + for(;;); } /* @@ -711,7 +780,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. */ diff --git a/signal.c b/signal.c index 7e6268a7..f20a0257 100644 --- a/signal.c +++ b/signal.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * Copyright (c) 2010 Romain Francoise * * Permission to use, copy, modify, and distribute this software for any @@ -24,6 +24,7 @@ #include "tmux.h" +struct event ev_sigint; struct event ev_sighup; struct event ev_sigchld; struct event ev_sigcont; @@ -32,7 +33,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; @@ -40,26 +41,34 @@ set_signals(void(*handler)(int, short, unused void *)) sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_IGN; +#ifndef TMATE if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); +#ifndef TMATE if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif - signal_set(&ev_sighup, SIGHUP, handler, NULL); +#ifdef TMATE + signal_set(&ev_sigint, SIGINT, handler, arg); + signal_add(&ev_sigint, NULL); +#endif + 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); } @@ -72,16 +81,24 @@ clear_signals(int after_fork) sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_DFL; +#ifndef TMATE if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); +#ifndef TMATE if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif if (after_fork) { +#ifdef TMATE + if (sigaction(SIGINT, &sigact, NULL) != 0) + fatal("sigaction failed"); +#endif if (sigaction(SIGHUP, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGCHLD, &sigact, NULL) != 0) @@ -95,6 +112,7 @@ clear_signals(int after_fork) if (sigaction(SIGWINCH, &sigact, NULL) != 0) fatal("sigaction failed"); } else { + event_del(&ev_sigint); event_del(&ev_sighup); event_del(&ev_sigchld); event_del(&ev_sigcont); diff --git a/status.c b/status.c index 7a1d2818..3a553945 100644 --- a/status.c +++ b/status.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -28,11 +28,12 @@ #include #include "tmux.h" +#include "tmate.h" -char *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *, +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_redraw_get_right(struct client *, time_t, int, - 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); @@ -61,7 +62,7 @@ status_prompt_find_history_file(void) const char *home, *history_file; char *path; - history_file = options_get_string(&global_options, "history-file"); + history_file = options_get_string(global_options, "history-file"); if (*history_file == '\0') return (NULL); if (*history_file == '/') @@ -145,7 +146,7 @@ status_prompt_save_history(void) /* Status timer callback. */ void -status_timer_callback(unused int fd, unused short events, void *arg) +status_timer_callback(__unused int fd, __unused short events, void *arg) { struct client *c = arg; struct session *s = c->session; @@ -160,11 +161,11 @@ status_timer_callback(unused int fd, unused short events, void *arg) c->flags |= CLIENT_STATUS; timerclear(&tv); - tv.tv_sec = options_get_number(&s->options, "status-interval"); + tv.tv_sec = options_get_number(s->options, "status-interval"); if (tv.tv_sec != 0) evtimer_add(&c->status_timer, &tv); - log_debug("client %d, status interval %d", c->ibuf.fd, (int)tv.tv_sec); + log_debug("client %p, status interval %d", c, (int)tv.tv_sec); } /* Start status timer for client. */ @@ -178,7 +179,7 @@ status_timer_start(struct client *c) else evtimer_set(&c->status_timer, status_timer_callback, c); - if (s != NULL && options_get_number(&s->options, "status")) + if (s != NULL && options_get_number(s->options, "status")) status_timer_callback(-1, 0, c); } @@ -198,31 +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; size_t leftlen; - style_apply_update(gc, &s->options, "status-left-style"); + style_apply_update(gc, s->options, "status-left-style"); - template = options_get_string(&s->options, "status-left"); + 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); @@ -230,21 +231,21 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag, /* 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; size_t rightlen; - style_apply_update(gc, &s->options, "status-right-style"); + style_apply_update(gc, s->options, "status-right-style"); - template = options_get_string(&s->options, "status-right"); + 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); @@ -261,7 +262,7 @@ status_get_window_at(struct client *c, u_int x) x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { - oo = &wl->window->options; + oo = wl->window->options; len = strlen(options_get_string(oo, "window-status-separator")); if (x < wl->status_width) @@ -286,10 +287,13 @@ 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 return (1); left = right = NULL; larrow = rarrow = 0; @@ -298,7 +302,7 @@ status_redraw(struct client *c) t = time(NULL); /* Set up default colour. */ - style_apply(&stdgc, &s->options, "status-style"); + style_apply(&stdgc, s->options, "status-style"); /* Create the target screen. */ memcpy(&old_status, &c->status, sizeof old_status); @@ -312,14 +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 @@ -340,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; } @@ -358,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); @@ -435,7 +439,7 @@ 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_cnputs(&ctx, llen, &lgc, "%s", left); if (larrow != 0) { memcpy(&gc, &stdgc, sizeof gc); if (larrow == -1) @@ -453,7 +457,7 @@ draw: } else screen_write_cursormove(&ctx, c->tty.sx - rlen, 0); if (rlen != 0) - screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right); + screen_write_cnputs(&ctx, rlen, &rgc, "%s", right); /* Figure out the offset for the window list. */ if (llen != 0) @@ -461,7 +465,7 @@ draw: else wloffset = 0; if (wlwidth < wlavailable) { - switch (options_get_number(&s->options, "status-justify")) { + switch (options_get_number(s->options, "status-justify")) { case 1: /* centred */ wloffset += (wlavailable - wlwidth) / 2; break; @@ -504,9 +508,9 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) return (xstrdup("")); if (c->flags & CLIENT_STATUSFORCE) - ft = format_create_flags(FORMAT_STATUS|FORMAT_FORCE); + ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE); else - ft = format_create_flags(FORMAT_STATUS); + ft = format_create(NULL, FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); @@ -520,7 +524,7 @@ char * 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; @@ -551,9 +555,9 @@ status_message_set(struct client *c, const char *fmt, ...) struct message_entry *msg, *msg1; va_list ap; int delay; - u_int first, limit; + u_int limit; - limit = options_get_number(&global_options, "message-limit"); + limit = options_get_number(global_options, "message-limit"); status_prompt_clear(c); status_message_clear(c); @@ -568,23 +572,24 @@ status_message_set(struct client *c, const char *fmt, ...) msg->msg = xstrdup(c->message_string); TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - first = c->message_next - limit; TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - if (msg->msg_num >= first) - continue; + if (msg->msg_num + limit >= c->message_next) + break; free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); } - delay = options_get_number(&c->session->options, "display-time"); - tv.tv_sec = delay / 1000; - tv.tv_usec = (delay % 1000) * 1000L; + 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); + 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; @@ -601,6 +606,12 @@ status_message_clear(struct client *c) c->message_string = NULL; c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) { + c->flags &= ~CLIENT_FORCE_STATUS; + recalculate_sizes(); + } +#endif c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ screen_reinit(&c->status); @@ -608,7 +619,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; @@ -624,25 +635,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; - style_apply(&gc, &s->options, "message-style"); + 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, ' '); @@ -666,7 +674,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, int keys; time_t t; - ft = format_create(); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); @@ -686,7 +694,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 @@ -727,7 +735,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) struct format_tree *ft; time_t t; - ft = format_create(); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); @@ -753,43 +761,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; /* Change colours for command mode. */ if (c->prompt_mdata.mode == 1) - style_apply(&gc, &s->options, "message-command-style"); + style_apply(&gc, s->options, "message-command-style"); else - style_apply(&gc, &s->options, "message-style"); + 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, ' '); @@ -799,8 +804,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); @@ -812,10 +818,10 @@ 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, *bufdata, *wsep = NULL; @@ -1116,7 +1122,7 @@ 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, size + 2); @@ -1131,8 +1137,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); } @@ -1214,19 +1220,7 @@ status_prompt_complete_list(u_int *size, const char *s) list[(*size)++] = (*cmdent)->name; } } - for (oe = server_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 (oe = session_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 (oe = window_options_table; oe->name != NULL; oe++) { + 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; diff --git a/style.c b/style.c index c00b0fee..151c2912 100644 --- a/style.c +++ b/style.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * Copyright (c) 2014 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any diff --git a/tmate-debug.c b/tmate-debug.c new file mode 100644 index 00000000..81850284 --- /dev/null +++ b/tmate-debug.c @@ -0,0 +1,112 @@ +#ifdef HAVE_EXECINFO_H +#include +#endif +#include +#include +#include +#include +#include "tmate.h" + +#ifndef HAVE_BACKTRACE + +void tmate_print_stack_trace(void) {} +void tmate_catch_sigsegv(void) {} +void tmate_preload_trace_lib(void) {} + +#else + +#if DEBUG + +static int print_resolved_stack_frame(const char *frame) +{ + char file[100]; + char cmd[200]; + char output[300]; + char address[20]; + char *line; + FILE *ps; + + static regex_t _regex; + static regex_t *regex; + regmatch_t matches[3]; + + if (!regex) { + if (regcomp(&_regex, "(.+)\\(\\) \\[([^]]+)\\]", REG_EXTENDED)) + return -1; + regex = &_regex; + } + + if (regexec(regex, frame, 3, matches, 0)) + return -1; + + memcpy(file, &frame[matches[1].rm_so], matches[1].rm_eo - matches[1].rm_so); + file[matches[1].rm_eo - matches[1].rm_so] = 0; + + memcpy(address, &frame[matches[2].rm_so], matches[2].rm_eo - matches[2].rm_so); + address[matches[2].rm_eo - matches[2].rm_so] = 0; + + sprintf(cmd, "addr2line -e %s %s -f -p -s", file, address); + + ps = popen(cmd, "r"); + if (!ps) + return -1; + + line = fgets(output, sizeof(output), ps); + pclose(ps); + + if (!line) + return -1; + + line[strlen(line)-1] = 0; /* remove \n */ + tmate_debug("%s(%s) [%s]", file, line, address); + return 0; +} +#endif + +void tmate_print_stack_trace(void) +{ + void *array[20]; + size_t size; + char **strings; + size_t i; + + size = backtrace (array, 20); + strings = backtrace_symbols (array, size); + + tmate_info ("============ %zd stack frames ============", size); + + for (i = 1; i < size; i++) { +#if DEBUG + if (print_resolved_stack_frame(strings[i]) < 0) +#endif + tmate_info("%s", strings[i]); + } + + free (strings); +} + +static void handle_crash(int sig) +{ + /* TODO send stack trace to server */ + const char *what = sig == SIGSEGV ? "SIGSEGV" : "SIGABRT"; + tmate_info("%s printing stack trace", what); + tmate_print_stack_trace(); + + /* Reraise */ + signal(sig, NULL); + kill(getpid(), sig); +} + +void tmate_catch_sigsegv(void) +{ + signal(SIGSEGV, handle_crash); + signal(SIGABRT, handle_crash); +} + +void tmate_preload_trace_lib(void) +{ + void *array[1]; + backtrace(array, 1); +} + +#endif diff --git a/tmate-decoder.c b/tmate-decoder.c new file mode 100644 index 00000000..3b1e31af --- /dev/null +++ b/tmate-decoder.c @@ -0,0 +1,211 @@ +#include "tmate.h" +#include "tmate-protocol.h" + +static void handle_notify(__unused struct tmate_session *session, + struct tmate_unpacker *uk) +{ + char *msg = unpack_string(uk); + tmate_status_message("%s", msg); + free(msg); +} + +static void handle_legacy_pane_key(__unused struct tmate_session *_session, + struct tmate_unpacker *uk) +{ + struct session *s; + struct window *w; + struct window_pane *wp; + + int key = unpack_int(uk); + + s = RB_MIN(sessions, &sessions); + if (!s) + return; + + w = s->curw->window; + if (!w) + return; + + wp = w->active; + if (!wp) + return; + + window_pane_key(wp, NULL, s, key, NULL); +} + +static struct window_pane *find_window_pane(struct session *s, int pane_id) +{ + struct window *w; + + if (pane_id != -1) + return window_pane_find_by_id(pane_id); + + w = s->curw->window; + if (!w) + return NULL; + + return w->active; +} + +static void handle_pane_key(__unused struct tmate_session *_session, + struct tmate_unpacker *uk) +{ + struct session *s; + struct window_pane *wp; + + int pane_id = unpack_int(uk); + key_code key = unpack_int(uk); + + s = RB_MIN(sessions, &sessions); + if (!s) + return; + + wp = find_window_pane(s, pane_id); + if (!wp) + return; + + window_pane_key(wp, NULL, s, key, NULL); +} + +static void handle_resize(struct tmate_session *session, + struct tmate_unpacker *uk) +{ + session->min_sx = unpack_int(uk); + session->min_sy = unpack_int(uk); + recalculate_sizes(); +} + +extern char **cfg_causes; +extern u_int cfg_ncauses; + +static void handle_exec_cmd_str(__unused struct tmate_session *session, + 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); + + if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) { + tmate_failed_cmd(client_id, cause); + free(cause); + goto out; + } + + cmd_q = cmdq_new(NULL); + cmdq_run(cmd_q, cmdlist, NULL); + cmd_list_free(cmdlist); + cmdq_free(cmd_q); + + /* 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); +} + +static void handle_exec_cmd(__unused struct tmate_session *session, + struct tmate_unpacker *uk) +{ + struct cmd_q *cmd_q; + struct cmd_list *cmdlist; + struct cmd *cmd; + char *cause; + u_int i; + unsigned int argc; + char **argv; + + int client_id = unpack_int(uk); + + argc = uk->argc; + argv = xmalloc(sizeof(char *) * argc); + for (i = 0; i < argc; i++) + argv[i] = unpack_string(uk); + + cmd = cmd_parse(argc, argv, NULL, 0, &cause); + if (!cmd) { + tmate_failed_cmd(client_id, cause); + free(cause); + goto out; + } + + cmdlist = xcalloc(1, sizeof *cmdlist); + cmdlist->references = 1; + TAILQ_INIT(&cmdlist->list); + TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + + cmd_q = cmdq_new(NULL); + cmdq_run(cmd_q, cmdlist, NULL); + cmd_list_free(cmdlist); + cmdq_free(cmd_q); + + /* 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: + cmd_free_argv(argc, argv); +} + +static void maybe_save_reconnection_data(struct tmate_session *session, + const char *name, const char *value) +{ + if (!strcmp(name, "tmate_reconnection_data")) { + free(session->reconnection_data); + session->reconnection_data = xstrdup(value); + } +} + +static void handle_set_env(struct tmate_session *session, + struct tmate_unpacker *uk) +{ + char *name = unpack_string(uk); + char *value = unpack_string(uk); + + tmate_set_env(name, value); + maybe_save_reconnection_data(session, name, value); + + free(name); + free(value); +} + +static void handle_ready(struct tmate_session *session, + __unused struct tmate_unpacker *uk) +{ + session->tmate_env_ready = 1; + signal_waiting_clients("tmate-ready"); +} + +void tmate_dispatch_slave_message(struct tmate_session *session, + struct tmate_unpacker *uk) +{ + int cmd = unpack_int(uk); + switch (cmd) { +#define dispatch(c, f) case c: f(session, uk); break + dispatch(TMATE_IN_NOTIFY, handle_notify); + dispatch(TMATE_IN_LEGACY_PANE_KEY, handle_legacy_pane_key); + dispatch(TMATE_IN_RESIZE, handle_resize); + dispatch(TMATE_IN_EXEC_CMD_STR, handle_exec_cmd_str); + dispatch(TMATE_IN_SET_ENV, handle_set_env); + dispatch(TMATE_IN_READY, handle_ready); + dispatch(TMATE_IN_PANE_KEY, handle_pane_key); + dispatch(TMATE_IN_EXEC_CMD, handle_exec_cmd); + default: tmate_info("Bad message type: %d", cmd); + } +} diff --git a/tmate-encoder.c b/tmate-encoder.c new file mode 100644 index 00000000..e1373451 --- /dev/null +++ b/tmate-encoder.c @@ -0,0 +1,490 @@ +#include +#include "tmate.h" +#include "tmate-protocol.h" +#include "window-copy.h" + +#define pack(what, ...) _pack(&tmate_session.encoder, what, ##__VA_ARGS__) + +void tmate_write_header(void) +{ + pack(array, 3); + pack(int, TMATE_OUT_HEADER); + pack(int, TMATE_PROTOCOL_VERSION); + pack(string, VERSION); +} + +void tmate_write_uname(void) +{ + struct utsname name; + if (uname(&name) < 0) { + tmate_debug("uname() failed"); + return; + } + + pack(array, 6); + pack(int, TMATE_OUT_UNAME); + pack(string, name.sysname); + pack(string, name.nodename); + pack(string, name.release); + pack(string, name.version); + pack(string, name.machine); +} + +void tmate_write_ready(void) +{ + pack(array, 1); + pack(int, TMATE_OUT_READY); +} + +void tmate_sync_layout(void) +{ + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + int num_panes = 0; + int num_windows = 0; + int active_pane_id; + int active_window_idx = -1; + + /* + * TODO this can get a little heavy. + * We are shipping the full layout whenever a window name changes, + * that is, at every shell command. + * Might be better to do something incremental. + */ + + /* + * We only allow one session, it makes our lives easier. + * Especially when the HTML5 client will come along. + * We make no distinction between a winlink and its window except + * that we send the winlink idx to draw the status bar properly. + */ + + s = RB_MIN(sessions, &sessions); + if (!s) + return; + + num_windows = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window) + num_windows++; + } + + if (!num_windows) + return; + + pack(array, 5); + pack(int, TMATE_OUT_SYNC_LAYOUT); + + pack(int, s->sx); + pack(int, s->sy); + + pack(array, num_windows); + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + if (!w) + continue; + + w->tmate_last_sync_active_pane = NULL; + active_pane_id = -1; + + if (active_window_idx == -1) + active_window_idx = wl->idx; + + pack(array, 4); + pack(int, wl->idx); + pack(string, w->name); + + num_panes = 0; + TAILQ_FOREACH(wp, &w->panes, entry) + num_panes++; + + pack(array, num_panes); + TAILQ_FOREACH(wp, &w->panes, entry) { + pack(array, 5); + pack(int, wp->id); + pack(int, wp->sx); + pack(int, wp->sy); + pack(int, wp->xoff); + pack(int, wp->yoff); + + if (wp == w->active) { + w->tmate_last_sync_active_pane = wp; + active_pane_id = wp->id; + } + + } + pack(int, active_pane_id); + } + + if (s->curw) + active_window_idx = s->curw->idx; + + pack(int, active_window_idx); +} + +/* TODO add a buffer for pty_data ? */ + +#define TMATE_MAX_PTY_SIZE (16*1024) + +void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) +{ + size_t to_write; + + while (len > 0) { + to_write = len < TMATE_MAX_PTY_SIZE ? len : TMATE_MAX_PTY_SIZE; + + pack(array, 3); + pack(int, TMATE_OUT_PTY_DATA); + pack(int, wp->id); + pack(str, to_write); + pack(str_body, buf, to_write); + + buf += to_write; + len -= to_write; + } +} + +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, + &cmd_set_option_entry, + &cmd_set_window_option_entry, + NULL +}; + +int tmate_should_replicate_cmd(const struct cmd_entry *cmd) +{ + const struct cmd_entry **ptr; + + for (ptr = replicated_cmds; *ptr; ptr++) + if (*ptr == cmd) + return 1; + return 0; +} + +#define sc (&session->saved_tmux_cmds) +#define SAVED_TMUX_CMD_INITIAL_SIZE 256 +static void __tmate_exec_cmd_args(int argc, const char **argv); + +static void append_saved_cmd(struct tmate_session *session, + int argc, const char **argv) +{ + if (!sc->cmds) { + sc->capacity = SAVED_TMUX_CMD_INITIAL_SIZE; + sc->cmds = xmalloc(sizeof(*sc->cmds) * sc->capacity); + sc->tail = 0; + } + + if (sc->tail == sc->capacity) { + sc->capacity *= 2; + sc->cmds = xrealloc(sc->cmds, sizeof(*sc->cmds) * sc->capacity); + } + + sc->cmds[sc->tail].argc = argc; + sc->cmds[sc->tail].argv = cmd_copy_argv(argc, (char **)argv); + + sc->tail++; +} + +static void replay_saved_cmd(struct tmate_session *session) +{ + unsigned int i; + for (i = 0; i < sc->tail; i++) + __tmate_exec_cmd_args(sc->cmds[i].argc, (const char **)sc->cmds[i].argv); +} +#undef sc + +struct args_entry { + u_char flag; + char *value; + RB_ENTRY(args_entry) entry; +}; + +static void extract_cmd(struct cmd *cmd, int *_argc, char ***_argv) +{ + struct args_entry *entry; + struct args* args = cmd->args; + int argc = 0; + char **argv; + int next = 0, i; + + argc++; /* cmd name */ + RB_FOREACH(entry, args_tree, &args->tree) { + argc++; + if (entry->value != NULL) + argc++; + } + argc += args->argc; + argv = xmalloc(sizeof(char *) * argc); + + argv[next++] = xstrdup(cmd->entry->name); + + RB_FOREACH(entry, args_tree, &args->tree) { + xasprintf(&argv[next++], "-%c", entry->flag); + if (entry->value != NULL) + argv[next++] = xstrdup(entry->value); + } + + for (i = 0; i < args->argc; i++) + argv[next++] = xstrdup(args->argv[i]); + + *_argc = argc; + *_argv = argv; +} + +static void __tmate_exec_cmd_args(int argc, const char **argv) +{ + int i; + + pack(array, argc + 1); + pack(int, TMATE_OUT_EXEC_CMD); + + for (i = 0; i < argc; i++) + pack(string, argv[i]); +} + +void tmate_exec_cmd_args(int argc, const char **argv) +{ + __tmate_exec_cmd_args(argc, argv); + append_saved_cmd(&tmate_session, argc, argv); +} + +void tmate_set_val(const char *name, const char *value) +{ + char *buf; + xasprintf(&buf, "%s=%s", name, value); + tmate_exec_cmd_args(3, (const char *[]){"set-option", "tmate-set", buf}); + free(buf); +} + +void tmate_exec_cmd(struct cmd *cmd) +{ + int argc; + char **argv; + + extract_cmd(cmd, &argc, &argv); + tmate_exec_cmd_args(argc, (const char **)argv); + cmd_free_argv(argc, argv); +} + +void tmate_failed_cmd(int client_id, const char *cause) +{ + pack(array, 3); + pack(int, TMATE_OUT_FAILED_CMD); + pack(int, client_id); + pack(string, cause); +} + +void tmate_status(const char *left, const char *right) +{ + static char *old_left, *old_right; + + if (old_left && !strcmp(old_left, left) && + old_right && !strcmp(old_right, right)) + return; + + pack(array, 3); + pack(int, TMATE_OUT_STATUS); + pack(string, left); + pack(string, right); + + free(old_left); + free(old_right); + old_left = xstrdup(left); + old_right = xstrdup(right); +} + +void tmate_sync_copy_mode(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + pack(array, 3); + pack(int, TMATE_OUT_SYNC_COPY_MODE); + + pack(int, wp->id); + + if (wp->mode != &window_copy_mode || + data->inputtype == WINDOW_COPY_PASSWORD) { + pack(array, 0); + return; + } + pack(array, 6); + pack(int, data->backing == &wp->base); + + pack(int, data->oy); + pack(int, data->cx); + pack(int, data->cy); + + if (data->screen.sel.flag) { + pack(array, 3); + pack(int, data->selx); + pack(int, -data->sely + screen_hsize(data->backing) + + screen_size_y(data->backing) - 1); + pack(int, data->rectflag); + } else + pack(array, 0); + + if (data->inputprompt) { + pack(array, 3); + pack(int, data->inputtype); + pack(string, data->inputprompt); + pack(string, data->inputstr); + } else + pack(array, 0); +} + +void tmate_write_copy_mode(struct window_pane *wp, const char *str) +{ + pack(array, 3); + pack(int, TMATE_OUT_WRITE_COPY_MODE); + pack(int, wp->id); + pack(string, str); +} + +void tmate_write_fin(void) +{ + pack(array, 1); + pack(int, TMATE_OUT_FIN); +} + +static void do_snapshot_grid(struct grid *grid, unsigned int max_history_lines) +{ + struct grid_line *line; + struct grid_cell gc; + unsigned int line_i, i; + unsigned int max_lines; + size_t str_len; + + max_lines = max_history_lines + grid->sy; + +#define grid_num_lines(grid) (grid->hsize + grid->sy) + + if (grid_num_lines(grid) > max_lines) + line_i = grid_num_lines(grid) - max_lines; + else + line_i = 0; + + pack(array, grid_num_lines(grid) - line_i); + for (; line_i < grid_num_lines(grid); line_i++) { + line = &grid->linedata[line_i]; + + pack(array, 2); + str_len = 0; + for (i = 0; i < line->cellsize; i++) { + grid_get_cell(grid, i, line_i, &gc); + str_len += gc.data.size; + } + + pack(str, str_len); + for (i = 0; i < line->cellsize; i++) { + grid_get_cell(grid, i, line_i, &gc); + pack(str_body, gc.data.data, gc.data.size); + } + + pack(array, line->cellsize); + for (i = 0; i < line->cellsize; i++) { + grid_get_cell(grid, i, line_i, &gc); + pack(unsigned_int, ((gc.flags << 24) | + (gc.attr << 16) | + (gc.bg << 8) | + gc.fg )); + } + } + +} + +static void do_snapshot_pane(struct window_pane *wp, unsigned int max_history_lines) +{ + struct screen *screen = &wp->base; + + pack(array, 4); + pack(int, wp->id); + + pack(unsigned_int, screen->mode); + + pack(array, 3); + pack(int, screen->cx); + pack(int, screen->cy); + do_snapshot_grid(screen->grid, max_history_lines); + + if (wp->saved_grid) { + pack(array, 3); + pack(int, wp->saved_cx); + pack(int, wp->saved_cy); + do_snapshot_grid(wp->saved_grid, max_history_lines); + } else { + pack(nil); + } +} + +static void tmate_send_session_snapshot(unsigned int max_history_lines) +{ + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *pane; + int num_panes; + + pack(array, 2); + pack(int, TMATE_OUT_SNAPSHOT); + + s = RB_MIN(sessions, &sessions); + if (!s) + tmate_fatal("no session?"); + + num_panes = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + if (!w) + continue; + + TAILQ_FOREACH(pane, &w->panes, entry) + num_panes++; + } + + pack(array, num_panes); + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + if (!w) + continue; + + TAILQ_FOREACH(pane, &w->panes, entry) + do_snapshot_pane(pane, max_history_lines); + } +} + +static void tmate_send_reconnection_data(struct tmate_session *session) +{ + if (!session->reconnection_data) + return; + + pack(array, 2); + pack(int, TMATE_OUT_RECONNECT); + pack(string, session->reconnection_data); +} + +#define RECONNECTION_MAX_HISTORY_LINE 300 + +void tmate_send_reconnection_state(struct tmate_session *session) +{ + /* Start with a fresh encoder */ + tmate_encoder_destroy(&session->encoder); + tmate_encoder_init(&session->encoder, NULL, session); + + tmate_write_header(); + tmate_send_reconnection_data(session); + replay_saved_cmd(session); + /* TODO send all option variables */ + tmate_write_uname(); + tmate_write_ready(); + + tmate_sync_layout(); + tmate_send_session_snapshot(RECONNECTION_MAX_HISTORY_LINE); +} diff --git a/tmate-env.c b/tmate-env.c new file mode 100644 index 00000000..92abf069 --- /dev/null +++ b/tmate-env.c @@ -0,0 +1,36 @@ +#include "tmate.h" + +struct tmate_env { + TAILQ_ENTRY(tmate_env) entry; + char *name; + char *value; +}; + +TAILQ_HEAD(, tmate_env) tmate_env_list; + +void tmate_set_env(const char *name, const char *value) +{ + struct tmate_env *tmate_env; + + TAILQ_FOREACH(tmate_env, &tmate_env_list, entry) { + if (!strcmp(tmate_env->name, name)) { + free(tmate_env->value); + tmate_env->value = xstrdup(value); + return; + } + } + + tmate_env = xmalloc(sizeof(*tmate_env)); + tmate_env->name = xstrdup(name); + tmate_env->value = xstrdup(value); + TAILQ_INSERT_HEAD(&tmate_env_list, tmate_env, entry); +} + +void tmate_format(struct format_tree *ft) +{ + struct tmate_env *tmate_env; + + TAILQ_FOREACH(tmate_env, &tmate_env_list, entry) { + format_add(ft, tmate_env->name, "%s", tmate_env->value); + } +} diff --git a/tmate-msg.c b/tmate-msg.c new file mode 100644 index 00000000..4ee0a5de --- /dev/null +++ b/tmate-msg.c @@ -0,0 +1,96 @@ +#include +#include "tmate.h" + +void status_message_callback(int, short, void *); + +/* Very similar to status.c:status_message_set */ + +static void tmate_status_message_client(struct client *c, const char *message) +{ + struct timeval tv; + struct message_entry *msg, *msg1; + int delay; + u_int limit; + + limit = options_get_number(global_options, "message-limit"); + delay = options_get_number(c->session ? c->session->options : global_s_options, + "tmate-display-time"); + + status_prompt_clear(c); + status_message_clear(c); + + xasprintf(&c->message_string, "[tmate] %s", message); + + 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); + + TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { + if (msg->msg_num + limit >= c->message_next) + break; + free(msg->msg); + TAILQ_REMOVE(&c->message_log, msg, entry); + free(msg); + } + + 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->flags |= CLIENT_STATUS | CLIENT_FORCE_STATUS; + + recalculate_sizes(); +} + +static void tmate_status_message_session(const char *message) +{ + if (tmate_foreground) + return; + + struct session *s; + s = RB_MIN(sessions, &sessions); + if (!s) { + cfg_add_cause("%s", message); + return; + } + + struct window_pane *wp; + wp = s->curw->window->active; + if (wp->mode == &window_copy_mode) + window_copy_add(wp, "%s", message); +} + +void __tmate_status_message(const char *fmt, va_list ap) +{ + struct client *c; + char *message; + + xvasprintf(&message, fmt, ap); + tmate_info("%s", message); + + TAILQ_FOREACH(c, &clients, entry) { + if (c && !(c->flags & CLIENT_READONLY)) + tmate_status_message_client(c, message); + } + + tmate_status_message_session(message); + + free(message); +} + +void tmate_status_message(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __tmate_status_message(fmt, ap); + va_end(ap); +} diff --git a/tmate-msgpack.c b/tmate-msgpack.c new file mode 100644 index 00000000..c41b07ee --- /dev/null +++ b/tmate-msgpack.c @@ -0,0 +1,224 @@ +#include "tmate.h" +#include "tmate-protocol.h" + +static void on_encoder_buffer_ready(__unused evutil_socket_t fd, + __unused short what, void *arg) +{ + struct tmate_encoder *encoder = arg; + + encoder->ev_active = false; + if (encoder->ready_callback) + encoder->ready_callback(encoder->userdata, encoder->buffer); +} + +static int on_encoder_write(void *userdata, const char *buf, size_t len) +{ + struct tmate_encoder *encoder = userdata; + + if (evbuffer_add(encoder->buffer, buf, len) < 0) + tmate_fatal("Cannot buffer encoded data"); + + if (!encoder->ev_active) { + event_active(encoder->ev_buffer, EV_READ, 0); + encoder->ev_active = true; + } + + return 0; +} + +/* Really sad hack, but we can get away with it */ +#define tmate_encoder_from_pk(pk) ((struct tmate_encoder *)pk) + +void msgpack_pack_string(msgpack_packer *pk, const char *str) +{ + size_t len = strlen(str); + + msgpack_pack_str(pk, len); + msgpack_pack_str_body(pk, str, len); +} + +void msgpack_pack_boolean(msgpack_packer *pk, bool value) +{ + if (value) + msgpack_pack_true(pk); + else + msgpack_pack_false(pk); +} + +void tmate_encoder_init(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata) +{ + msgpack_packer_init(&encoder->pk, encoder, &on_encoder_write); + encoder->buffer = evbuffer_new(); + encoder->ready_callback = callback; + encoder->userdata = userdata; + + if (!encoder->buffer) + tmate_fatal("Can't allocate buffer"); + + encoder->ev_buffer = event_new(tmate_session.ev_base, -1, + EV_READ | EV_PERSIST, on_encoder_buffer_ready, encoder); + if (!encoder->ev_buffer) + tmate_fatal("Can't allocate event"); + + event_add(encoder->ev_buffer, NULL); + + encoder->ev_active = false; +} + +void tmate_encoder_destroy(struct tmate_encoder *encoder) +{ + /* encoder->pk doesn't need any cleanup */ + evbuffer_free(encoder->buffer); + event_del(encoder->ev_buffer); + event_free(encoder->ev_buffer); + memset(encoder, 0, sizeof(*encoder)); +} + +void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata) +{ + encoder->ready_callback = callback; + encoder->userdata = userdata; + if (encoder->ready_callback) + encoder->ready_callback(encoder->userdata, encoder->buffer); +} + +void tmate_decoder_error(void) +{ + /* TODO Don't kill the session, disconnect */ + tmate_print_stack_trace(); + tmate_fatal("Received a bad message"); +} + +void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj) +{ + if (obj.type != MSGPACK_OBJECT_ARRAY) + tmate_decoder_error(); + + uk->argv = obj.via.array.ptr; + uk->argc = obj.via.array.size; +} + +int64_t unpack_int(struct tmate_unpacker *uk) +{ + int64_t val; + + if (uk->argc == 0) + tmate_decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER && + uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER) + tmate_decoder_error(); + + val = uk->argv[0].via.i64; + + uk->argv++; + uk->argc--; + + return val; +} + +bool unpack_bool(struct tmate_unpacker *uk) +{ + bool val; + + if (uk->argc == 0) + tmate_decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_BOOLEAN) + tmate_decoder_error(); + + val = uk->argv[0].via.boolean; + + uk->argv++; + uk->argc--; + + return val; +} + +void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len) +{ + if (uk->argc == 0) + tmate_decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_STR && + uk->argv[0].type != MSGPACK_OBJECT_BIN) + tmate_decoder_error(); + + *len = uk->argv[0].via.str.size; + *buf = uk->argv[0].via.str.ptr; + + uk->argv++; + uk->argc--; +} + +char *unpack_string(struct tmate_unpacker *uk) +{ + const char *buf; + char *alloc_buf; + size_t len; + + unpack_buffer(uk, &buf, &len); + + alloc_buf = xmalloc(len + 1); + memcpy(alloc_buf, buf, len); + alloc_buf[len] = '\0'; + + return alloc_buf; +} + +void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested) +{ + if (uk->argc == 0) + tmate_decoder_error(); + + init_unpacker(nested, uk->argv[0]); + + uk->argv++; + uk->argc--; +} + +#define UNPACKER_RESERVE_SIZE 1024 + +void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, + void *userdata) +{ + if (!msgpack_unpacker_init(&decoder->unpacker, UNPACKER_RESERVE_SIZE)) + tmate_fatal("Cannot initialize the unpacker"); + decoder->reader = reader; + decoder->userdata = userdata; +} + +void tmate_decoder_destroy(struct tmate_decoder *decoder) +{ + msgpack_unpacker_destroy(&decoder->unpacker); + memset(decoder, 0, sizeof(*decoder)); +} + +void tmate_decoder_get_buffer(struct tmate_decoder *decoder, + char **buf, size_t *len) +{ + if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker, UNPACKER_RESERVE_SIZE)) + tmate_fatal("cannot expand decoder buffer"); + + *buf = msgpack_unpacker_buffer(&decoder->unpacker); + *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); +} + +void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) +{ + struct tmate_unpacker _uk, *uk = &_uk; + msgpack_unpacked result; + + msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); + + msgpack_unpacked_init(&result); + while (msgpack_unpacker_next(&decoder->unpacker, &result)) { + init_unpacker(uk, result.data); + decoder->reader(decoder->userdata, uk); + } + msgpack_unpacked_destroy(&result); +} diff --git a/tmate-protocol.h b/tmate-protocol.h new file mode 100644 index 00000000..81716bb2 --- /dev/null +++ b/tmate-protocol.h @@ -0,0 +1,107 @@ +#ifndef TMATE_PROTOCOL_H +#define TMATE_PROTOCOL_H + +enum tmate_control_out_msg_types { + TMATE_CTL_HEADER, + TMATE_CTL_DEAMON_OUT_MSG, + TMATE_CTL_SNAPSHOT, + TMATE_CTL_CLIENT_JOIN, + TMATE_CTL_CLIENT_LEFT, + TMATE_CTL_EXEC, + TMATE_CTL_LATENCY, +}; + +/* +[TMATE_CTL_HEADER, int: ctl_proto_version, string: ip_address, string: pubkey, + string: session_token, string: session_token_ro, string: ssh_cmd_fmt] + string: client_version, int: client_protocol_version] +[TMATE_CTL_DEAMON_OUT_MSG, object: msg] +[TMATE_CTL_SNAPSHOT, [[int: pane_id, [int: cur_x, int: cur_y], int: mode, + [[string: line_utf8, [int: char_attr, ...]], ...], ...], ...]] +[TMATE_CTL_CLIENT_JOIN, int: client_id, string: ip_address, string: pubkey, boolean: readonly] +[TMATE_CTL_CLIENT_LEFT, int: client_id] +[TMATE_CTL_EXEC, string: username, string: ip_address, string: pubkey, string: command] +[TMATE_CTL_LATENCY, int: client_id, int: latency_ms] // client_id == -1: tmate host +*/ + +enum tmate_control_in_msg_types { + TMATE_CTL_DEAMON_FWD_MSG, + TMATE_CTL_REQUEST_SNAPSHOT, + TMATE_CTL_PANE_KEYS, + TMATE_CTL_RESIZE, + TMATE_CTL_EXEC_RESPONSE, + TMATE_CTL_RENAME_SESSION, +}; + +/* +[TMATE_CTL_DEAMON_FWD_MSG, object: msg] +[TMATE_CTL_REQUEST_SNAPSHOT, int: max_history_lines] +[TMATE_CTL_PANE_KEYS, int: pane_id, string: keys] +[TMATE_CTL_RESIZE, int: sx, int: sy] // sx == -1: no clients +[TMATE_CTL_EXEC_RESPONSE, int: exit_code, string: message] +[TMATE_CTL_RENAME_SESSION, string: stoken, string: stoken_ro] +*/ + +enum tmate_daemon_out_msg_types { + TMATE_OUT_HEADER, + TMATE_OUT_SYNC_LAYOUT, + TMATE_OUT_PTY_DATA, + TMATE_OUT_EXEC_CMD_STR, + TMATE_OUT_FAILED_CMD, + TMATE_OUT_STATUS, + TMATE_OUT_SYNC_COPY_MODE, + TMATE_OUT_WRITE_COPY_MODE, + TMATE_OUT_FIN, + TMATE_OUT_READY, + TMATE_OUT_RECONNECT, + TMATE_OUT_SNAPSHOT, + TMATE_OUT_EXEC_CMD, + TMATE_OUT_UNAME, +}; + +/* +[TMATE_OUT_HEADER, int: proto_version, string: version] +[TMATE_OUT_SYNC_LAYOUT, [int: sx, int: sy, [[int: win_id, string: win_name, + [[int: pane_id, int: sx, int: sy, int: xoff, int: yoff], ...], + int: active_pane_id], ...], int: active_win_id] +[TMATE_OUT_PTY_DATA, int: pane_id, binary: buffer] +[TMATE_OUT_EXEC_CMD_STR, string: cmd] +[TMATE_OUT_FAILED_CMD, int: client_id, string: cause] +[TMATE_OUT_STATUS, string: left, string: right] +[TMATE_OUT_SYNC_COPY_MODE, int: pane_id, [int: backing, int: oy, int: cx, int: cy, + [int: selx, int: sely, int: flags], + [int: type, string: input_prompt, string: input_str]]) + // Any of the array can be [] +[TMATE_OUT_WRITE_COPY_MODE, int: pane_id, string: str] +[TMATE_OUT_FIN] +[TMATE_OUT_READY] +[TMATE_OUT_RECONNECT, string: reconnection_data] +[TMATE_OUT_SNAPSHOT, ...] +[TMATE_OUT_EXEC_CMD, string: cmd_name, ...string: args] +[TMATE_OUT_UNAME, string: name.sysname, string: name.nodename, + string: name.release, string: name.version, string: name.machine] +*/ + +enum tmate_daemon_in_msg_types { + TMATE_IN_NOTIFY, + TMATE_IN_LEGACY_PANE_KEY, + TMATE_IN_RESIZE, + TMATE_IN_EXEC_CMD_STR, + TMATE_IN_SET_ENV, + TMATE_IN_READY, + TMATE_IN_PANE_KEY, + TMATE_IN_EXEC_CMD, +}; + +/* +[TMATE_IN_NOTIFY, string: msg] +[TMATE_IN_PANE_KEY, int: key] +[TMATE_IN_RESIZE, int: sx, int: sy] // sx == -1: no clients +[TMATE_IN_EXEC_CMD_STR, int: client_id, string: cmd] +[TMATE_IN_SET_ENV, string: name, string: value] +[TMATE_IN_READY] +[TMATE_IN_PANE_KEY, int: pane_id, uint64 keycode] // pane_id == -1: active pane +[TMATE_IN_EXEC_CMD, int: client_id, ...string: args] +*/ + +#endif diff --git a/tmate-session.c b/tmate-session.c new file mode 100644 index 00000000..5ba0896c --- /dev/null +++ b/tmate-session.c @@ -0,0 +1,260 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "tmate.h" + +#define TMATE_DNS_RETRY_TIMEOUT 2 +#define TMATE_RECONNECT_RETRY_TIMEOUT 2 + +struct tmate_session tmate_session; + +static void lookup_and_connect(void); + +static void on_dns_retry(__unused evutil_socket_t fd, __unused short what, + void *arg) +{ + struct tmate_session *session = arg; + + assert(session->ev_dns_retry); + event_free(session->ev_dns_retry); + session->ev_dns_retry = NULL; + + lookup_and_connect(); +} + +static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) +{ + struct evutil_addrinfo *ai; + const char *host = ptr; + + evdns_base_free(tmate_session.ev_dnsbase, 0); + tmate_session.ev_dnsbase = NULL; + + if (errcode) { + struct tmate_session *session = &tmate_session; + + if (session->ev_dns_retry) + return; + + struct timeval tv = { .tv_sec = TMATE_DNS_RETRY_TIMEOUT, .tv_usec = 0 }; + + session->ev_dns_retry = evtimer_new(session->ev_base, on_dns_retry, session); + if (!session->ev_dns_retry) + tmate_fatal("out of memory"); + evtimer_add(session->ev_dns_retry, &tv); + + tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)", + host, TMATE_DNS_RETRY_TIMEOUT, + evutil_gai_strerror(errcode)); + return; + } + + tmate_status_message("Connecting to %s...", host); + + int i, num_clients = 0; + for (ai = addr; ai; ai = ai->ai_next) + num_clients++; + + struct tmate_ssh_client *ssh_clients[num_clients]; + + for (ai = addr, i = 0; ai; ai = ai->ai_next, i++) { + char buf[128]; + const char *ip = NULL; + if (ai->ai_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; + ip = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128); + } else if (ai->ai_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; + ip = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128); + } + + ssh_clients[i] = tmate_ssh_client_alloc(&tmate_session, ip); + } + + for (i = 0; i < num_clients; i++) + connect_ssh_client(ssh_clients[i]); + + evutil_freeaddrinfo(addr); +} + +static void lookup_and_connect(void) +{ + struct evutil_addrinfo hints; + const char *tmate_server_host; + + assert(!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)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = EVUTIL_AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + tmate_server_host = options_get_string(global_options, + "tmate-server-host"); + tmate_debug("Looking up %s...", tmate_server_host); + (void)evdns_getaddrinfo(tmate_session.ev_dnsbase, tmate_server_host, NULL, + &hints, dns_cb, (void *)tmate_server_host); +} + +static void __tmate_session_init(struct tmate_session *session, + struct event_base *base) +{ + memset(session, 0, sizeof(*session)); + + session->ev_base = base; + + /* + * Early initialization of encoder because we need to parse + * config files to get the server configs, but while we are parsing + * config files, we need to buffer bind commands and all for the + * slave. + * Decoder is setup later. + */ + tmate_encoder_init(&session->encoder, NULL, &tmate_session); + + session->min_sx = -1; + session->min_sy = -1; + + TAILQ_INIT(&session->clients); +} + +void tmate_session_init(struct event_base *base) +{ + __tmate_session_init(&tmate_session, base); + tmate_write_header(); +} + +static void send_authorized_keys(void) +{ + char *path; + path = options_get_string(global_options, "tmate-authorized-keys"); + if (strlen(path) == 0) + return; + + path = xstrdup(path); + tmate_info("Using %s for access control", path); + + FILE *f; + char *line; + size_t len; + + if (path[0] == '~' && path[1] == '/') { + const char *home = find_home(); + if (home) { + char *new_path; + xasprintf(&new_path, "%s%s", home, &path[1]); + free(path); + path = new_path; + } + } + + if ((f = fopen(path, "r")) == NULL) { + cfg_add_cause("%s: %s", path, strerror(errno)); + free(path); + return; + } + + while ((line = fparseln(f, &len, NULL, NULL, 0)) != NULL) { + if (len == 0) + continue; + tmate_set_val("authorized_keys", line); + free(line); + } + + if (ferror(f)) + cfg_add_cause("%s: %s", path, strerror(errno)); + + fclose(f); + free(path); +} + +void tmate_session_start(void) +{ + /* + * We split init and start because: + * - We need to process the tmux config file during the connection as + * we are setting up the tmate identity. + * - While we are parsing the config file, we need to be able to + * serialize it, and so we need a worker encoder. + */ + if (tmate_foreground) { + tmate_set_val("foreground", "true"); + tmate_info("To connect to the session locally, run: tmate -S %s attach", socket_path); + } else { + cfg_add_cause("%s", "Tip: if you wish to use tmate only for remote access, run: tmate -F"); + cfg_add_cause("%s", "To see the following messages again, run in a tmate session: tmate show-messages"); + cfg_add_cause("%s", "Press or to continue"); + cfg_add_cause("%s", "---------------------------------------------------------------------"); + } + + send_authorized_keys(); + tmate_write_uname(); + tmate_write_ready(); + lookup_and_connect(); +} + +static void on_reconnect_retry(__unused evutil_socket_t fd, __unused short what, void *arg) +{ + struct tmate_session *session = arg; + + assert(session->ev_connection_retry); + event_free(session->ev_connection_retry); + session->ev_connection_retry = NULL; + + if (session->last_server_ip) { + /* + * We have a previous server ip. Let's try that again first, + * but then connect to any server if it fails again. + */ + struct tmate_ssh_client *c = tmate_ssh_client_alloc(session, + session->last_server_ip); + connect_ssh_client(c); + free(session->last_server_ip); + session->last_server_ip = NULL; + } else { + lookup_and_connect(); + } +} + +void tmate_reconnect_session(struct tmate_session *session, const char *message) +{ + /* + * We no longer have an SSH connection. Time to reconnect. + * We'll reuse some of the session information if we can, + * and we'll try to reconnect to the same server if possible, + * to avoid an SSH connection string change. + */ + struct timeval tv = { .tv_sec = TMATE_RECONNECT_RETRY_TIMEOUT, .tv_usec = 0 }; + + if (session->ev_connection_retry) + return; + + session->ev_connection_retry = evtimer_new(session->ev_base, on_reconnect_retry, session); + if (!session->ev_connection_retry) + tmate_fatal("out of memory"); + evtimer_add(session->ev_connection_retry, &tv); + + if (message && !tmate_foreground) + tmate_status_message("Reconnecting... (%s)", message); + else + tmate_status_message("Reconnecting..."); + + /* + * This says that we'll need to send a snapshot of the current state. + */ + session->reconnected = true; +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c new file mode 100644 index 00000000..bed79e95 --- /dev/null +++ b/tmate-ssh-client.c @@ -0,0 +1,585 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tmate.h" +#include "window-copy.h" + +static void on_ssh_client_event(struct tmate_ssh_client *client); +static void __on_ssh_client_event(evutil_socket_t fd, short what, void *arg); + +static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...); +static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...); + +static void read_channel(struct tmate_ssh_client *client) +{ + struct tmate_decoder *decoder = &client->tmate_session->decoder; + char *buf; + ssize_t len; + + for (;;) { + tmate_decoder_get_buffer(decoder, &buf, &len); + len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); + if (len < 0) { + kill_ssh_client(client, "Error reading from channel: %s", + ssh_get_error(client->session)); + break; + } + + if (len == 0) + break; + + tmate_decoder_commit(decoder, len); + } +} + +static void on_decoder_read(void *userdata, struct tmate_unpacker *uk) +{ + struct tmate_ssh_client *client = userdata; + tmate_dispatch_slave_message(client->tmate_session, uk); +} + +static void on_encoder_write(void *userdata, struct evbuffer *buffer) +{ + struct tmate_ssh_client *client = userdata; + ssize_t len, written; + unsigned char *buf; + + if (!client->channel) + return; + + for(;;) { + len = evbuffer_get_length(buffer); + if (!len) + break; + + buf = evbuffer_pullup(buffer, -1); + + written = ssh_channel_write(client->channel, buf, len); + if (written < 0) { + kill_ssh_client(client, "Error writing to channel: %s", + ssh_get_error(client->session)); + break; + } + + evbuffer_drain(buffer, written); + } +} + +static void on_ssh_auth_server_complete(struct tmate_ssh_client *connected_client) +{ + /* + * The first ssh connection succeeded. Hopefully this one offers the + * best latency. We can now kill the other ssh clients that are trying + * to connect. + */ + struct tmate_session *session = connected_client->tmate_session; + struct tmate_ssh_client *client, *tmp_client; + + TAILQ_FOREACH_SAFE(client, &session->clients, node, tmp_client) { + if (client == connected_client) + continue; + + kill_ssh_client(client, NULL); + } +} + +static char *get_identity(void) +{ + char *identity; + + identity = options_get_string(global_options, "tmate-identity"); + if (!strlen(identity)) + return NULL; + + if (strchr(identity, '/')) + identity = xstrdup(identity); + else + xasprintf(&identity, "%%d/%s", identity); + + return identity; +} + +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; + + client->tmate_session->need_passphrase = 1; + + if (client->tmate_session->passphrase) + strlcpy(buf, client->tmate_session->passphrase, len); + else + strcpy(buf, ""); + + return 0; +} + +static void on_passphrase_read(const char *passphrase, void *private) +{ + struct tmate_ssh_client *client = private; + + client->tmate_session->passphrase = xstrdup(passphrase); + on_ssh_client_event(client); +} + +static void request_passphrase(struct tmate_ssh_client *client) +{ + struct window_pane *wp; + struct window_copy_mode_data *data; + + /* + * We'll display the prompt on the first pane. + * It doesn't make much sense, but it's simpler to reuse the copy mode + * and its key parsing logic compared to rolling something on our own. + */ + wp = RB_MIN(window_pane_tree, &all_window_panes); + + if (wp->mode) { + data = wp->modedata; + if (data->inputtype == WINDOW_COPY_PASSWORD) { + /* We are already requesting the passphrase */ + return; + } + window_pane_reset_mode(wp); + } + + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_from_pane(wp, 0); + data = wp->modedata; + + data->inputtype = WINDOW_COPY_PASSWORD; + data->inputprompt = "SSH key passphrase"; + + mode_key_init(&data->mdata, &mode_key_tree_vi_edit); + + window_copy_update_selection(wp, 1); + window_copy_redraw_screen(wp); + + data->password_cb = on_passphrase_read; + data->password_cb_private = client; +} + +#define KEEPALIVE_IDLE 30 +#define KEEPALIVE_CNT 4 +#define KEEPALIVE_INTVL 11 +#define WRITE_TIMEOUT 80 + +static void tune_socket_opts(int fd) +{ +#define SSO(level, optname, val) ({ \ + int _flag = val; \ + if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \ + /* If the connection has been closed, we'll get EINVAL */ \ + if (errno != EINVAL) \ + tmate_info("setsockopt(" #level ", " #optname ", %d) failed %s", val, strerror(errno)); \ + } \ +}) + + SSO(IPPROTO_IP, IP_TOS, 0x10); /* IPTOS_LOWDELAY */ + SSO(IPPROTO_TCP, TCP_NODELAY, 1); + SSO(SOL_SOCKET, SO_KEEPALIVE, 1); +#ifdef TCP_KEEPALIVE + /* + * The TCP_KEEPALIVE options enable to specify the amount of time, in + * seconds, that the connection must be idle before keepalive probes + * (if enabled) are sent. + */ + SSO(IPPROTO_TCP, TCP_KEEPALIVE, KEEPALIVE_IDLE); +#endif +#ifdef TCP_KEEPIDLE + /* + * Same as TCP_KEEPALIVE, but on different systems + */ + SSO(IPPROTO_TCP, TCP_KEEPIDLE, KEEPALIVE_IDLE); +#endif +#ifdef TCP_KEEPCNT + /* + * When keepalive probes are enabled, this option will set the number + * of times a keepalive probe should be repeated if the peer is not + * responding. After this many probes, the connection will be closed. + */ + SSO(IPPROTO_TCP, TCP_KEEPCNT, KEEPALIVE_CNT); +#endif +#ifdef TCP_KEEPINTVL + /* + * When keepalive probes are enabled, this option will set the amount + * of time in seconds between successive keepalives sent to probe an + * unresponsive peer. + */ + SSO(IPPROTO_TCP, TCP_KEEPINTVL, KEEPALIVE_INTVL); +#endif +#ifdef TCP_USER_TIMEOUT + /* + * This option takes an unsigned int as an argument. When the + * value is greater than 0, it specifies the maximum amount of + * time in milliseconds that transmitted data may remain + * unacknowledged before TCP will forcibly close the + * corresponding connection and return ETIMEDOUT to the + * application. + */ + SSO(IPPROTO_TCP, TCP_USER_TIMEOUT, 1000*WRITE_TIMEOUT); +#endif +#undef SSO +} + +static void init_conn_fd(struct tmate_ssh_client *client) +{ + int fd; + + if (client->ev_ssh) + return; + + if ((fd = ssh_get_fd(client->session)) < 0) + return; + + tune_socket_opts(fd); + + client->ev_ssh = event_new(client->tmate_session->ev_base, fd, + EV_READ | EV_PERSIST, + __on_ssh_client_event, client); + if (!client->ev_ssh) + tmate_fatal("out of memory"); + event_add(client->ev_ssh, NULL); +} + +static void on_ssh_client_event(struct tmate_ssh_client *client) +{ + ssh_session session = client->session; + ssh_channel channel = client->channel; + + switch (client->state) { + case SSH_INIT: { + client->session = session = ssh_new(); + if (!session) { + tmate_fatal("cannot ssh_new()"); + return; + } + + ssh_set_callbacks(session, &client->ssh_callbacks); + + int verbosity = SSH_LOG_NOLOG + log_get_level(); + int port = options_get_number(global_options, "tmate-server-port"); + + ssh_set_blocking(session, 0); + ssh_options_set(session, SSH_OPTIONS_HOST, client->server_ip); + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + ssh_options_set(session, SSH_OPTIONS_PORT, &port); + ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); + ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); + + char *identity; + if ((identity = get_identity())) { + /* + * FIXME libssh will continue with the next set of + * keys if the identity has a passphrase and the + * regular one doesn't. + */ + ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity); + + /* Do not use keys from ssh-agent. */ + unsetenv("SSH_AUTH_SOCK"); + free(identity); + } + + client->state = SSH_CONNECT; + } + // fall through + + case SSH_CONNECT: + switch (ssh_connect(session)) { + case SSH_AGAIN: + init_conn_fd(client); + return; + case SSH_ERROR: + kill_ssh_client(client, "Error connecting: %s", + ssh_get_error(session)); + return; + case SSH_OK: + init_conn_fd(client); + + tmate_debug("Establishing connection to %s", client->server_ip); + client->state = SSH_AUTH_SERVER; + } + // fall through + + case SSH_AUTH_SERVER: { + ssh_key pubkey; + enum ssh_keytypes_e key_type; + unsigned char *hash; + ssize_t hash_len; + char *hash_str; + const char *server_hash_str; + int match; + +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) + if (ssh_get_server_publickey(session, &pubkey) < 0) + tmate_fatal("ssh_get_server_publickey"); +#else + if (ssh_get_publickey(session, &pubkey) < 0) + tmate_fatal("ssh_get_publickey"); +#endif + + if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, + &hash, &hash_len) < 0) { + kill_ssh_client(client, "Failed to get server fingerprint"); + return; + } + + hash_str = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256, + hash, hash_len); + if (!hash_str) + tmate_fatal("malloc failed"); + + key_type = ssh_key_type(pubkey); + + switch (key_type) { + case SSH_KEYTYPE_RSA: + server_hash_str = options_get_string(global_options, + "tmate-server-rsa-fingerprint"); + break; + case SSH_KEYTYPE_ECDSA: +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) + case SSH_KEYTYPE_ECDSA_P256: + case SSH_KEYTYPE_ECDSA_P384: + case SSH_KEYTYPE_ECDSA_P521: +#endif + server_hash_str = options_get_string(global_options, + "tmate-server-ecdsa-fingerprint"); + break; + case SSH_KEYTYPE_ED25519: + server_hash_str = options_get_string(global_options, + "tmate-server-ed25519-fingerprint"); + break; + default: + server_hash_str = ""; + } + + match = !strcmp(hash_str, server_hash_str); + if (!match) { + kill_ssh_client(client, "Server fingerprint not recognized: " + "`%s', expected `%s'", server_hash_str, hash_str); + } + + ssh_key_free(pubkey); + ssh_clean_pubkey_hash(&hash); + free(hash_str); + + if (!match) + return; + + /* + * At this point, we abort other connection attempts to the + * other tmate servers, since we have reached the fastest one. + * We need to do it before we ask the user its passphrase, + * otherwise the speed test would be biased. + */ + tmate_debug("Connected to %s", client->server_ip); + on_ssh_auth_server_complete(client); + + client->state = SSH_AUTH_CLIENT_NONE; + } + // fall through + + case SSH_AUTH_CLIENT_NONE: + switch (ssh_userauth_none(session, NULL)) { + case SSH_AUTH_AGAIN: + return; + case SSH_AUTH_ERROR: + kill_ssh_client(client, "Auth error: %s", ssh_get_error(session)); + return; + case SSH_AUTH_SUCCESS: + tmate_debug("Auth successful via none method"); + client->state = SSH_NEW_CHANNEL; + goto SSH_NEW_CHANNEL; + case SSH_AUTH_PARTIAL: + case SSH_AUTH_DENIED: + client->state = SSH_AUTH_CLIENT_PUBKEY; + } + // fall through + + case SSH_AUTH_CLIENT_PUBKEY: + client->tried_passphrase = client->tmate_session->passphrase; + switch (ssh_userauth_publickey_auto(session, NULL, client->tried_passphrase)) { + case SSH_AUTH_AGAIN: + return; + case SSH_AUTH_PARTIAL: + case SSH_AUTH_INFO: + case SSH_AUTH_DENIED: + if (client->tmate_session->need_passphrase) { + request_passphrase(client); + } else { + kill_ssh_client(client, "SSH keys not found." + " Run 'ssh-keygen' to create keys."); + return; + } + + if (client->tried_passphrase) + tmate_status_message("Can't load SSH key." + " Try typing passphrase again in case of typo. ctrl-c to abort."); + return; + case SSH_AUTH_ERROR: + kill_ssh_client(client, "Auth error: %s", ssh_get_error(session)); + return; + case SSH_AUTH_SUCCESS: + tmate_debug("Auth successful with pubkey"); + client->state = SSH_NEW_CHANNEL; + } + // fall through + +SSH_NEW_CHANNEL: + case SSH_NEW_CHANNEL: + client->channel = channel = ssh_channel_new(session); + if (!channel) { + tmate_fatal("cannot ssh_channel_new()"); + return; + } + client->state = SSH_OPEN_CHANNEL; + // fall through + + case SSH_OPEN_CHANNEL: + switch (ssh_channel_open_session(channel)) { + case SSH_AGAIN: + return; + case SSH_ERROR: + kill_ssh_client(client, "Error opening channel: %s", + ssh_get_error(session)); + return; + case SSH_OK: + tmate_debug("Session opened, initializing tmate"); + client->state = SSH_BOOTSTRAP; + } + // fall through + + case SSH_BOOTSTRAP: + switch (ssh_channel_request_subsystem(channel, "tmate")) { + case SSH_AGAIN: + return; + case SSH_ERROR: + kill_ssh_client(client, "Error initializing tmate: %s", + ssh_get_error(session)); + return; + case SSH_OK: + tmate_debug("Ready"); + + /* Writes are now performed in a blocking fashion */ + ssh_set_blocking(session, 1); + + client->state = SSH_READY; + + if (client->tmate_session->reconnected) + tmate_send_reconnection_state(client->tmate_session); + + tmate_encoder_set_ready_callback(&client->tmate_session->encoder, + on_encoder_write, client); + tmate_decoder_init(&client->tmate_session->decoder, + on_decoder_read, client); + + free(client->tmate_session->last_server_ip); + client->tmate_session->last_server_ip = xstrdup(client->server_ip); + } + // fall through + + case SSH_READY: + read_channel(client); + } +} + +static void __on_ssh_client_event(__unused evutil_socket_t fd, __unused short what, void *arg) +{ + on_ssh_client_event(arg); +} + +static void kill_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...) +{ + bool last_client; + va_list ap; + char *message = NULL; + + TAILQ_REMOVE(&client->tmate_session->clients, client, node); + last_client = TAILQ_EMPTY(&client->tmate_session->clients); + + if (fmt && last_client) { + va_start(ap, fmt); + xvasprintf(&message, fmt, ap); + va_end(ap); + tmate_status_message("%s", message); + } + + tmate_debug("SSH client killed (%s)", client->server_ip); + + if (client->ev_ssh) { + event_del(client->ev_ssh); + event_free(client->ev_ssh); + client->ev_ssh = NULL; + } + + if (client->state == SSH_READY) { + tmate_encoder_set_ready_callback(&client->tmate_session->encoder, NULL, NULL); + tmate_decoder_destroy(&client->tmate_session->decoder); + + client->tmate_session->min_sx = -1; + client->tmate_session->min_sy = -1; + recalculate_sizes(); + } + + if (client->session) { + /* ssh_free() also frees the associated channels. */ + ssh_free(client->session); + client->session = NULL; + client->channel = NULL; + } + + if (last_client) + tmate_reconnect_session(client->tmate_session, message); + + free(client->server_ip); + free(client); +} + +void connect_ssh_client(struct tmate_ssh_client *client) +{ + assert(!client->session); + client->state = SSH_INIT; + on_ssh_client_event(client); +} + +static void ssh_log_function(int priority, const char *function, + const char *buffer, __unused void *userdata) +{ + tmate_debug("[%d] [%s] %s", priority, function, buffer); +} + +struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, + const char *server_ip) + +{ + struct tmate_ssh_client *client; + client = xmalloc(sizeof(*client)); + memset(client, 0, sizeof(*client)); + + ssh_set_log_callback(ssh_log_function); + + memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks)); + ssh_callbacks_init(&client->ssh_callbacks); + client->ssh_callbacks.userdata = client; + client->ssh_callbacks.auth_function = passphrase_callback; + + client->tmate_session = session; + TAILQ_INSERT_TAIL(&session->clients, client, node); + + client->server_ip = xstrdup(server_ip); + client->state = SSH_NONE; + client->session = NULL; + client->channel = NULL; + + return client; +} diff --git a/tmate.1 b/tmate.1 new file mode 120000 index 00000000..44d7405b --- /dev/null +++ b/tmate.1 @@ -0,0 +1 @@ +tmux.1 \ No newline at end of file diff --git a/tmate.h b/tmate.h new file mode 100644 index 00000000..f4783dc8 --- /dev/null +++ b/tmate.h @@ -0,0 +1,218 @@ +#ifndef TMATE_H +#define TMATE_H + +#include +#include +#include +#include +#include + +#include "tmux.h" + +#define tmate_debug(...) log_emit(LOG_DEBUG, __VA_ARGS__) +#define tmate_info(...) log_emit(LOG_INFO, __VA_ARGS__) +#define tmate_fatal(...) fatalx( __VA_ARGS__) + +/* tmate-msgpack.c */ + +typedef void tmate_encoder_write_cb(void *userdata, struct evbuffer *buffer); + +struct tmate_encoder { + msgpack_packer pk; + tmate_encoder_write_cb *ready_callback; + void *userdata; + struct evbuffer *buffer; + struct event *ev_buffer; + bool ev_active; +}; + +extern void tmate_encoder_init(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata); +extern void tmate_encoder_destroy(struct tmate_encoder *encoder); +extern void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata); + +extern void msgpack_pack_string(msgpack_packer *pk, const char *str); +extern void msgpack_pack_boolean(msgpack_packer *pk, bool value); + +#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, ##__VA_ARGS__) + +struct tmate_unpacker; +struct tmate_decoder; +typedef void tmate_decoder_reader(void *userdata, struct tmate_unpacker *uk); + +struct tmate_decoder { + struct msgpack_unpacker unpacker; + tmate_decoder_reader *reader; + void *userdata; +}; + +extern void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata); +extern void tmate_decoder_destroy(struct tmate_decoder *decoder); +extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len); +extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); + +struct tmate_unpacker { + int argc; + msgpack_object *argv; +}; + +extern void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj); +extern void tmate_decoder_error(void); +extern int64_t unpack_int(struct tmate_unpacker *uk); +extern bool unpack_bool(struct tmate_unpacker *uk); +extern void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len); +extern char *unpack_string(struct tmate_unpacker *uk); +extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested); + +#define unpack_each(nested_uk, tmp_uk, uk) \ + for (unpack_array(uk, tmp_uk); \ + (tmp_uk)->argc > 0 && (init_unpacker(nested_uk, (tmp_uk)->argv[0]), 1); \ + (tmp_uk)->argv++, (tmp_uk)->argc--) + +/* tmate-encoder.c */ + +#define TMATE_PROTOCOL_VERSION 6 + +struct tmate_session; + +extern void tmate_write_header(void); +extern void tmate_write_uname(void); +extern void tmate_write_ready(void); +extern void tmate_sync_layout(void); +extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); +extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); +extern void tmate_set_val(const char *name, const char *value); +extern void tmate_exec_cmd_args(int argc, const char **argv); +extern void tmate_exec_cmd(struct cmd *cmd); +extern void tmate_failed_cmd(int client_id, const char *cause); +extern void tmate_status(const char *left, const char *right); +extern void tmate_sync_copy_mode(struct window_pane *wp); +extern void tmate_write_copy_mode(struct window_pane *wp, const char *str); +extern void tmate_write_fin(void); +extern void tmate_send_reconnection_state(struct tmate_session *session); + +/* tmate-decoder.c */ + +struct tmate_session; +extern void tmate_dispatch_slave_message(struct tmate_session *session, + struct tmate_unpacker *uk); + +/* tmate-ssh-client.c */ + +enum tmate_ssh_client_state_types { + SSH_NONE, + SSH_INIT, + SSH_CONNECT, + SSH_AUTH_SERVER, + SSH_AUTH_CLIENT_NONE, + SSH_AUTH_CLIENT_PUBKEY, + SSH_NEW_CHANNEL, + SSH_OPEN_CHANNEL, + SSH_BOOTSTRAP, + SSH_READY, +}; + +struct tmate_ssh_client { + /* XXX The "session" word is used for three things: + * - the ssh session + * - the tmate sesssion + * - the tmux session + * A tmux session is associated 1:1 with a tmate session. + * An ssh session belongs to a tmate session, and a tmate session + * has one ssh session, except during bootstrapping where + * there is one ssh session per tmate server, and the first one wins. + */ + struct tmate_session *tmate_session; + TAILQ_ENTRY(tmate_ssh_client) node; + + char *server_ip; + + int state; + + /* + * ssh_callbacks is allocated because the libssh API sucks (userdata + * has to be in the struct itself). + */ + struct ssh_callbacks_struct ssh_callbacks; + char *tried_passphrase; + ssh_session session; + ssh_channel channel; + + struct event *ev_ssh; +}; +TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); + +extern void connect_ssh_client(struct tmate_ssh_client *client); +extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, + const char *server_ip); + +/* 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; + + /* True when the slave has sent all the environment variables */ + int tmate_env_ready; + + int min_sx; + int min_sy; + + /* + * This list contains one connection per IP. The first connected + * client wins, and saved in *client. When we have a winner, the + * losers are disconnected and killed. + */ + struct tmate_ssh_clients clients; + int need_passphrase; + char *passphrase; + + bool reconnected; + struct event *ev_connection_retry; + char *last_server_ip; + char *reconnection_data; + /* + * When we reconnect, instead of serializing the key bindings and + * options, we replay all the tmux commands we replicated. + * It may be a little innacurate to replicate the state, but + * it's much easier. + */ + struct { + unsigned int capacity; + unsigned int tail; + struct { + int argc; + char **argv; + } *cmds; + } saved_tmux_cmds; +}; + +extern struct tmate_session tmate_session; +extern void tmate_session_init(struct event_base *base); +extern void tmate_session_start(void); +extern void tmate_reconnect_session(struct tmate_session *session, const char *message); + +/* tmate-debug.c */ +extern void tmate_print_stack_trace(void); +extern void tmate_catch_sigsegv(void); +extern void tmate_preload_trace_lib(void); + +/* tmate-msg.c */ + +extern void __tmate_status_message(const char *fmt, va_list ap); +extern void printflike(1, 2) tmate_status_message(const char *fmt, ...); + +/* tmate-env.c */ + +extern int tmate_has_received_env(void); +extern void tmate_set_env(const char *name, const char *value); +extern void tmate_format(struct format_tree *ft); + +#endif diff --git a/tmux.1 b/tmux.1 index 43c20103..55f88c98 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,6 +1,6 @@ .\" $OpenBSD$ .\" -.\" Copyright (c) 2007 Nicholas Marriott +.\" 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 @@ -15,14 +15,15 @@ .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: March 25 2013 $ -.Dt TMUX 1 +.Dt TMATE 1 .Os .Sh NAME -.Nm tmux -.Nd terminal multiplexer +.Nm tmate +.Nd terminal multiplexer with instant terminal sharing .Sh SYNOPSIS -.Nm tmux +.Nm tmate .Bk -words +.Op show-messages .Op Fl 2CluvV .Op Fl c Ar shell-command .Op Fl f Ar file @@ -32,13 +33,24 @@ .Ek .Sh DESCRIPTION .Nm -is a terminal multiplexer: +is a terminal multiplexer with instant terminal sharing: it enables a number of terminals to be created, accessed, and -controlled from a single screen. +controlled from a single screen and be shared with another mates. .Nm may be detached from a screen and continue running in the background, -then later reattached. +then later reattached, like as a daemon. +.Pp +.Nm +provides an instant pairing solution, allowing you to share a terminal +with one or several teammates. Together with a voice call, it's almost like +pairing in person. The terminal sharing works by using SSH connections to +backend servers maintained by tmate upstream developers; teammates need to be +given a randomly-generated token to be able to join a session. +.Pp +.Nm +is a modified version of tmux, and uses the same configurations such as +keybindings, color schemes, etc. .Pp When .Nm @@ -47,9 +59,9 @@ is started it creates a new with a single .Em window and displays it on screen. -A status line at the bottom of the screen -shows information on the current session -and is used to enter interactive commands. +A status line at the bottom of the screen shows information +on the current session, such as ssh command to share with +your mate, and is used to enter interactive commands. .Pp A session is a single collection of .Em pseudo terminals @@ -61,7 +73,7 @@ A window occupies the entire screen and may be split into rectangular panes, each of which is a separate pseudo terminal (the -.Xr pty 7 +.Xr pty 4 manual page documents the technical details of pseudo terminals). Any number of .Nm @@ -80,7 +92,7 @@ key strokes). .Nm may be reattached using: .Pp -.Dl $ tmux attach +.Dl $ tmate attach .Pp In .Nm , @@ -98,6 +110,10 @@ The options are as follows: Force .Nm to assume the terminal supports 256 colours. +.It Fl a Ar file +Limit access to the public keys listed in the +.Ar file +given as argument. .It Fl C Start in control mode (see the .Sx CONTROL MODE @@ -126,7 +142,9 @@ By default, loads the system configuration file from .Pa @SYSCONFDIR@/tmux.conf , if present, then looks for a user configuration file at -.Pa ~/.tmux.conf . +.Pa ~/.tmux.conf +and +.Pa ~/.tmate.conf . .Pp The configuration file is a set of .Nm @@ -143,11 +161,10 @@ 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 , -.Ev TMPDIR -if it is unset, or +.Ev TMUX_TMPDIR +or .Pa /tmp -if both are unset. +if it is unset. The default socket is named .Em default . This option allows a different socket name to be specified, allowing several @@ -168,7 +185,9 @@ 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. +when using +.Nm +as a login shell. .It Fl S Ar socket-path Specify a full alternative path to the server socket. If @@ -191,13 +210,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. @@ -373,7 +390,7 @@ These specify the client, session, window or pane which a command should affect. .Pp .Ar target-client is the name of the -.Xr pty 7 +.Xr pty 4 file to which the client is connected, for example either of .Pa /dev/ttyp1 or @@ -423,6 +440,10 @@ 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 @@ -478,8 +499,11 @@ Each has a single-character alternative form. .El .Pp .Ar target-pane -may be a -pane ID or 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 or pane ID, for example: @@ -593,7 +617,7 @@ to be given as multiple arguments and executed directly (without This can avoid issues with shell quoting. For example: .Bd -literal -offset indent -$ tmux new-window vi /etc/passwd +$ tmate new-window vi /etc/passwd .Ed .Pp Will run @@ -612,7 +636,7 @@ bind-key F1 set-window-option force-width 81 Or if using .Xr sh 1 : .Bd -literal -offset indent -$ tmux bind-key F1 set-window-option force-width 81 +$ tmate bind-key F1 set-window-option force-width 81 .Ed .Pp Multiple commands may be specified together as part of a @@ -644,11 +668,11 @@ bind-key R source-file ~/.tmux.conf \e; \e Or from .Xr sh 1 : .Bd -literal -offset indent -$ tmux kill-window -t :1 +$ tmate kill-window -t :1 -$ tmux new-window \e; split-window -d +$ tmate new-window \e; split-window -d -$ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach +$ tmate new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach .Ed .Sh CLIENTS AND SESSIONS The @@ -717,12 +741,11 @@ will set the session working directory (used for new windows) to .Pp If .Fl E -is used, +is used, the .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 @@ -748,7 +771,7 @@ Kill the .Nm server and clients and destroy all sessions. .It Xo Ic kill-session -.Op Fl a +.Op Fl aC .Op Fl t Ar target-session .Xc Destroy the given session, closing any windows linked to it and no other @@ -756,6 +779,10 @@ 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 @@ -824,7 +851,7 @@ and specify the size of the initial window (80 by 24 if not given). .Pp If run from a terminal, any -.Xr termios 3 +.Xr termios 4 special characters are saved and used for new windows in the new session. .Pp The @@ -850,13 +877,12 @@ with .Ar target-session . This means they share the same set of windows - all windows from .Ar target-session -are linked to the new session and any subsequent new windows or windows being -closed are applied to both sessions. +are linked to the new session, any new windows are linked to both sessions and +any windows closed removed from both sessions. The current and previous window and any session options remain independent and either session may be killed without affecting the other. -Giving .Fl n -or +and .Ar shell-command are invalid if .Fl t @@ -872,10 +898,9 @@ but a different format may be specified with .Pp If .Fl E -is used, +is used, the .Ic update-environment option will not be applied. -.Ic update-environment . .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client @@ -895,7 +920,7 @@ is specified, only update the client's status bar. Rename the session to .Ar new-name . .It Xo Ic show-messages -.Op Fl IJT +.Op Fl JT .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) @@ -908,11 +933,10 @@ With .Fl t , display the log for .Ar target-client . -.Fl I , .Fl J and .Fl T -show debugging information about the running server, jobs and terminals. +show debugging information about jobs and terminals. .It Ic source-file Ar path .D1 (alias: Ic source ) Execute commands from @@ -1232,10 +1256,10 @@ command displays the layout of each window in a form suitable for use with .Ic select-layout . For example: .Bd -literal -offset indent -$ tmux list-windows +$ tmate list-windows 0: ksh [159x48] layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} -$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} +$ tmate select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} .Ed .Pp .Nm @@ -1249,7 +1273,7 @@ Commands related to windows and panes are as follows: .Op Fl dP .Op Fl F Ar format .Op Fl s Ar src-pane -.Op Fl t Ar dst-pane +.Op Fl t Ar dst-window .Xc .D1 (alias: Ic breakp ) Break @@ -1321,7 +1345,7 @@ interactively from a list. After a client is chosen, .Ql %% is replaced by the client -.Xr pty 7 +.Xr pty 4 path in .Ar template and the result executed as a command. @@ -2303,8 +2327,8 @@ User options may have any name, so long as they are prefixed with and be set to any string. For example: .Bd -literal -offset indent -$ tmux setw -q @foo "abc123" -$ tmux showw -v @foo +$ tmate setw -q @foo "abc123" +$ tmate showw -v @foo abc123 .Ed .Pp @@ -2390,7 +2414,8 @@ to work correctly, this .Em must be set to .Ql screen , -.Ql tmux +.Ql tmux , +.Ql tmate or a derivative of them. .It Ic escape-time Ar time Set the time in milliseconds for which @@ -2564,12 +2589,18 @@ 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 @@ -2660,14 +2691,21 @@ captures the mouse and allows mouse events to be bound as key bindings. See the .Sx MOUSE SUPPORT section for details. -.It Xo Ic mouse-utf8 -.Op Ic on | off -.Xc -If enabled, request mouse input as UTF-8 on UTF-8 terminals. .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 @@ -2779,12 +2817,6 @@ Examples are: #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp -By default, UTF-8 in -.Ar string -is not interpreted, to enable UTF-8, use the -.Ic status-utf8 -option. -.Pp The default is .Ql "[#S] " . .It Ic status-left-length Ar length @@ -2813,10 +2845,8 @@ 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. +.Xr strftime 3 +and character pairs are replaced. .It Ic status-right-length Ar length Set the maximum .Ar length @@ -2836,17 +2866,6 @@ For how to specify see the .Ic message-command-style option. -.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 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 @@ -2926,7 +2945,7 @@ and poor for interactive programs such as shells. .Op Ic on | off .Xc Allow programs to change the window name using a terminal escape -sequence (\\033k...\\033\\\\). +sequence (\eek...\ee\e\e). The default is on. .Pp .It Xo Ic alternate-screen @@ -3093,13 +3112,6 @@ 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. -.Pp .It Ic window-active-style Ar style Set the style for the window's active pane. For how to specify @@ -3228,6 +3240,68 @@ 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 alert-activity +Run when a window has activity. +See +.Ic monitor-activity . +.It alert-bell +Run when a window has received a bell. +.It alert-silence +Run when a window has been silent. +See +.Ic monitor-silence . +.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 @@ -3245,10 +3319,10 @@ for a pane border or 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 "" +.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" +.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" +.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" +.It Li "WheelUp" Ta "WheelDown" Ta "" Ta "" .El .Pp Each should be suffixed with a location, for example @@ -3323,12 +3397,42 @@ if 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. +a number and a colon. +Positive numbers count from the start of the string and negative from the end, +so +.Ql #{=5:pane_title} +will include at most the first 5 characters of the pane title, or +.Ql #{=-5:pane_title} +the last 5 characters. +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 #() . @@ -3356,11 +3460,10 @@ The following variables are available, where appropriate: .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_activity_string" Ta "" Ta "String time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" -.It Li "client_created_string" Ta "" Ta "String 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" @@ -3370,6 +3473,7 @@ The following variables are available, where appropriate: .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" @@ -3385,7 +3489,6 @@ The following variables are available, where appropriate: .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 "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 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" @@ -3410,14 +3513,12 @@ The following variables are available, where appropriate: .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_activity_string" Ta "" Ta "String time of session last activity" .It Li "session_created" Ta "" Ta "Integer time session created" -.It Li "session_created_string" Ta "" Ta "String time session created" .It Li "session_last_attached" Ta "" Ta "Integer time session last attached" -.It Li "session_last_attached_string" Ta "" Ta "String 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" @@ -3426,10 +3527,10 @@ The following variables are available, where appropriate: .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_activity_string" Ta "" Ta "String time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" -.It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" .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" @@ -3437,11 +3538,12 @@ The following variables are available, where appropriate: .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" +.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" @@ -3951,7 +4053,7 @@ This command only works from outside .El .Sh TERMINFO EXTENSIONS .Nm -understands some extensions to +understands some unofficial extensions to .Xr terminfo 5 : .Bl -tag -width Ds .It Em Cs , Cr @@ -3975,10 +4077,12 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Tc +Indicate that the terminal supports the +.Ql direct colour +RGB escape sequence (for example, \ee[38;2;255;255;255m). .It Em \&Ms -This sequence can be used by -.Nm -to store the current buffer in the host terminal's selection (clipboard). +Store the current buffer in the host terminal's selection (clipboard). See the .Em set-clipboard option above and the @@ -4032,12 +4136,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 @@ -4085,18 +4193,18 @@ To create a new session running .Xr vi 1 : .Pp -.Dl $ tmux new-session vi +.Dl $ tmate new-session vi .Pp Most commands have a shorter form, known as an alias. For new-session, this is .Ic new : .Pp -.Dl $ tmux new vi +.Dl $ tmate new vi .Pp Alternatively, the shortest unambiguous form of a command is accepted. If there are several options, they are listed: .Bd -literal -offset indent -$ tmux n +$ tmate n ambiguous command: n, could be: new-session, new-window, next-window .Ed .Pp @@ -4126,7 +4234,7 @@ A session may be detached using .Xr ssh 1 disconnection) and reattached with: .Pp -.Dl $ tmux attach-session +.Dl $ tmate attach-session .Pp Typing .Ql C-b \&? @@ -4169,6 +4277,6 @@ bind-key / command-prompt "split-window 'exec man %%'" bind-key S command-prompt "new-window -n %1 'ssh %1'" .Ed .Sh SEE ALSO -.Xr pty 7 +.Xr pty 4 .Sh AUTHORS -.An Nicholas Marriott Aq Mt nicm@users.sourceforge.net +.An Nicholas Marriott Aq Mt nicholas.marriott@gmail.com diff --git a/tmux.c b/tmux.c index dff7952c..730c2dfc 100644 --- a/tmux.c +++ b/tmux.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -19,9 +19,12 @@ #include #include +#include #include #include #include +#include +#include #include #include #include @@ -30,50 +33,48 @@ #include #include "tmux.h" +#include "tmate.h" -#if defined(DEBUG) && defined(__OpenBSD__) -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 hooks *global_hooks; -struct options global_options; /* server options */ -struct options global_s_options; /* session options */ -struct options global_w_options; /* window options */ -struct environ global_environ; - -char *shell_cmd; -int debug_level; -time_t start_time; -char socket_path[PATH_MAX]; +struct timeval start_time; +const char *socket_path; __dead void usage(void); -char *makesocketpath(const char *); +static char *make_label(const char *); #ifndef HAVE___PROGNAME -char *__progname = (char *) "tmux"; +char *__progname = (char *) "tmate"; +#endif + +#ifdef TMATE +int tmate_foreground; #endif __dead void usage(void) { fprintf(stderr, - "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" - " [-S socket-path] [command [flags]]\n", - __progname); + "Usage: %s [options] [tmux-command [flags]]\n" + "\n" + "Basic options:\n" + " -n specify the session token instead of getting a random one\n" + " -r same, but for the read-only token\n" + " -k specify an api-key, necessary for using named sessions on tmate.io\n" + " -F set the foreground mode, useful for setting remote access\n" + " -f set the config file path\n" + " -S set the socket path, useful to issue commands to a running tmate instance\n" + " -a limit access to ssh public keys listed in provided file\n" + " -v set verbosity (can be repeated)\n" + " -V print version\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(path); - free(path); - } -} - const char * getshell(void) { @@ -120,40 +121,60 @@ areshell(const char *shell) return (0); } -char * -makesocketpath(const char *label) +static char * +make_label(const char *label) { - char base[PATH_MAX], realbase[PATH_MAX], *path, *s; - struct stat sb; - u_int uid; + char *base, resolved[PATH_MAX], *path, *s; + struct stat sb; + u_int uid; + int saved_errno; +#ifdef TMATE + int do_random_label = label == NULL; +#endif + + if (label == NULL) + label = "default"; uid = getuid(); + if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); - else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); + xasprintf(&base, "%s/tmate-%u", s, uid); else - xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); + xasprintf(&base, "%s/tmate-%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_IRWXO) != 0) { errno = EACCES; - return (NULL); + goto fail; } - if (realpath(base, realbase) == NULL) - strlcpy(realbase, base, sizeof realbase); +#ifdef TMATE + if (do_random_label) + label = "XXXXXX"; +#endif - xasprintf(&path, "%s/%s", realbase, label); + if (realpath(base, resolved) == NULL) + strlcpy(resolved, base, sizeof resolved); + xasprintf(&path, "%s/%s", resolved, label); +#ifdef TMATE + if (do_random_label) + mktemp(path); +#endif return (path); + +fail: + saved_errno = errno; + free(base); + errno = saved_errno; + return (NULL); } void @@ -191,15 +212,44 @@ find_home(void) return (home); } +#ifdef TMATE +static char *api_key; +static char *session_name; +static char *session_name_ro; +static char *authorized_keys; + +void tmate_load_cli_options(void) +{ +#define SET_OPT(name, val) ({\ + if (val) { \ + run_headless_command(3, (const char *[]){"set-option", name, val}, DEFER_ERRORS_CFG, NULL); \ + free(val); \ + val = NULL; \ + } \ +}) + SET_OPT("tmate-api-key", api_key); + SET_OPT("tmate-session-name", session_name); + SET_OPT("tmate-session-name-ro", session_name_ro); + SET_OPT("tmate-authorized-keys", authorized_keys); +#undef SET_OPT +} +#endif + int main(int argc, char **argv) { - char *s, *path, *label, **var, tmp[PATH_MAX]; - int opt, flags, 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 + if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && + setlocale(LC_CTYPE, "C.UTF-8") == NULL) { + if (setlocale(LC_CTYPE, "") == NULL) + errx(1, "invalid LC_ALL, LC_CTYPE or LANG"); + s = nl_langinfo(CODESET); + if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0) + errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s); + } setlocale(LC_TIME, ""); tzset(); @@ -209,15 +259,20 @@ main(int argc, char **argv) else flags = 0; +#ifdef TMATE + tmate_catch_sigsegv(); + flags |= CLIENT_256COLOURS | CLIENT_UTF8; +#endif + label = path = NULL; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { + while ((opt = getopt(argc, argv, "h2c:CdFf:lL:qS:uUVvk:n:r:a:")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; break; case 'c': - free(shell_cmd); - shell_cmd = xstrdup(optarg); + free(shellcmd); + shellcmd = xstrdup(optarg); break; case 'C': if (flags & CLIENT_CONTROL) @@ -248,8 +303,26 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; break; case 'v': - debug_level++; + log_add_level(); break; + case 'F': + tmate_foreground = 1; + log_add_level(); + unsetenv("TMUX"); + break; + case 'k': + api_key = xstrdup(optarg); + break; + case 'n': + session_name = xstrdup(optarg); + break; + case 'r': + session_name_ro = xstrdup(optarg); + break; + case 'a': + authorized_keys = xstrdup(optarg); + break; + case 'h': default: usage(); } @@ -257,49 +330,54 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (shell_cmd != NULL && argc != 0) + if (shellcmd != NULL && argc != 0) usage(); - if (!(flags & CLIENT_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)) +#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", tmp); + environ_set(global_environ, "PWD", "%s", tmp); - options_init(&global_options, NULL); - options_table_populate_tree(server_options_table, &global_options); + 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 & CLIENT_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) { @@ -309,52 +387,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); + 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. */ - if (path == NULL) { - /* If no -L, use the environment. */ - if (label == NULL) { - s = getenv("TMUX"); - if (s != NULL) { - path = xstrdup(s); - path[strcspn (path, ",")] = '\0'; - if (*path == '\0') { - free(path); - label = xstrdup("default"); - } - } else - label = xstrdup("default"); - } - - /* -L or default set. */ - if (label != NULL) { - if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket: %s\n", - strerror(errno)); - exit(1); - } + if (path == NULL && label == NULL) { + s = getenv("TMUX"); + if (s != NULL && *s != '\0' && *s != ',') { + path = xstrdup(s); + path[strcspn (path, ",")] = '\0'; } } - free(label); - - if (strlcpy(socket_path, path, sizeof socket_path) >= - sizeof socket_path) { - fprintf(stderr, "socket path too long: %s\n", path); + if (path == NULL && (path = make_label(label)) == NULL) { + fprintf(stderr, "can't create socket: %s\n", strerror(errno)); exit(1); } - free(path); - -#ifdef HAVE_SETPROCTITLE - /* Set process title. */ - setproctitle("%s (%s)", __progname, socket_path); -#endif + socket_path = path; + free(label); /* Pass control to the client. */ - exit(client_main(osdep_event_init(), argc, argv, flags)); + exit(client_main(event_init(), argc, argv, flags, shellcmd)); } diff --git a/tmux.h b/tmux.h index 4d19a5ab..b38615b6 100644 --- a/tmux.h +++ b/tmux.h @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -19,6 +19,8 @@ #ifndef TMUX_H #define TMUX_H +#define TMATE + #define PROTOCOL_VERSION 8 #include @@ -29,18 +31,31 @@ #include #include #include +#include #ifdef HAVE_UTEMPTER #include #endif #include "compat.h" +#include "xmalloc.h" extern char *__progname; extern char **environ; +struct client; +struct environ; +struct input_ctx; +struct mouse_event; +struct options; +struct session; +struct tmuxpeer; +struct tmuxproc; + /* Default global configuration file. */ +#ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf" +#endif /* * Minimum layout cell size, NOT including separator line. The scroll region @@ -51,12 +66,6 @@ extern char **environ; /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 -/* - * UTF-8 data size. This must be big enough to hold combined characters as well - * as single. - */ -#define UTF8_SIZE 9 - /* * 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 @@ -67,13 +76,6 @@ extern char **environ; #define READ_BACKOFF 512 #define READ_TIME 100 -/* 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)) - /* Attribute to make gcc check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) @@ -89,13 +91,14 @@ extern char **environ; #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_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) @@ -115,8 +118,14 @@ extern char **environ; { #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 key_code { +enum { /* Focus events. */ KEYC_FOCUS_IN = KEYC_BASE, KEYC_FOCUS_OUT, @@ -132,6 +141,9 @@ enum key_code { KEYC_MOUSE_KEY(MOUSEDRAG1), KEYC_MOUSE_KEY(MOUSEDRAG2), KEYC_MOUSE_KEY(MOUSEDRAG3), + KEYC_MOUSE_KEY(MOUSEDRAGEND1), + KEYC_MOUSE_KEY(MOUSEDRAGEND2), + KEYC_MOUSE_KEY(MOUSEDRAGEND3), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), @@ -383,6 +395,7 @@ enum tty_code_code { TTYC_SMSO, /* enter_standout_mode, so */ TTYC_SMUL, /* enter_underline_mode, us */ TTYC_SS, /* set cursor style, Ss */ + TTYC_TC, /* 24-bit "true" colour, Tc */ TTYC_TSL, /* to_status_line, tsl */ TTYC_VPA, /* row_address, cv */ TTYC_XENL, /* eat_newline_glitch, xn */ @@ -396,11 +409,12 @@ enum msgtype { MSG_IDENTIFY_FLAGS = 100, MSG_IDENTIFY_TERM, MSG_IDENTIFY_TTYNAME, - MSG_IDENTIFY_CWD, + MSG_IDENTIFY_OLDCWD, /* unused */ MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, MSG_IDENTIFY_CLIENTPID, + MSG_IDENTIFY_CWD, MSG_COMMAND = 200, MSG_DETACH, @@ -563,7 +577,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; @@ -604,14 +618,23 @@ struct mode_key_table { #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 attributes. */ @@ -628,46 +651,86 @@ struct utf8_data { #define GRID_FLAG_FG256 0x1 #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 +#define GRID_FLAG_EXTENDED 0x8 +#define GRID_FLAG_FGRGB 0x10 +#define GRID_FLAG_BGRGB 0x20 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 +/* Grid cell RGB colours. */ +struct grid_cell_rgb { + u_char r; + u_char g; + u_char b; +}; + /* Grid cell data. */ struct grid_cell { - u_char attr; - u_char flags; - u_char fg; - u_char bg; + u_char flags; + u_char attr; + union { + u_char fg; + struct grid_cell_rgb fg_rgb; + }; + union { + u_char bg; + struct grid_cell_rgb bg_rgb; + }; + 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, @@ -682,11 +745,6 @@ struct options_entry { RB_ENTRY(options_entry) entry; }; -struct options { - RB_HEAD(options_tree, options_entry) tree; - struct options *parent; -}; - /* Scheduled job. */ struct job { enum { @@ -769,15 +827,12 @@ struct screen_write_ctx { * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ -struct client; -struct session; -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 client *, struct session *, - int, struct mouse_event *); + key_code, struct mouse_event *); }; /* Structures for choose mode. */ @@ -803,7 +858,6 @@ struct window_choose_data { }; /* Child window structure. */ -struct input_ctx; struct window_pane { u_int id; u_int active_point; @@ -831,7 +885,7 @@ struct window_pane { int argc; char **argv; char *shell; - int cwd; + const char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; @@ -849,6 +903,10 @@ struct window_pane { struct bufferevent *pipe_event; size_t pipe_off; +#ifdef TMATE + size_t tmate_off; +#endif + struct screen *screen; struct screen base; @@ -879,6 +937,9 @@ struct window { struct timeval activity_time; +#ifdef TMATE + struct window_pane *tmate_last_sync_active_pane; +#endif struct window_pane *active; struct window_pane *last; struct window_panes panes; @@ -901,7 +962,7 @@ struct window { #define WINDOW_FORCEHEIGHT 0x4000 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) - struct options options; + struct options *options; u_int references; @@ -965,7 +1026,6 @@ struct environ_entry { RB_ENTRY(environ_entry) entry; }; -RB_HEAD(environ, environ_entry); /* Client session. */ struct session_group { @@ -979,7 +1039,7 @@ struct session { u_int id; char *name; - int cwd; + const char *cwd; struct timeval creation_time; struct timeval last_attached_time; @@ -995,16 +1055,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; @@ -1033,31 +1095,31 @@ RB_HEAD(sessions, session); /* Mouse input. */ struct mouse_event { - int valid; + int valid; - int key; - int statusat; + key_code key; + int statusat; - u_int x; - u_int y; - u_int b; + u_int x; + u_int y; + u_int b; - u_int lx; - u_int ly; - u_int lb; + u_int lx; + u_int ly; + u_int lb; - int s; - int w; - int wp; + int s; + int w; + int wp; - u_int sgr_type; - u_int sgr_b; + 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; @@ -1105,8 +1167,6 @@ struct tty { int fd; struct bufferevent *event; - int log_fd; - struct termios tio; struct grid_cell cell; @@ -1133,7 +1193,7 @@ struct tty { struct tty_key *key_tree; }; -/* TTY command context and function pointer. */ +/* TTY command context. */ struct tty_ctx { struct window_pane *wp; @@ -1171,7 +1231,7 @@ struct message_entry { /* Client connection. */ struct client { - struct imsgbuf ibuf; + struct tmuxpeer *peer; pid_t pid; int fd; @@ -1181,10 +1241,10 @@ struct client { struct timeval creation_time; struct timeval activity_time; - struct environ environ; + struct environ *environ; char *title; - int cwd; + const char *cwd; char *term; char *ttyname; @@ -1209,7 +1269,7 @@ struct client { #define CLIENT_STATUS 0x10 #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 @@ -1222,6 +1282,10 @@ struct client { #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; @@ -1266,6 +1330,39 @@ struct args { 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. */ struct cmd { const struct cmd_entry *entry; @@ -1308,6 +1405,8 @@ struct cmd_q { int references; int flags; #define CMD_Q_DEAD 0x1 +#define CMD_Q_REENTRY 0x2 +#define CMD_Q_NOHOOKS 0x4 struct client *client; int client_exit; @@ -1315,6 +1414,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; @@ -1325,27 +1428,56 @@ struct cmd_q { 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_READONLY 0x2 int flags; - enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); + enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; /* Key binding and key table. */ struct key_binding { - int key; + key_code key; struct cmd_list *cmdlist; int can_repeat; @@ -1378,19 +1510,26 @@ enum options_table_type { 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; + const char *style; }; /* Common command usages. */ @@ -1405,29 +1544,46 @@ struct options_table_entry { #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 char *shell_cmd; -extern int debug_level; -extern time_t start_time; -extern char socket_path[PATH_MAX]; -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; +#ifdef TMATE +extern int tmate_foreground; +void tmate_load_cli_options(void); +#endif const char *getshell(void); int checkshell(const char *); int areshell(const char *); void setblocking(int, int); 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 int cfg_finished; extern int cfg_references; extern struct client *cfg_client; +extern char **cfg_causes; +extern u_int cfg_ncauses; void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); void set_cfg_file(const char *); -void cfg_add_cause(const char *, ...); +void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); @@ -1442,18 +1598,16 @@ 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 *, int); +char *paste_make_sample(struct paste_buffer *); /* format.c */ #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 struct format_tree; -struct format_tree *format_create(void); -struct format_tree *format_create_flags(int); +struct format_tree *format_create(struct cmd_q *, int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); -const char *format_find(struct format_tree *, const char *); char *format_expand_time(struct format_tree *, const char *, time_t); char *format_expand(struct format_tree *, const char *); void format_defaults(struct format_tree *, struct client *, @@ -1462,7 +1616,23 @@ 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 *, int); + 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[]; @@ -1481,7 +1651,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); @@ -1497,10 +1668,10 @@ 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 *); @@ -1515,37 +1686,36 @@ struct options_entry *options_set_style(struct options *, const char *, 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 *, int, +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 *, @@ -1610,10 +1780,10 @@ 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); @@ -1624,9 +1794,9 @@ const char *tty_term_describe(struct tty_term *, enum tty_code_code); 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 *); +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 *); @@ -1634,7 +1804,7 @@ 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); @@ -1642,19 +1812,25 @@ long long args_strtonum(struct args *, u_char, long long, long long, char **); /* cmd-find.c */ -struct session *cmd_find_current(struct cmd_q *); -struct session *cmd_find_session(struct cmd_q *, const char *, int); -struct winlink *cmd_find_window(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_window_marked(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, - struct window_pane **); -struct winlink *cmd_find_pane_marked(struct cmd_q *, const char *, - struct session **, struct window_pane **); +int cmd_find_current(struct cmd_find_state *, struct cmd_q *, + int); +int cmd_find_target(struct cmd_find_state *, + 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); -int cmd_find_index(struct cmd_q *, const char *, - struct session **); +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_winlink(struct cmd_find_state *, + struct session *, struct winlink *); +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); @@ -1663,7 +1839,9 @@ 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); +int cmd_prepare_state(struct cmd *, struct cmd_q *, + 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 **); @@ -1673,13 +1851,13 @@ char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ -enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, - const char *, 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 *); @@ -1699,10 +1877,16 @@ 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(struct event_base *, int, char **, int); +#define DEFER_ERRORS_CFG 1 +int run_headless_command(int argc, const char **argv, int flags, void (*err_callback)(const char *)); +void run_initial_client_cmd(void); +int client_main(struct event_base *, int, char **, int, const char *); /* key-bindings.c */ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); @@ -1710,28 +1894,29 @@ 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); +struct key_table *key_bindings_get_table(const char *, int); void key_bindings_unref_table(struct key_table *); -void key_bindings_add(const char *, int, int, struct cmd_list *); -void key_bindings_remove(const char *, int); +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 *, 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 int server_exit; +extern struct tmuxproc *server_proc; extern struct clients clients; -extern struct session *marked_session; -extern struct winlink *marked_winlink; -extern struct window_pane *marked_window_pane; +extern struct cmd_find_state marked_pane; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); @@ -1743,22 +1928,21 @@ void server_update_socket(void); void server_add_accept(int); /* server-client.c */ +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 *, int); +void server_client_handle_key(struct client *, key_code); void server_client_create(int); 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_detach(struct client *, enum msgtype); void server_client_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 *); @@ -1775,15 +1959,12 @@ 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 *); @@ -1801,7 +1982,7 @@ 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); @@ -1812,16 +1993,16 @@ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); -void input_reset(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, 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); @@ -1845,9 +2026,8 @@ 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); @@ -1855,21 +2035,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); @@ -1883,24 +2056,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 printflike(2, 3) screen_write_cstrlen(int, const char *, ...); -void printflike(5, 6) screen_write_cnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, ...); -size_t printflike(2, 3) screen_write_strlen(int, const char *, ...); +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 printflike(5, 6) 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_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); @@ -1979,8 +2152,8 @@ 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 *, int, char **, const char *, - const char *, int, struct environ *, struct termios *, - u_int, u_int, u_int, 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); struct window_pane *window_find_string(struct window *, const char *); @@ -2007,21 +2180,21 @@ 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 *); int window_pane_spawn(struct window_pane *, int, char **, - const char *, const char *, int, struct environ *, + 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 client *, - struct session *, int, struct mouse_event *); + 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 *); @@ -2037,17 +2210,17 @@ 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); @@ -2056,8 +2229,8 @@ void layout_resize_pane(struct window_pane *, enum layout_type, void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); 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 */ @@ -2082,6 +2255,7 @@ 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 *); 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; @@ -2110,7 +2284,7 @@ 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 */ @@ -2140,8 +2314,8 @@ 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 *, int, char **, const char *, - int, struct environ *, struct termios *, int, u_int, - u_int, 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 *); @@ -2149,7 +2323,7 @@ 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 *, int, char **, - const char *, int, 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 *); @@ -2171,17 +2345,20 @@ void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ -void utf8_build(void); void utf8_set(struct utf8_data *, u_char); -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 *); +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); +enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *); +enum utf8_state utf8_split(wchar_t, struct utf8_data *); 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_rtrimcstr(const char *, u_int); char *utf8_trimcstr(const char *, u_int); +char *utf8_padcstr(const char *, u_int); /* osdep-*.c */ char *osdep_get_name(int, char *); @@ -2189,22 +2366,18 @@ char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); /* log.c */ -void log_open(const char *); -void log_close(void); -void printflike(1, 2) log_debug(const char *, ...); -__dead void printflike(1, 2) log_fatal(const char *, ...); -__dead void printflike(1, 2) log_fatalx(const char *, ...); - -/* xmalloc.c */ -char *xstrdup(const char *); -void *xcalloc(size_t, size_t); -void *xmalloc(size_t); -void *xrealloc(void *, size_t); -void *xreallocarray(void *, size_t, size_t); -int printflike(2, 3) xasprintf(char **, const char *, ...); -int xvasprintf(char **, const char *, va_list); -int printflike(3, 4) xsnprintf(char *, size_t, const char *, ...); -int xvsnprintf(char *, size_t, const char *, va_list); +void log_add_level(void); +int log_get_level(void); +void log_open_fp(FILE *f); +void log_open(const char *); +void log_close(void); +#define LOG_ERROR 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#define log_debug(...) log_emit(LOG_DEBUG+1, __VA_ARGS__) +void printflike(2, 3) log_emit(int level, const char *, ...); +__dead void printflike(1, 2) fatal(const char *, ...); +__dead void printflike(1, 2) fatalx(const char *, ...); /* style.c */ int style_parse(const struct grid_cell *, diff --git a/tty-acs.c b/tty-acs.c index 5d03c3eb..7fd265d4 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 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 diff --git a/tty-keys.c b/tty-keys.c index c1de2ab7..105f99f7 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -33,11 +33,11 @@ * 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 *); @@ -45,7 +45,7 @@ int tty_keys_mouse(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[] = { /* @@ -165,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. */ @@ -317,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; @@ -325,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; @@ -344,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. */ @@ -444,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. */ @@ -464,19 +464,25 @@ 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; + wchar_t wc; /* 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); @@ -529,14 +535,35 @@ 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; + + if (utf8_combine(&ud, &wc) != UTF8_DONE) + goto discard_key; + key = wc; + + 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; /* @@ -564,7 +591,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; @@ -578,7 +605,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); @@ -598,13 +625,13 @@ 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 %#x", (int) size, buf, key); + log_debug("discard key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); @@ -614,7 +641,7 @@ discard_key: /* 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; @@ -632,8 +659,7 @@ 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_b; + u_int i, x, y, b, sgr_b; u_char sgr_type, c; /* @@ -664,8 +690,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. */ @@ -673,30 +699,13 @@ 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); diff --git a/tty-term.c b/tty-term.c index 550fd16d..a3a26369 100644 --- a/tty-term.c +++ b/tty-term.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -18,9 +18,9 @@ #include -#ifdef HAVE_CURSES_H +#if defined(HAVE_CURSES_H) #include -#else +#elif defined(HAVE_NCURSES_H) #include #endif #include @@ -254,6 +254,7 @@ const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, @@ -409,12 +410,12 @@ tty_term_find(char *name, int fd, char **cause) 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"); @@ -460,7 +461,7 @@ tty_term_find(char *name, int fd, char **cause) } /* Apply terminal overrides. */ - s = options_get_string(&global_options, "terminal-overrides"); + s = options_get_string(global_options, "terminal-overrides"); tty_term_override(term, s); /* Delete curses data. */ @@ -562,7 +563,7 @@ 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); } @@ -597,7 +598,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); } @@ -607,7 +608,7 @@ 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); } diff --git a/tty.c b/tty.c index 7be952c8..bbde3541 100644 --- a/tty.c +++ b/tty.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -31,11 +31,20 @@ #include "tmux.h" +static int tty_log_fd = -1; + void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); +static int tty_same_fg(const struct grid_cell *, const struct grid_cell *); +static int tty_same_bg(const struct grid_cell *, const struct grid_cell *); +static int tty_same_colours(const struct grid_cell *, const struct grid_cell *); +static int tty_is_fg(const struct grid_cell *, int); +static int tty_is_bg(const struct grid_cell *, int); + void tty_set_italics(struct tty *); int tty_try_256(struct tty *, u_char, const char *); +int tty_try_rgb(struct tty *, const struct grid_cell_rgb *, const char *); void tty_colours(struct tty *, const struct grid_cell *); void tty_check_fg(struct tty *, struct grid_cell *); @@ -46,8 +55,8 @@ 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 *, const struct window_pane *); @@ -59,6 +68,86 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) +static int +tty_same_fg(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + int flags1, flags2; + + flags1 = (gc1->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); + flags2 = (gc2->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); + + if (flags1 != flags2) + return (0); + + if (flags1 & GRID_FLAG_FGRGB) { + if (gc1->fg_rgb.r != gc2->fg_rgb.r) + return (0); + if (gc1->fg_rgb.g != gc2->fg_rgb.g) + return (0); + if (gc1->fg_rgb.b != gc2->fg_rgb.b) + return (0); + return (1); + } + return (gc1->fg == gc2->fg); +} + +static int +tty_same_bg(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + int flags1, flags2; + + flags1 = (gc1->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); + flags2 = (gc2->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); + + if (flags1 != flags2) + return (0); + + if (flags1 & GRID_FLAG_BGRGB) { + if (gc1->bg_rgb.r != gc2->bg_rgb.r) + return (0); + if (gc1->bg_rgb.g != gc2->bg_rgb.g) + return (0); + if (gc1->bg_rgb.b != gc2->bg_rgb.b) + return (0); + return (1); + } + return (gc1->bg == gc2->bg); +} + +static int +tty_same_colours(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + return (tty_same_fg(gc1, gc2) && tty_same_bg(gc1, gc2)); +} + +static int +tty_is_fg(const struct grid_cell *gc, int c) +{ + if (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)) + return (0); + return (gc->fg == c); +} + +static int +tty_is_bg(const struct grid_cell *gc, int c) +{ + if (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)) + return (0); + return (gc->bg == c); +} + +void +tty_create_log(void) +{ + char name[64]; + + xsnprintf(name, sizeof name, "tmate-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) { @@ -68,7 +157,6 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) return (-1); memset(tty, 0, sizeof *tty); - tty->log_fd = -1; if (term == NULL || *term == '\0') tty->termname = xstrdup("unknown"); @@ -139,17 +227,6 @@ tty_set_size(struct tty *tty, u_int sx, u_int sy) { int 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, cause); if (tty->term == NULL) { tty_close(tty); @@ -159,8 +236,8 @@ tty_open(struct tty *tty, 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); @@ -170,7 +247,7 @@ tty_open(struct tty *tty, 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; @@ -179,8 +256,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) { } @@ -228,8 +305,8 @@ tty_start_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) { - if (options_get_number(&global_options, "focus-events")) { + 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"); } @@ -293,7 +370,7 @@ tty_stop_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) { + if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { tty->flags &= ~TTY_FOCUS; tty_raw(tty, "\033[?1004l"); @@ -308,11 +385,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); @@ -338,10 +410,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 @@ -408,8 +478,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 @@ -440,16 +510,16 @@ 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; } @@ -459,7 +529,7 @@ 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"); + s = options_get_string(global_options, "default-terminal"); if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { tty_putcode(tty, TTYC_SITM); return; @@ -496,7 +566,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; - if (s != NULL && strcmp(s->ccolour, tty->ccolour)) + if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) @@ -526,21 +596,15 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } 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_BUTTON) tty_puts(tty, "\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) @@ -550,10 +614,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) 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) { @@ -596,7 +657,7 @@ 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; @@ -661,10 +722,8 @@ void tty_draw_line(struct tty *tty, const struct window_pane *wp, struct screen *s, u_int py, u_int ox, u_int oy) { - const struct grid_cell *gc; + struct grid_cell gc; struct grid_line *gl; - struct grid_cell tmpgc; - struct utf8_data ud; u_int i, sx; int flags; @@ -691,18 +750,13 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, 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, wp); - } else - tty_cell(tty, gc, wp); + } + tty_cell(tty, &gc, wp); } if (sx < tty->sx) { @@ -1083,7 +1137,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) { /* @@ -1100,7 +1154,7 @@ 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, wp); } @@ -1160,8 +1214,7 @@ void 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 && @@ -1176,23 +1229,22 @@ tty_cell(struct tty *tty, const struct grid_cell *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 @@ -1211,8 +1263,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); } @@ -1446,12 +1498,10 @@ void tty_colours(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; - u_char fg = gc->fg, bg = gc->bg, flags = gc->flags; int have_ax, fg_default, bg_default; /* No changes? Nothing is necessary. */ - if (fg == tc->fg && bg == tc->bg && - ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0) + if (tty_same_colours(gc, tc)) return; /* @@ -1460,8 +1510,8 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) * case if only one is default need to fall onward to set the other * colour. */ - fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256)); - bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256)); + fg_default = tty_is_fg(gc, 8); + bg_default = tty_is_bg(gc, 8); if (fg_default || bg_default) { /* * If don't have AX but do have op, send sgr0 (op can't @@ -1474,48 +1524,54 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { - if (fg_default && - (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) { + if (fg_default && !tty_is_fg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[39m"); - else if (tc->fg != 7 || - tc->flags & GRID_FLAG_FG256) + else if (!tty_is_fg(tc, 7)) tty_putcode1(tty, TTYC_SETAF, 7); tc->fg = 8; - tc->flags &= ~GRID_FLAG_FG256; + tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); } - if (bg_default && - (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) { + if (bg_default && !tty_is_bg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[49m"); - else if (tc->bg != 0 || - tc->flags & GRID_FLAG_BG256) + else if (!tty_is_bg(tc, 0)) tty_putcode1(tty, TTYC_SETAB, 0); tc->bg = 8; - tc->flags &= ~GRID_FLAG_BG256; + tc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); } } } /* Set the foreground colour. */ - if (!fg_default && (fg != tc->fg || - ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)))) + if (!fg_default && !tty_same_fg(gc, tc)) tty_colours_fg(tty, gc); /* * Set the background colour. This must come after the foreground as * tty_colour_fg() can call tty_reset(). */ - if (!bg_default && (bg != tc->bg || - ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)))) + if (!bg_default && !tty_same_bg(gc, tc)) tty_colours_bg(tty, gc); } void tty_check_fg(struct tty *tty, struct grid_cell *gc) { - u_int colours; + struct grid_cell_rgb *rgb = &gc->fg_rgb; + u_int colours; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_FGRGB) { + /* Not a 24-bit terminal? Translate to 256-colour palette. */ + if (!tty_term_flag(tty->term, TTYC_TC)) { + gc->flags &= ~GRID_FLAG_FGRGB; + gc->flags |= GRID_FLAG_FG256; + gc->fg = colour_find_rgb(rgb->r, rgb->g, rgb->b); + } + else + return; + } colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ @@ -1547,8 +1603,20 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) void tty_check_bg(struct tty *tty, struct grid_cell *gc) { - u_int colours; + struct grid_cell_rgb *rgb = &gc->bg_rgb; + u_int colours; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_BGRGB) { + /* Not a 24-bit terminal? Translate to 256-colour palette. */ + if (!tty_term_flag(tty->term, TTYC_TC)) { + gc->flags &= ~GRID_FLAG_BGRGB; + gc->flags |= GRID_FLAG_BG256; + gc->bg = colour_find_rgb(rgb->r, rgb->g, rgb->b); + } + else + return; + } colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ @@ -1583,12 +1651,21 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) u_char fg = gc->fg; char s[32]; + tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); + + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_FGRGB) { + if (tty_try_rgb(tty, &gc->fg_rgb, "38") == 0) + goto save_fg; + /* Should not get here, already converted in tty_check_fg. */ + return; + } + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { - /* Try as 256 colours. */ if (tty_try_256(tty, fg, "38") == 0) goto save_fg; - /* Else already handled by tty_check_fg. */ + /* Should not get here, already converted in tty_check_fg. */ return; } @@ -1604,9 +1681,12 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) save_fg: /* Save the new values in the terminal current cell. */ - tc->fg = fg; - tc->flags &= ~GRID_FLAG_FG256; - tc->flags |= gc->flags & GRID_FLAG_FG256; + if (gc->flags & GRID_FLAG_FGRGB) + memcpy(&tc->fg_rgb, &gc->fg_rgb, sizeof tc->fg_rgb); + else + tc->fg = fg; + tc->flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_FG256); + tc->flags |= (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); } void @@ -1616,12 +1696,19 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) u_char bg = gc->bg; char s[32]; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_BGRGB) { + if (tty_try_rgb(tty, &gc->bg_rgb, "48") == 0) + goto save_bg; + /* Should not get here, already converted in tty_check_bg. */ + return; + } + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { - /* Try as 256 colours. */ if (tty_try_256(tty, bg, "48") == 0) goto save_bg; - /* Else already handled by tty_check_bg. */ + /* Should not get here, already converted in tty_check_bg. */ return; } @@ -1637,9 +1724,12 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) save_bg: /* Save the new values in the terminal current cell. */ - tc->bg = bg; - tc->flags &= ~GRID_FLAG_BG256; - tc->flags |= gc->flags & GRID_FLAG_BG256; + if (gc->flags & GRID_FLAG_BGRGB) + memcpy(&tc->bg_rgb, &gc->bg_rgb, sizeof tc->bg_rgb); + else + tc->bg = bg; + tc->flags &= ~(GRID_FLAG_BGRGB|GRID_FLAG_BG256); + tc->flags |= (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); } int @@ -1647,6 +1737,13 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) { char s[32]; + /* + * 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; + /* * If the terminfo entry has 256 colours and setaf and setab exist, * assume that they work correctly. @@ -1664,13 +1761,6 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) return (0); } - /* - * If the user has specified -2 to the client, setaf and setab may not - * work, so send the usual sequence. - */ - if (tty->term_flags & TERM_256COLOURS) - goto fallback; - return (-1); fallback: @@ -1679,6 +1769,20 @@ fallback: return (0); } +int +tty_try_rgb(struct tty *tty, const struct grid_cell_rgb *rgb, const char *type) +{ + char s[32]; + + if (!tty_term_flag(tty->term, TTYC_TC)) + return (-1); + + xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type, rgb->r, + rgb->g, rgb->b); + tty_puts(tty, s); + return (0); +} + void tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) { @@ -1688,8 +1792,8 @@ tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) return; pgc = &wp->colgc; - agc = options_get_style(&wp->window->options, "window-active-style"); - wgc = options_get_style(&wp->window->options, "window-style"); + 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)) { diff --git a/utf8.c b/utf8.c index 76b4846a..c0407576 100644 --- a/utf8.c +++ b/utf8.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * 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 @@ -20,195 +20,38 @@ #include #include +#include #include "tmux.h" -struct utf8_width_entry { - u_int first; - u_int last; - - int width; - - struct utf8_width_entry *left; - 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 }, - { 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 }, - { 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 }, - { 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 }, - { 0x005c7, 0x005c7, 0, NULL, NULL }, - { 0x02060, 0x02063, 0, NULL, NULL }, - { 0x00c3e, 0x00c40, 0, NULL, NULL }, - { 0x10a05, 0x10a06, 0, NULL, NULL }, -}; - -struct utf8_width_entry *utf8_width_root = NULL; - -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 *); +static int utf8_width(wchar_t); /* Set a single character. */ void -utf8_set(struct utf8_data *utf8data, u_char ch) +utf8_set(struct utf8_data *ud, u_char ch) { - *utf8data->data = ch; - utf8data->size = 1; + u_int i; - utf8data->width = 1; + *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'; } /* @@ -217,150 +60,97 @@ utf8_set(struct utf8_data *utf8data, u_char ch) * 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) + wchar_t wc; + int width; + + 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); + + if (ud->width == 0xff) + return (UTF8_ERROR); + + if (utf8_combine(ud, &wc) != UTF8_DONE) + return (UTF8_ERROR); + if ((width = utf8_width(wc)) < 0) + return (UTF8_ERROR); + ud->width = width; + + return (UTF8_DONE); } -/* Check if two width tree entries overlap. */ -int -utf8_overlap(struct utf8_width_entry *item1, struct utf8_width_entry *item2) +/* Get width of Unicode character. */ +static int +utf8_width(wchar_t wc) { - 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); + int width; + + width = wcwidth(wc); + if (width < 0 || width > 0xff) + return (-1); + return (width); } -/* Build UTF-8 width tree. */ -void -utf8_build(void) +/* Combine UTF-8 into Unicode. */ +enum utf8_state +utf8_combine(const struct utf8_data *ud, wchar_t *wc) { - struct utf8_width_entry **ptr, *item, *node; - u_int i, j; - - 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; - else if (item->first > node->last) - ptr = &node->right; - } - *ptr = item; + switch (mbtowc(wc, ud->data, ud->size)) { + case -1: + mbtowc(NULL, NULL, MB_CUR_MAX); + return (UTF8_ERROR); + case 0: + return (UTF8_ERROR); + default: + return (UTF8_DONE); } } -/* Combine UTF-8 into 32-bit Unicode. */ -u_int -utf8_combine(const struct utf8_data *utf8data) +/* Split Unicode into UTF-8. */ +enum utf8_state +utf8_split(wchar_t wc, struct utf8_data *ud) { - u_int value; + char s[MB_LEN_MAX]; + int slen; - value = 0xff; - switch (utf8data->size) { - case 1: - value = utf8data->data[0]; - break; - case 2: - value = utf8data->data[1] & 0x3f; - value |= (utf8data->data[0] & 0x1f) << 6; - break; - case 3: - value = utf8data->data[2] & 0x3f; - value |= (utf8data->data[1] & 0x3f) << 6; - value |= (utf8data->data[0] & 0x0f) << 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] & 0x07) << 18; - break; - } - return (value); -} + slen = wctomb(s, wc); + if (slen <= 0 || slen > (int)sizeof ud->data) + return (UTF8_ERROR); -/* Split a two-byte UTF-8 character. */ -u_int -utf8_split2(u_int uc, u_char *ptr) -{ - if (uc > 0x7f) { - ptr[0] = (uc >> 6) | 0xc0; - ptr[1] = (uc & 0x3f) | 0x80; - return (2); - } - ptr[0] = uc; - return (1); -} + memcpy(ud->data, s, slen); + ud->size = slen; -/* Lookup width of UTF-8 data in tree. */ -u_int -utf8_width(const struct utf8_data *utf8data) -{ - struct utf8_width_entry *item; - u_int value; - - value = utf8_combine(utf8data); - - 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); - } - return (1); + ud->width = utf8_width(wc); + return (UTF8_DONE); } /* @@ -371,28 +161,26 @@ utf8_width(const struct utf8_data *utf8data) int utf8_strvis(char *dst, const char *src, size_t len, int flag) { - struct utf8_data utf8data; + struct utf8_data ud; const char *start, *end; - int more; + enum utf8_state more; size_t i; start = dst; end = src + len; while (src < end) { - if (utf8_open(&utf8data, *src)) { - more = 1; - while (++src < end && more) - more = utf8_append(&utf8data, *src); - if (!more) { + 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 < utf8data.size; i++) - *dst++ = utf8data.data[i]; + for (i = 0; i < ud.size; i++) + *dst++ = ud.data[i]; continue; - } else if (utf8data.have > 0) { - /* Not a complete UTF-8 character. */ - src -= utf8data.have; } + /* Not a complete, valid UTF-8 character. */ + src -= ud.have; } if (src < end - 1) dst = vis(dst, src[0], flag, src[1]); @@ -405,6 +193,49 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) 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. @@ -414,27 +245,25 @@ utf8_fromcstr(const char *src) { struct utf8_data *dst; size_t n; - int more; + enum utf8_state more; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); - if (utf8_open(&dst[n], *src)) { - more = 1; - while (*++src != '\0' && more) + if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { + while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&dst[n], *src); - if (!more) { + if (more == UTF8_DONE) { n++; continue; } src -= dst[n].have; } utf8_set(&dst[n], *src); - src++; - n++; + src++; } dst = xreallocarray(dst, n + 1, sizeof *dst); @@ -469,21 +298,21 @@ utf8_cstrwidth(const char *s) { struct utf8_data tmp; u_int width; - int more; + enum utf8_state more; width = 0; while (*s != '\0') { - if (utf8_open(&tmp, *s)) { - more = 1; - while (*++s != '\0' && more) + if ((more = utf8_open(&tmp, *s)) == UTF8_MORE) { + while (*++s != '\0' && more == UTF8_MORE) more = utf8_append(&tmp, *s); - if (!more) { + if (more == UTF8_DONE) { width += tmp.width; continue; } s -= tmp.have; } - width++; + if (*s > 0x1f && *s != 0x7f) + width++; s++; } return (width); @@ -512,3 +341,61 @@ utf8_trimcstr(const char *s, u_int width) free(tmp); return (out); } + +/* Trim UTF-8 string to width. Caller frees. */ +char * +utf8_rtrimcstr(const char *s, u_int width) +{ + struct utf8_data *tmp, *next, *end; + char *out; + u_int at; + + tmp = utf8_fromcstr(s); + + for (end = tmp; end->size != 0; end++) + /* nothing */; + if (end == tmp) { + free(tmp); + return (xstrdup("")); + } + next = end - 1; + + at = 0; + for (;;) + { + if (at + next->width > width) { + next++; + break; + } + at += next->width; + + if (next == tmp) + break; + next--; + } + + out = utf8_tocstr(next); + 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 37baf0d7..7a727aac 100644 --- a/window-choose.c +++ b/window-choose.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -29,17 +29,17 @@ 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 client *, - struct session *, int, struct mouse_event *); + 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 *, - int, struct mouse_event *); + 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 *); @@ -86,9 +86,9 @@ 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 @@ -169,7 +169,7 @@ window_choose_init(struct window_pane *wp) screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; - 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 @@ -186,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; @@ -299,8 +299,8 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) } 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; @@ -314,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; @@ -490,7 +490,8 @@ 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, int key, struct mouse_event *m) +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; @@ -508,8 +509,8 @@ window_choose_get_item(struct window_pane *wp, int key, struct mouse_event *m) } void -window_choose_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, int key, struct mouse_event *m) +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; @@ -613,10 +614,10 @@ window_choose_key(struct window_pane *wp, unused struct client *c, 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; @@ -633,10 +634,10 @@ window_choose_key(struct window_pane *wp, unused struct client *c, 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); @@ -648,8 +649,8 @@ window_choose_key(struct window_pane *wp, unused struct client *c, 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); @@ -743,23 +744,22 @@ window_choose_key(struct window_pane *wp, unused struct client *c, } 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) style_apply(&gc, oo, "mode-style"); @@ -776,7 +776,7 @@ window_choose_write_line( 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, + 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 @@ -821,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" @@ -834,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++; } diff --git a/window-clock.c b/window-clock.c index 5bc546a9..4cc58684 100644 --- a/window-clock.c +++ b/window-clock.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -28,7 +28,7 @@ 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 client *, - struct session *, int, struct mouse_event *); + struct session *, key_code, struct mouse_event *); void window_clock_timer_callback(int, short, void *); void window_clock_draw_screen(struct window_pane *); @@ -120,7 +120,7 @@ const char window_clock_table[14][5][5] = { }; void -window_clock_timer_callback(unused int fd, unused short events, void *arg) +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; @@ -185,8 +185,9 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_clock_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, unused int key, unused struct mouse_event *m) +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); } @@ -204,8 +205,8 @@ window_clock_draw_screen(struct window_pane *wp) 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); diff --git a/window-copy.c b/window-copy.c index c7d360de..939d3e19 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -23,14 +23,16 @@ #include #include "tmux.h" +#include "tmate.h" struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); +void window_copy_pagedown(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_key(struct window_pane *, struct client *, struct session *, - int, struct mouse_event *); -int window_copy_key_input(struct window_pane *, int); -int window_copy_key_numeric_prefix(struct window_pane *, int); + 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); @@ -93,70 +95,7 @@ const struct window_mode window_copy_mode = { window_copy_key, }; -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, -}; - -/* - * 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). - */ -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; -}; +#include "window-copy.h" struct screen * window_copy_init(struct window_pane *wp) @@ -195,7 +134,7 @@ window_copy_init(struct window_pane *wp) s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); - 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 @@ -204,6 +143,10 @@ window_copy_init(struct window_pane *wp) data->backing = NULL; +#ifdef TMATE + data->password_cb = NULL; +#endif + return (s); } @@ -231,6 +174,10 @@ window_copy_init_from_pane(struct window_pane *wp, int scroll_exit) window_copy_write_line(wp, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_stop(&ctx); + +#ifdef TMATE + tmate_sync_copy_mode(wp); +#endif } void @@ -280,13 +227,15 @@ 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, old_cy; +#ifdef TMATE + char *msg; +#endif + 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); @@ -301,7 +250,14 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) } else data->backing_written = 1; old_cy = backing->cy; - screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); +#ifdef TMATE + xvasprintf(&msg, fmt, ap); + 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, fmt, ap); +#endif screen_write_stop(&back_ctx); data->oy += screen_hsize(data->backing) - old_hsize; @@ -326,15 +282,80 @@ window_copy_pageup(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - u_int n; + u_int n, ox, oy; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + + if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) + window_copy_other_end(wp); + + if (data->cx != ox) { + data->lastcx = data->cx; + data->lastsx = ox; + } + data->cx = data->lastcx; n = 1; if (screen_size_y(s) > 2) n = screen_size_y(s) - 2; + if (data->oy + n > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); else data->oy += n; + + if (!data->screen.sel.flag || !data->rectflag) { + u_int py = screen_hsize(data->backing) + data->cy - data->oy; + u_int px = window_copy_find_length(wp, py); + if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) + window_copy_cursor_end_of_line(wp); + } + + window_copy_update_selection(wp, 1); + window_copy_redraw_screen(wp); +} + +void +window_copy_pagedown(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int n, ox, oy; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + + if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely) + window_copy_other_end(wp); + + if (data->cx != ox) { + data->lastcx = data->cx; + data->lastsx = ox; + } + data->cx = data->lastcx; + + n = 1; + if (screen_size_y(s) > 2) + n = screen_size_y(s) - 2; + + if (data->oy < n) + data->oy = 0; + else + data->oy -= n; + + if (!data->screen.sel.flag || !data->rectflag) { + u_int py = screen_hsize(data->backing) + data->cy - data->oy; + u_int px = window_copy_find_length(wp, py); + if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) + window_copy_cursor_end_of_line(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); } @@ -366,9 +387,9 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) window_copy_redraw_screen(wp); } -void -window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, - int key, struct mouse_event *m) +static void +__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; @@ -481,21 +502,8 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, window_copy_pageup(wp); break; case MODEKEYCOPY_NEXTPAGE: - n = 1; - if (screen_size_y(s) > 2) - n = screen_size_y(s) - 2; - for (; np != 0; np--) { - if (data->oy < n) - data->oy = 0; - else - data->oy -= n; - } - if (data->scroll_exit && data->oy == 0) { - window_pane_reset_mode(wp); - return; - } - window_copy_update_selection(wp, 1); - window_copy_redraw_screen(wp); + for (; np != 0; np--) + window_copy_pagedown(wp); break; case MODEKEYCOPY_HALFPAGEUP: n = screen_size_y(s) / 2; @@ -629,13 +637,13 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, 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; @@ -645,7 +653,7 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, 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; @@ -714,6 +722,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, case MODEKEYCOPY_SEARCHAGAIN: case MODEKEYCOPY_SEARCHREVERSE: switch (data->searchtype) { +#ifdef TMATE + case WINDOW_COPY_PASSWORD: + break; +#endif case WINDOW_COPY_OFF: case WINDOW_COPY_GOTOLINE: case WINDOW_COPY_JUMPFORWARD: @@ -722,7 +734,6 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, case WINDOW_COPY_JUMPTOBACK: case WINDOW_COPY_NAMEDBUFFER: case WINDOW_COPY_NUMERICPREFIX: - break; case WINDOW_COPY_SEARCHUP: ss = data->searchstr; if (cmd == MODEKEYCOPY_SEARCHAGAIN) { @@ -777,7 +788,7 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, 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 @@ -787,7 +798,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 @@ -799,8 +810,18 @@ input_off: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); } +void +window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, + key_code key, struct mouse_event *m) +{ + __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; @@ -876,6 +897,15 @@ window_copy_key_input(struct window_pane *wp, int key) window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; break; +#ifdef TMATE + case WINDOW_COPY_PASSWORD: + if (data->password_cb) { + data->password_cb(data->inputstr, + data->password_cb_private); + } + window_pane_reset_mode(wp); + return 0; +#endif } data->numprefix = -1; return (1); @@ -897,7 +927,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; @@ -944,21 +974,21 @@ int 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); - if (cis && ud.size == 1) - return (tolower(ud.data[0]) == sud.data[0]); + if (cis && ud->size == 1) + return (tolower(ud->data[0]) == sud->data[0]); - return (memcmp(ud.data, sud.data, ud.size) == 0); + return (memcmp(ud->data, sud->data, ud->size) == 0); } int @@ -1021,19 +1051,23 @@ 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, cis; + int n, wrapped, wrapflag, cis; const char *ptr; +#ifdef TMATE + if (!searchstr) + return; +#endif + 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; @@ -1088,19 +1122,23 @@ 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, cis; + int n, wrapped, wrapflag, cis; const char *ptr; +#ifdef TMATE + if (!searchstr) + return; +#endif + 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; @@ -1168,7 +1206,7 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, { 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; char hdr[512]; size_t last, xoff = 0, size = 0, limit; @@ -1177,12 +1215,18 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, last = screen_size_y(s) - 1; if (py == 0) { +#ifdef TMATE + if (data->inputtype != WINDOW_COPY_PASSWORD) { +#endif size = xsnprintf(hdr, sizeof hdr, "[%u/%u]", data->oy, screen_hsize(data->backing)); if (size > screen_size_x(s)) size = screen_size_x(s); screen_write_cursormove(ctx, screen_size_x(s) - size, 0); screen_write_puts(ctx, &gc, "%s", hdr); +#ifdef TMATE + } +#endif } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { limit = sizeof hdr; if (limit > screen_size_x(s) + 1) @@ -1191,6 +1235,17 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, xoff = size = xsnprintf(hdr, limit, "Repeat: %d", data->numprefix); } else { +#ifdef TMATE + if (data->inputtype == WINDOW_COPY_PASSWORD) { + int password_len = strlen(data->inputstr); + xoff = size = xsnprintf(hdr, sizeof hdr, "%s: ", data->inputprompt); + memset(hdr+xoff, '*', password_len); + xoff += password_len; + size += password_len; + hdr[xoff] = '\0'; + } + else +#endif xoff = size = xsnprintf(hdr, limit, "%s: %s", data->inputprompt, data->inputstr); } @@ -1301,7 +1356,7 @@ 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; @@ -1401,7 +1456,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 @@ -1460,7 +1515,7 @@ window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, { 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); @@ -1484,11 +1539,11 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, if (buf == NULL) return; - ft = format_create(); + ft = format_create(NULL, 0); format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); - job = job_run(expanded, sess, -1, NULL, NULL, NULL); + job = job_run(expanded, sess, NULL, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); free(expanded); @@ -1523,7 +1578,7 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) if (buf == NULL) return; - 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); @@ -1545,12 +1600,12 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) } 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_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; @@ -1579,11 +1634,11 @@ 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); - if (ud.size == 1 && (gc->attr & GRID_ATTR_CHARSET)) { + 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); @@ -1622,16 +1677,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 @@ -1639,8 +1695,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; /* @@ -1653,9 +1708,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--; } @@ -1689,17 +1743,15 @@ 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++; } @@ -1782,11 +1834,13 @@ void window_copy_cursor_left(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; + u_int py; - if (data->cx == 0) { + py = screen_hsize(data->backing) + data->cy - data->oy; + if (data->cx == 0 && py > 0) { window_copy_cursor_up(wp, 0); window_copy_cursor_end_of_line(wp); - } else { + } else if (data->cx > 0) { window_copy_update_cursor(wp, data->cx - 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1797,19 +1851,20 @@ void window_copy_cursor_right(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; - u_int px, py; + u_int px, py, yy; + py = screen_hsize(data->backing) + data->cy - data->oy; + yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; if (data->screen.sel.flag && data->rectflag) px = screen_size_x(&data->screen); else { - py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); } - if (data->cx >= px) { + if (data->cx >= px && py < yy) { window_copy_cursor_start_of_line(wp); window_copy_cursor_down(wp, 0); - } else { + } else if (data->cx < px) { window_copy_update_cursor(wp, data->cx + 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1913,8 +1968,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; @@ -1922,10 +1976,9 @@ 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, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1940,8 +1993,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; @@ -1951,10 +2003,9 @@ 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, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1971,8 +2022,7 @@ 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 + jump_again; @@ -1980,10 +2030,9 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) 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, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1998,8 +2047,7 @@ 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; @@ -2012,10 +2060,9 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) 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, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -2074,7 +2121,7 @@ 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 options *oo = wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; int keys, expected = 1; @@ -2219,6 +2266,16 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny) 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) { @@ -2237,7 +2294,7 @@ window_copy_rectangle_toggle(struct window_pane *wp) } void -window_copy_start_drag(struct client *c, unused struct mouse_event *m) +window_copy_start_drag(struct client *c, struct mouse_event *m) { struct window_pane *wp; u_int x, y; @@ -2250,7 +2307,7 @@ window_copy_start_drag(struct client *c, unused struct mouse_event *m) return; c->tty.mouse_drag_update = window_copy_drag_update; - c->tty.mouse_drag_release = window_copy_drag_release; + c->tty.mouse_drag_release = NULL; /* will fire MouseUp key */ window_copy_update_cursor(wp, x, y); window_copy_start_selection(wp); @@ -2258,7 +2315,7 @@ window_copy_start_drag(struct client *c, unused struct mouse_event *m) } void -window_copy_drag_update(unused struct client *c, struct mouse_event *m) +window_copy_drag_update(__unused struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_copy_mode_data *data; @@ -2277,16 +2334,3 @@ window_copy_drag_update(unused struct client *c, struct mouse_event *m) 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 d66cb1f6..45ec7790 100644 --- a/window.c +++ b/window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * 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 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Each window is attached to a number of panes, each of which is a pty. This @@ -295,13 +297,17 @@ window_create1(u_int sx, u_int sy) TAILQ_INIT(&w->panes); w->active = NULL; +#ifdef TMATE + w->tmate_last_sync_active_pane = NULL; +#endif + w->lastlayout = -1; w->layout_root = NULL; w->sx = sx; w->sy = sy; - options_init(&w->options, &global_w_options); + w->options = options_create(global_w_options); w->references = 0; @@ -315,8 +321,8 @@ window_create1(u_int sx, u_int sy) struct window * window_create(const char *name, int argc, char **argv, const char *path, - const char *shell, int cwd, struct environ *env, struct termios *tio, - u_int sx, u_int sy, u_int hlimit, char **cause) + 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; @@ -334,7 +340,7 @@ window_create(const char *name, int argc, char **argv, const char *path, 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); @@ -358,7 +364,7 @@ window_destroy(struct window *w) if (event_initialized(&w->alerts_timer)) evtimer_del(&w->alerts_timer); - options_free(&w->options); + options_free(w->options); window_destroy_panes(w); @@ -379,9 +385,22 @@ window_remove_ref(struct window *w) void window_set_name(struct window *w, const char *new_name) { +#ifdef TMATE + /* + * We don't want to sync the layout too much. + * We might want to have some sort of timer for when to + * sync the layout. + */ + if (!strcmp(w->name, new_name)) + return; +#endif + free(w->name); w->name = xstrdup(new_name); notify_window_renamed(w); +#ifdef TMATE + tmate_sync_layout(); +#endif } void @@ -436,8 +455,8 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) * 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"); + 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)) @@ -567,7 +586,7 @@ window_add_pane(struct window *w, u_int hlimit) void window_lost_pane(struct window *w, struct window_pane *wp) { - if (wp == marked_window_pane) + if (wp == marked_pane.wp) server_clear_marked(); if (wp == w->active) { @@ -578,6 +597,8 @@ window_lost_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; } @@ -597,7 +618,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); @@ -635,7 +656,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); @@ -688,7 +709,7 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; - if (server_check_marked() && wl == marked_winlink) + if (server_check_marked() && wl == marked_pane.wl) flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; @@ -735,7 +756,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->argc = 0; wp->argv = NULL; wp->shell = NULL; - wp->cwd = -1; + wp->cwd = NULL; wp->fd = -1; wp->event = NULL; @@ -754,6 +775,10 @@ 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); @@ -798,7 +823,7 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); - close(wp->cwd); + free((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); free(wp); @@ -806,12 +831,12 @@ window_pane_destroy(struct window_pane *wp) int window_pane_spawn(struct window_pane *wp, int argc, char **argv, - const char *path, const char *shell, int cwd, struct environ *env, + const char *path, const char *shell, const char *cwd, struct environ *env, struct termios *tio, char **cause) { struct winsize ws; - char *argv0, *cmd, **argvp, paneid[16]; - const char *ptr, *first; + char *argv0, *cmd, **argvp; + const char *ptr, *first, *home; struct termios tio2; #ifdef HAVE_UTEMPTER char s[32]; @@ -831,9 +856,9 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, free(wp->shell); wp->shell = xstrdup(shell); } - if (cwd != -1) { - close(wp->cwd); - wp->cwd = dup(cwd); + if (cwd != NULL) { + free((void *)wp->cwd); + wp->cwd = xstrdup(cwd); } cmd = cmd_stringify_argv(wp->argc, wp->argv); @@ -852,8 +877,10 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, free(cmd); return (-1); case 0: - if (fchdir(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"); @@ -861,8 +888,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, 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"); @@ -870,9 +896,8 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, closefrom(STDERR_FILENO + 1); if (path != NULL) - environ_set(env, "PATH", path); - xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); - environ_set(env, "TMUX_PANE", paneid); + environ_set(env, "PATH", "%s", path); + environ_set(env, "TMUX_PANE", "%%%u", wp->id); environ_push(env); clear_signals(1); @@ -913,6 +938,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, #ifdef HAVE_UTEMPTER xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id); utempter_add_record(wp->fd, s); + kill(getpid(), SIGCHLD); #endif setblocking(wp->fd, 0); @@ -928,13 +954,13 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, } void -window_pane_timer_callback(unused int fd, unused short events, void *data) +window_pane_timer_callback(__unused int fd, __unused short events, void *data) { window_pane_read_callback(NULL, data); } void -window_pane_read_callback(unused struct bufferevent *bufev, void *data) +window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; struct evbuffer *evb = wp->event->input; @@ -959,13 +985,23 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(evb); + new_data = EVBUFFER_DATA(evb) + wp->pipe_off; 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(evb); +#ifdef TMATE + wp->tmate_off = EVBUFFER_LENGTH(evb); +#endif return; start_timer: @@ -980,12 +1016,12 @@ start_timer: } 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 @@ -1016,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); @@ -1046,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); @@ -1096,6 +1132,8 @@ 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|PANE_CHANGED); + + server_status_window(wp->window); return (0); } @@ -1110,11 +1148,17 @@ window_pane_reset_mode(struct window_pane *wp) wp->screen = &wp->base; wp->flags |= (PANE_REDRAW|PANE_CHANGED); + + server_status_window(wp->window); + +#ifdef TMATE + tmate_sync_copy_mode(wp); +#endif } void window_pane_key(struct window_pane *wp, struct client *c, struct session *s, - int key, struct mouse_event *m) + key_code key, struct mouse_event *m) { struct window_pane *wp2; @@ -1134,11 +1178,13 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, if (KEYC_IS_MOUSE(key)) return; - if (options_get_number(&wp->window->options, "synchronize-panes")) { + 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)) + if (wp2->fd == -1 || wp2->flags & PANE_INPUTOFF) + continue; + if (window_pane_visible(wp2)) input_key(wp2, key, NULL); } } diff --git a/xmalloc.c b/xmalloc.c index b1570a3a..90af1e2f 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -1,148 +1,139 @@ /* $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 "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 newsize) +xreallocarray(void *ptr, size_t nmemb, size_t size) { - void *newptr; + void *new_ptr; - if (newsize == 0) - fatalx("zero size"); - 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; } -void * -xreallocarray(void *oldptr, size_t nmemb, size_t size) +char * +xstrdup(const char *str) { - size_t newsize = nmemb * size; - void *newptr; + char *cp; - if (newsize == 0) - fatalx("zero size"); - if (SIZE_MAX / nmemb < size) - fatalx("nmemb * size > SIZE_MAX"); - if ((newptr = realloc(oldptr, newsize)) == NULL) - fatal("xreallocarray failed"); - - return (newptr); + 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; } +__attribute__((__format__(__printf__, 2, 0))) 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"); - return (i); + if (i < 0 || *ret == NULL) + fatal("xasprintf: %s", strerror(errno)); + + return i; } int -xsnprintf(char *buf, size_t len, const char *fmt, ...) +xsnprintf(char *str, size_t len, const char *fmt, ...) { va_list ap; - int i; + int i; va_start(ap, fmt); - i = xvsnprintf(buf, len, fmt, ap); + i = xvsnprintf(str, len, fmt, ap); va_end(ap); - return (i); + return i; } +__attribute__((__format__(__printf__, 3, 0))) int -xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) +xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) { - int i; + 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..0360b0d9 --- /dev/null +++ b/xmalloc.h @@ -0,0 +1,44 @@ +/* $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 + +#if !defined(__bounded__) +# define __bounded__(x, y, z) +#endif + +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 10d51c78..7bfefd15 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * 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 @@ -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, size_t *, u_int *); -int xterm_keys_modifiers(const char *, size_t, size_t *, u_int *); +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; }; @@ -115,7 +116,7 @@ const struct xterm_keys_entry xterm_keys_table[] = { */ int xterm_keys_match(const char *template, const char *buf, size_t len, - size_t *size, u_int *modifiers) + size_t *size, key_code *modifiers) { size_t pos; int retval; @@ -148,7 +149,8 @@ xterm_keys_match(const char *template, const char *buf, size_t len, /* Find modifiers from buffer. */ int -xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) +xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, + key_code *modifiers) { u_int flags; @@ -179,11 +181,12 @@ xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) * 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, modifiers; + u_int i; int matched; + key_code modifiers; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; @@ -201,11 +204,11 @@ xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) /* 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;