| @ -1,317 +0,0 @@ | |||
| #!/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); | |||
| } | |||