From a9143229e92251062642a81eb4514e04f5035c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Tue, 22 Oct 2013 16:35:30 +0200 Subject: Add cwd-spawn to urxvt to allow spawning a new terminal from the current working dir. --- .Xresources | 5 +- .urxvt/extensions/cwd-spawn | 181 ++++++++++++++++++++++++++++++++++++++++++++ .zsh/zshrc | 15 +++- 3 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 .urxvt/extensions/cwd-spawn diff --git a/.Xresources b/.Xresources index 60d5c6c..660eb72 100644 --- a/.Xresources +++ b/.Xresources @@ -24,12 +24,15 @@ URxvt.keysym.Shift-Down: command:\033]721;1\007 ! perl-modules URxvt.perl-lib: HOME/.urxvt/extensions/ -URxvt.perl-ext-common: default,url-select,clipboard,selection-autotransform,wgetpaste +URxvt.perl-ext-common: default,url-select,clipboard,selection-autotransform,wgetpaste,cwd-spawn ! allow Alt-V/-C for pasting from/copying to clipboard URxvt.keysym.M-v: perl:clipboard:paste URxvt.keysym.M-c: perl:clipboard:copy +! open new urxvt(c) starting in current dir with M-o +URxvt.keysym.M-o: perl:cwd-spawn + ! url-select via M-u URxvt.url-select.launcher: HOME/bin/openlink URxvt.keysym.M-u: perl:url-select:select_next diff --git a/.urxvt/extensions/cwd-spawn b/.urxvt/extensions/cwd-spawn new file mode 100644 index 0000000..fd5e08c --- /dev/null +++ b/.urxvt/extensions/cwd-spawn @@ -0,0 +1,181 @@ +# urxvt prepends "use strict; use utf8;\n", screwing with our line numbers +#line 3 + +=head1 NAME + +cwd-spawn - open a new urxvt within the current working directory. + + +=head1 INSTALLATION + + +1) adjust your F<.Xresources> + + URxvt*perl-lib: /home/user/.urxvt + URxvt*perl-ext: cwd-spawn + URxvt*keysym.M-o: perl:cwd-spawn + +2) copy/symlink this script into F + +3) adjust your shell config to include these functions + (known to work with zsh/bash/ksh) + + cwd_to_urxvt() { + local update="\0033]777;cwd-spawn;path;$PWD\0007" + + case $TERM in + screen*) + # pass through to parent terminal emulator + update="\0033P$update\0033\\";; + esac + + echo -ne "$update" + } + + cwd_to_urxvt # execute upon startup to set initial directory + + ssh_connection_to_urxvt() { + # don't propagate information to urxvt if ssh is used non-interactive + [ -t 0 ] || [ -t 1 ] || return + + local update="\0033]777;cwd-spawn;ssh;$1\0007" + + case $TERM in + screen*) + # pass through to parent terminal emulator + update="\0033P$update\0033\\";; + esac + + echo -ne "$update" + } + +4) adjust F<.ssh/config> + + Host * + PermitLocalCommand yes + LocalCommand ssh_connection_to_urxvt "%r %h %p" + +5) execute cwd_to_urxvt each time you change your directory. + + # zsh supports hooks which execute each time you change your cwd: + chpwd_functions=(${chpwd_functions} cwd_to_urxvt) + +Support for other shells are left as an exercise for the reader ;-) + + +=head1 BUGS + +C doesn’t invoke LocalCommand if you connect through “master” mode. +Thus C always copies the connection information into the new terminal. +While this works fine for a single connection, it fails if you nest ssh connections (as a slave through “master” mode). +As a workaround, manually invoke C. + + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2011 Maik Fischer L + +This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. + + +=cut + +sub _octal_escape { + my ($string) = @_; + + my $escape = sub { + my ($char) = @_; + ord($char) > 127 + ? $char + : sprintf("\0%.3o", $char) + }; + + $string =~ s!([^A-Za-z0-9/_-])!$escape->($1)!sge; + return $string; +} + +use Encode; +my $utf8 = Encode::find_encoding('UTF-8'); + +sub on_osc_seq_perl { + my ($self, $osc, $resp) = @_; + + return unless $osc =~ s/^cwd-spawn;//; + + # decode raw bytestring into utf8 + local $@; + $osc = eval { $utf8->decode($osc, Encode::FB_CROAK) }; + if ($@) { + warn "cwd-spawn: called with garbage: $@"; + return; + } + + return unless $osc =~ s/^(path|ssh);//; + my $cmd = $1; + + my $storage = $self->{'cwd-spawn'} ||= {}; + + if ($cmd eq 'path') { + # path sanitizing is context specific, we do that in on_user_command + $storage->{path} = $osc; + + } else { + my ($user, $host, $port) = split ' ', $osc; + + # user and port are arguments to parameters, we can pass them as-is + # host may be used to pass arbitrary parameters to ssh + return if $host =~ /^-/; + + @{$storage}{qw/user host port/} = ($user, $host, $port); + } + + $self->_dump if $ENV{DEBUG_URXVT_CWDSPAWN}; + return 1; +} + +sub on_user_command { + my ($self, $cmd) = @_; + + return unless $cmd eq 'cwd-spawn'; + + $self->_dump if $ENV{DEBUG_URXVT_CWDSPAWN}; + + my $storage = $self->{'cwd-spawn'} + or return; + + my ($cwd, $user, $host, $port) = @{$storage}{qw/path user host port/}; + + my $name = 'URxvt'; # hardcode for now + + my @args; + if ($host) { + # escape $path here since it is subject to shell-expansion + my $path = _octal_escape($cwd); + @args = ( + '-e', + 'ssh', '-t', '-p', $port, '-l', $user, $host, + "cd \"$path\"; exec \$SHELL -l" + ) + } else { + @args = ('-cd', $cwd); + } + + warn "cwd-spawn: would start urxvt with: @args" + if $ENV{DEBUG_URXVT_CWDSPAWN}; + + my $term = urxvt::term->new($self->env, $name, @args); + + # ssh doesn't execute LocalCommand if used through ControlMaster + # see BUGS in POD + $term->cmd_parse("\e]777;cwd-spawn;ssh;$user $host $port\a") if $host; + + return; +} + +sub _dump { + my $storage = shift->{'cwd-spawn'}; + warn 'cwd-spawn: $storage is empty!' unless $storage; + warn sprintf('cwd-spawn: ' . ('%s: "%s" ' x keys %$storage), %$storage); +} + +# vim: set ts=4 sw=4 sts=4 ft=perl expandtab: diff --git a/.zsh/zshrc b/.zsh/zshrc index 74974fd..87a5876 100644 --- a/.zsh/zshrc +++ b/.zsh/zshrc @@ -194,7 +194,19 @@ _set_title () { echo -ne "${pre}${USER}@${HOST}:${PWD/$HOME/~}${post}" } -chpwd_functions=( ${chpwd_functions} _set_title ) +_set_cwd () { + local update="\0033]777;cwd-spawn;path;$PWD\0007" + + case $TERM in + screen*) + # pass through to parent terminal emulator + update="\0033P$update\0033\\";; + esac + + echo -ne "$update" +} + +chpwd_functions=( ${chpwd_functions} _set_title _set_cwd ) case $TERM in xterm*) @@ -205,6 +217,7 @@ esac # exec once for initialization _set_title +_set_cwd # }}} # Key bindings {{{ -- cgit v1.2.3