#!/usr/bin/perl # usage: cmd | $0 [-silent] | cmd # implant an editor session into a pipe, allowing a # fallback to also forwarding the unmodified contents # even if the user already saved a modified buffer to disk. # # if used with file arguments (xargs), help and (empty) # contents of stdin can be found in the last buffer. my $version="0.1.2"; # created PJ 20090803 jakobi@acm.org # last change PJ 20091011 (fixed <> issue) # copyright: (c) 2009 jakobi@acm.org, GPL v3 or later # archive: http://jakobi.github.com/script-archive-doc/ # editor requirements: # # ability to read and write from tty redirection. Test with # 'echo | ($EDITORPIPED /etc/hosts </dev/tty >/dev/tty;echo dummy) | wc -l'. # This must both allow an editor session and have wc -l see exactly 1 line. # Use shell variable $EDITORPIPED to avoid editor guessing. # vim tips: # # to just edit a bunch of files with a bufferlist in buffer 1, # using gf to switch to the file under the cursor: # find | sort | vim - # this '-' is half-magic, just like '.' # # even if - is missing, vim still can edit if stdin is guaranteed # to be empty. However, that requires a stty sane (possibly # to </dev/stderr or </dev/tty) to restore the terminal afterwards. # Which in turn will annoy subsequent cbreak/... programs like less... # # if the file contains arbitrary characters, use Vgf to treat # the whole line as the filename to jump to in a new buffer. # # vim -g and vim -remote with 2>/dev/null allow doing this # without a temp file when used with :w >>/dev/stdout | :q # to exit [the >> is required, as vim assumes plain files # and throws errors otherwise. Likewise, a mere :wq fails # and might think it fun to create a spurious '-' file # somewhere hidden in the fs] $Me=$0; $Me=~s@.*/([^/]+)$@$1@; while(@ARGV) { $_=$ARGV[0]; /^--?silent$/ and do {$silent=1; shift; next}; /^--?head(er)?$/ and do {shift;$header=shift;next;}; last; } @vimargs=@ARGV; @ARGV=(); if ($ENV{EDITORPIPED}) { $EDITOR=$ENV{EDITORPIPED}; } else { # prefer vim, otherwise try guessing $EDITOR=$tmp if chomp($tmp=`which vim`) and not $?; $EDITOR=$ENV{EDITOR} if not -x $EDITOR; $EDITOR=$ENV{VISUAL} if not $EDITOR; $EDITOR="vi" if not $EDITOR; } chomp($tmpname =`mktemp -q /tmp/bufpipe.$$.XXXXXX`); ($? or $tmpname eq "") and die "# $Me: cannot create tmpfile\n"; # read all the input from STDIN and invoke the editor $msg=<<EOF; # $Me: pipe.vim - edit a pipe (version $version) # $Me: # $Me: pipe.vim: # $Me: - header lines will be stripped only at the start of file # $Me: - return a single line of #FAIL or #EXIT (optionally # $Me: with RC) to force a exit. Just exit or use #PASS or #ORG # $Me: to forward the original input to the next pipe stage # $Me: - a buffer of just whitespace sets the output to "" # $Me: # $Me: vim: # $Me: - use gf or [VISUAL]gf to preview files # $Me: - use :ViewHtml to view html files / :setl noro # $Me: (:com! ViewHtml exe "%!lynx -dump -force_html /dev/stdin" |setl ro) EOF $msg.="$header\n\n" if $header; # $time=time; foreach(@ARGV){s/^(\s+)/.\/$1/;s/^/< /;$_.=qq/\0/}; # MAGIC <> INSECURE MESS undef $/; $_=<>; # SECURE:OK open(FH,">",$tmpname) and print FH $msg, $_ and close FH or do{unlink $tmpname; die "# ERR $Me: cannot write tmpfile\n" }; # sleep 2 if time-$time<3 and not $silent; # with useless cat to also protect against <() system "bash", "-c", $EDITOR.' "$@" </dev/tty >/dev/tty | cat', "$Me:$EDITOR", @vimargs, $tmpname; $rc=$?>>8; if (not $rc) { $tmp=`cat $tmpname`; $tmp=~s/\A(#\Q $Me:\E.*\n)+\n?//; # remove remants of $msg if ($tmp=~/\A#[\t ]*(?:FAIL|EXIT)(?:[\t ](\d+))?([\t ].*|)\Z/) { $rc=20; $rc=$1 if $1; warn "# ERR $Me: setting rc to $rc as requestion - suppressing output\n"; } elsif ($tmp=~/\A#[\t ]*(ORG|ORIGINAL|PASS)([\t ].*|)\Z/) { warn "# $Me: passing on pre-editor pipe contents as requested\n"; print $_; } else { $tmp=~/\A\s*\z/ and $tmp=""; # spurious empty line due to broken editor? print $tmp; } } else { warn "# ERR $Me: editor returned $rc - suppressing output\n"; } unlink($tmpname); exit($rc);