#!/usr/bin/perl
|
|
use strict;
|
|
use warnings;
|
|
|
|
use constant
|
|
{
|
|
PROG_EDIT => 'vim',
|
|
};
|
|
|
|
sub open_smart;
|
|
sub edit;
|
|
sub stdin_to_editor;
|
|
sub reveal;
|
|
sub header_edit;
|
|
sub wait_or_not;
|
|
|
|
sub usage
|
|
{
|
|
print STDERR <<"!";
|
|
Usage: $0 -[efWwRh]
|
|
-e: edit
|
|
-f: stdin-edit
|
|
-W: wait for exit (true by default for editing)
|
|
-w: don't wait for exit
|
|
-R: reveal
|
|
-h: header search
|
|
!
|
|
exit 1;
|
|
}
|
|
|
|
my $cmd = \&open_smart;
|
|
my(@files, @args);
|
|
|
|
my %opts = (
|
|
'e' => 0,
|
|
'f' => 0,
|
|
'W' => 0,
|
|
'R' => 0,
|
|
'h' => 0,
|
|
);
|
|
|
|
my $wait_set = 0;
|
|
|
|
usage() unless @ARGV;
|
|
|
|
for(my $i = 0; $i < @ARGV; ++$i){
|
|
$_ = $ARGV[$i];
|
|
|
|
if($_ eq '--'){
|
|
push @files, @ARGV[$i + 1 .. $#ARGV];
|
|
last;
|
|
}
|
|
|
|
if(/^-([a-z])$/i){
|
|
my $k = $1;
|
|
|
|
if(exists $opts{$k}){
|
|
$opts{$k} = 1;
|
|
$wait_set = 1 if $k eq 'W';
|
|
}elsif($k eq 'w'){
|
|
$opts{W} = 0;
|
|
$wait_set = 1;
|
|
}else{
|
|
usage();
|
|
}
|
|
|
|
}elsif($_ eq '--args'){
|
|
push @args, @ARGV[$i + 1 .. $#ARGV];
|
|
last;
|
|
|
|
}elsif(/^-/){
|
|
usage();
|
|
|
|
}else{
|
|
push @files, $_;
|
|
|
|
}
|
|
}
|
|
|
|
if($opts{e} + $opts{f} + $opts{R} + $opts{h} > 1){
|
|
print STDERR "Can't combine -e, -f, -R and -h\n";
|
|
usage();
|
|
}
|
|
|
|
my $should_wait = 1;
|
|
if($opts{e}){
|
|
$cmd = \&edit;
|
|
|
|
}elsif($opts{f}){
|
|
# <STDIN> | $EDITOR -
|
|
$cmd = \&stdin_to_editor;
|
|
|
|
}elsif($opts{R}){
|
|
# open with rox
|
|
$cmd = \&reveal;
|
|
$should_wait = 0;
|
|
|
|
}elsif($opts{h}){
|
|
# search /usr/include/$_ for @files
|
|
$cmd = \&header_edit;
|
|
|
|
}
|
|
|
|
$opts{W} = 1 if $should_wait and not $wait_set;
|
|
|
|
exit(&{$cmd}((
|
|
wait => !!$opts{W},
|
|
args => [@args],
|
|
files => [@files])));
|
|
|
|
# end ---
|
|
|
|
sub read_maps
|
|
{
|
|
my $rc = "$ENV{HOME}/.openrc";
|
|
open F, '<', $rc or die "open $rc: $!\n";
|
|
|
|
my %maps;
|
|
my $prog_reveal = '';
|
|
|
|
my $mode = 0;
|
|
while(<F>){
|
|
chomp;
|
|
s/#.*//;
|
|
|
|
if(/^\[(.*)\]$/){
|
|
if($1 eq 'full'){
|
|
$mode = $1;
|
|
}elsif($1 eq 'suffix'){
|
|
$mode = $1;
|
|
}elsif($1 eq 'directories'){
|
|
$mode = $1;
|
|
}else{
|
|
die "invalid section \"$1\" in $rc\n";
|
|
}
|
|
|
|
}elsif(!($mode eq 'directories') and my($prog, $matches) = /^([^:]+): *(.*)/){
|
|
sub getenv
|
|
{
|
|
my $k = shift;
|
|
return $ENV{$k} if $ENV{$k};
|
|
my %backup = (
|
|
"TERM" => "urxvt",
|
|
"VISUAL" => "vim",
|
|
);
|
|
return $backup{$k} if $backup{$k};
|
|
return "\$$k";
|
|
}
|
|
|
|
my @matches = split / *, */, $matches;
|
|
|
|
$prog =~ s/\$([A-Z_]+)/getenv($1)/e;
|
|
|
|
my $key = $prog;
|
|
if($mode eq 'suffix'){
|
|
# compare file extensions case insensitively
|
|
$key = lc $key;
|
|
}
|
|
|
|
push @{$maps{$key}}, [ $_, $mode ] for @matches;
|
|
|
|
}elsif($mode eq 'directories' && length){
|
|
if(length($prog_reveal)){
|
|
die "already have dir program, in $rc\n";
|
|
}
|
|
$prog_reveal = $_;
|
|
|
|
}elsif(length){
|
|
die "invalid confiuration line: \"$1\" in $rc\n";
|
|
}
|
|
}
|
|
|
|
if(!length($prog_reveal)){
|
|
die "no directory program specified, in $rc\n";
|
|
}
|
|
|
|
close F;
|
|
|
|
return $prog_reveal, \%maps;
|
|
}
|
|
|
|
sub open_smart
|
|
{
|
|
my $ec = 0;
|
|
my %h = @_;
|
|
|
|
my @to_open;
|
|
|
|
my ($prog_reveal, $maps) = read_maps();
|
|
|
|
file:
|
|
for my $fnam (@{$h{files}}){
|
|
#print "maps:\n";
|
|
|
|
if(-d $fnam){
|
|
push @to_open, [($h{wait}, $prog_reveal, @{$h{args}}, $fnam)];
|
|
next file;
|
|
}
|
|
|
|
(my $fnam_for_test = $fnam) =~ s/\.[a-zA-Z]+$/lc($&)/e;
|
|
|
|
for my $prog (keys %$maps){
|
|
#print " $_:\n";
|
|
for(@{$maps->{$prog}}){
|
|
my($reg, $mode) = ($_->[0], $_->[1]);
|
|
if($mode eq 'suffix'){
|
|
$reg = "\\.$reg\$";
|
|
}
|
|
#print " $reg\n"
|
|
|
|
if($fnam_for_test =~ /$reg/){
|
|
push @to_open, [($h{wait}, $prog, @{$h{args}}, $fnam)];
|
|
next file;
|
|
}
|
|
}
|
|
}
|
|
|
|
die "no program found for $fnam\n";
|
|
}
|
|
|
|
wait_or_not(@{$_}) for @to_open;
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub wait_or_not
|
|
{
|
|
my($wait, @rest) = @_;
|
|
my $pid = fork();
|
|
|
|
die "fork(): $!\n" unless defined $pid;
|
|
|
|
if($pid == 0){
|
|
if($rest[0] =~ / /){
|
|
my $a = shift @rest;
|
|
unshift @rest, split / +/, $a;
|
|
}
|
|
|
|
exec @rest;
|
|
die;
|
|
}else{
|
|
# parent
|
|
if($wait){
|
|
my $reaped = wait();
|
|
my $ret = $?;
|
|
|
|
die "wait(): $!\n" if $reaped == -1;
|
|
warn "unexpected dead child $reaped (expected $pid)\n" if $reaped != $pid;
|
|
|
|
return $ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
sub edit
|
|
{
|
|
my %h = @_;
|
|
my $e = $ENV{VISUAL} || $ENV{EDITOR} || PROG_EDIT;
|
|
return wait_or_not($h{wait}, $e, @{$h{args}}, @{$h{files}});
|
|
}
|
|
|
|
sub stdin_to_editor
|
|
{
|
|
my $tmp = "/tmp/stdin_$$";
|
|
|
|
open F, '>', $tmp or die "open $tmp: $!\n";
|
|
print F $_ while <STDIN>;
|
|
close F;
|
|
|
|
my %h = @_;
|
|
push @{$h{files}}, $tmp;
|
|
my $r = edit(%h);
|
|
unlink $tmp;
|
|
return $r;
|
|
}
|
|
|
|
sub reveal
|
|
{
|
|
my %h = @_;
|
|
my ($prog_reveal) = read_maps();
|
|
return wait_or_not($h{wait}, $prog_reveal, @{$h{args}}, @{$h{files}});
|
|
}
|
|
|
|
sub header_edit
|
|
{
|
|
my %h = @_;
|
|
my @files = @{$h{files}};
|
|
@{$h{files}} = ();
|
|
|
|
for my $name (@files){
|
|
sub find_header
|
|
{
|
|
my @inc = ("", "arpa", "net", "sys");
|
|
my $r = shift;
|
|
my @matches;
|
|
|
|
for(my @tmp = @inc){
|
|
push @inc, "x86_64-linux-gnu/$_";
|
|
}
|
|
|
|
for my $inc (@inc){
|
|
$inc = "/usr/include/$inc";
|
|
|
|
opendir D, $inc or next;
|
|
push @matches, map { "$inc/$_" } grep /$r/, readdir D;
|
|
closedir D;
|
|
}
|
|
|
|
return @matches;
|
|
}
|
|
|
|
my @paths = find_header($name);
|
|
push @{$h{files}}, @paths if @paths;
|
|
}
|
|
|
|
return edit(%h);
|
|
}
|