#!/usr/bin/perl # dwim (do what i mean) is my own plan9-like plumber use v5.24; use warnings; use strict; use subs qw/cd dir dirname env fail handle path run/; my ($cwd, $handler, $phrase); my ($DEBUG, $OPENER); while ($_ = shift @ARGV) { if (/^-/) { $DEBUG = 1 if /d/; $OPENER = 1 if /o/; die "usage: $0 [-d] [phrase]\n" if not /[do]/ } else { $phrase = $_; last; } } my @OPENER = $OPENER ? ('xtopen') : (); my @EDITOR = (@OPENER, 'vi'); my @PDF_VIEWER = ('xpdf'); my @MAN_VIEWER = (@OPENER, 'man'); my @MAILER = (@OPENER, 'mutt'); my @BROWSER = (@OPENER, 'w3m', '-title'); my @FILE_BROWSER = ('rox'); my @MAILDIR_VIEWER = (@OPENER, 'mutt -f'); my $MAILROOT = env MAILROOT => '/home/john/mail/'; $phrase = `xsel -o` if not defined $phrase; for ($phrase) { if (/^(https?:\/?\/?\S+)$/) { handle 'web address'; run @BROWSER, $1 } if (/^(mailto:\S+)$/ or /^(\S+@.+\.\w+)$/) { handle 'e-mail address'; run @MAILER, $1 } if (/^(.+?):(\d+|\/\S+)/) { handle 'file:line/query (like grep -n)'; my $p = path $1; my $q = $2; $q =~ s/\)$//; run @EDITOR, "+$q", $p } if (/^(.+) line (\d+)/) { handle ' line (like perl)'; my $p = path $1; run @EDITOR, "+$2", $p } if (/^(.+):(.+)$/) { handle 'file:query (like grep)'; my $p = path $1; run @EDITOR, "+/$2", $p if -e $p; fail 'file not found' if $DEBUG; # FALLTHROUGH } if (/^([-_A-Za-z.]+) ?\((\d+)\)[):,.]*$/) { handle 'manual(section)'; run @MAN_VIEWER, $2, $1; } if (/^([A-Za-z_][A-Za-z0-9_]*)\(/) { handle 'function(call'; my $dir = dir; s/\(.*/(/; for (`grep -n '^$_' "$dir"/*.c "$dir"/*.h`) { run @EDITOR, "+$2", path($1) if /([^:]+):(\d+):/; } fail 'could not find definition'; # FALLTHROUGH } if (/^<(\S+)>$/) { handle ''; open my $h, 'find / -maxdepth 3 -type d -name include 2>&-|' or fail 'could not search include directories'; while (<$h>) { chomp; if (-e "$_/$1") { close $h; run @EDITOR, "$_/$1"; } } close $h; fail 'header file not found'; exit 1; } if (/(?:\+\+\+|---) (\S+) .*$@@ -(\d+)/ms) { handle 'diff header'; my ($p, $l) = ($1, $2); $p = path($p)."/$p" if not $p =~ m{^/}; run @EDITOR, "+$l", $p; } if (/^ (?:modified: |deleted: )?(\S*)\n/m) { handle 'git status'; chdir dir; my @files = m//mg; run @OPENER, '-pwith-git -s', 'git', 'add', @files; } if (/^(\S+)$/) { handle 'maildir / directory / file'; my $p = path $1; run @MAILDIR_VIEWER, $p if absp($p) =~ /^$MAILROOT/; run @FILE_BROWSER, $p if -d absp($p); run @MAN_VIEWER, absp($p) if -e absp($p) and $p =~ /\.[1-9]$/; run @PDF_VIEWER, $p if -e absp($p) and $p =~ /\.pdf$/; run @EDITOR, $p if -e absp($p); fail 'file not found' if $DEBUG; # FALLTHROUGH } die "no handler matched by: $phrase\n" } # absp -- return absolute path to relative file (after calling path) sub absp { return $_[0] if $_[0] =~ m,^/,; return $cwd . "/$_[0]"; } # cd -- chdir and set $cwd sub cd { chdir $_[0]; $cwd = $_[0]; } # dir -- return directory of current window sub dir { state $dir; return $dir if defined $dir; for (`xtitle`) { chomp; s/.*\(([^(]+)\)$/$1/; s,^~([^/]+),/home/$1,; s,^~,/home/$ENV{USER},; die "couldn't retrieve current directory\n" if ! -d $_ and ! -d ($_ = dirname($_)); return $dir = $_; } die "couldn't retrieve current directory: xtitle not installed\n"; } # dirname -- return path without last part sub dirname { my $path = shift; $path =~ s,/[^/]+$,,; return $path; } # env -- for (K => V), return environment variable K if defined, otherwise V sub env { my %h = @_; my $k = (keys %h)[0]; return $ENV{$k} if defined ${ENV}{$k}; return $h{$k}; } # fail -- print debug message when matched handler fails sub fail { my $msg = shift; print STDERR "$handler FAILED: $msg\n"; } # handle -- save matched handler and print debug message sub handle { $handler = shift; print STDERR "$handler MATCHED\n" if $DEBUG; } # path -- return abbreviated path to file (and chdir to its directory) sub path { my ($path, $dir) = (shift, dir); $path =~ s,^~([^/]+),/home/$1,; $path =~ s,^~,/home/$ENV{USER},; $path = "$dir/$path" if not $path =~ m,^/,; $dir = dirname($path); cd $dir; $path =~ s,^\Q$dir/\E,,; return $path; } # run -- launch program and quit sub run { if ($DEBUG) { my @argv = @_; s/(\s)/\\$1/g for @argv; # escape whitespace print STDERR "@argv\n"; } if (!$OPENER or fork == 0) { exec @_; die "could not exec: $1\n"; } exit; }