#!/usr/bin/perl
# make an executable file

use strict;
use warnings;

sub usage; sub mkexec; sub guesstype; sub getcmd; sub launch_editor;
sub nextarg { $ARGV[0] }

my $DEFAULT_TYPE = 'sh';
my $DEFAULT_MODE = 0755;
my $DEFAULT_FORCE = 0;


## .... ##   . . .  .   .    .     .      .       .        .         .
## INIT ## -------------------------------------------------------------------
## '''' ##   ' ' '  '   '    '     '      '       '        '         '

defined nextarg() or usage;
my $force   = (nextarg() eq '-f' ? shift : $DEFAULT_FORCE);
my $name    = (defined nextarg() ? shift : usage);
my $type    = (defined nextarg() ? shift : guesstype $name);

my $bangs = {
    pl   => `which perl`,
    sh   => '/bin/sh',
    py   => `which python`,
    bash => '/bin/bash',
    sed  => `which sed`,
    bc   => `which bc`
};

my $templates = {
    pl   => "use strict;\nuse warnings;\n",
    py   => "if __name__ == '__main__':\n"
};

my $editors = [ qw/ vim editor / ];
my $cmds;
$cmds->{vim}->{test}    = "vim --version 2>/dev/null";
$cmds->{vim}->{run}     = "vim +\"normal G\$\" '%s'";
$cmds->{editor}->{test} = "editor --version 2>/dev/null";
$cmds->{editor}->{run}  = "editor '%s'";


## ... ##   # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
## RUN ## ----------------------------------------------------------------- #
## ''' ##   # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if (exists $bangs->{$type}) {
    $force and -e $name
        and (defined `cp "$name" "$name~"` or die $!);
    (not -e $name or $force)
        and mkexec $name, mkbody($type);
    chmod $DEFAULT_MODE, $name;
    launch_editor $name;
} else {
    die "unknown type: $type\n";
}


## .... ##   ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''-.
## SUBZ ## ----------------------------------------------------------------- :
## '''' ##   ..............................................................-'

sub usage {
    print STDERR "usage: $0 [-f] filename [type]\n";
    exit 0;
}

sub guesstype {
    my $name = shift;
    my ($ext) = $name =~ m|\.(\w+)$|;
    return ( $ext ? $ext : $DEFAULT_TYPE);
}

sub mkbody {
    my $type = shift;
    my $tmpl = "";
    $tmpl .= $templates->{$type} if exists $templates->{$type};
    return sprintf "#!%s\n%s\n", $bangs->{$type}, $tmpl;
}

sub mkexec {
    my ($name, $body) = @_;
    open EXE, ">", $name    or die "cannot open $name for writing: $!\n";
    -W EXE                  or die "file $name is not writable\n";
    print EXE $body;
    close EXE               or die "cannot close $name: $!\n";
}

sub get_cmd {
    foreach (@$editors) {
        return $cmds->{$_}->{run} if `$cmds->{$_}->{test}`
    }
    warn "no supported editor available\n";
    return;
}

sub launch_editor {
    my $name = shift;
    my $form = get_cmd;
    if ($form) {
        my $command = sprintf get_cmd, $name;
        exec "$command";
    } else { return }
    warn "failed to launch editor: $form\n";
}