You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

317 lines
4.9 KiB

#!/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);
}