#!perl

use strict;
use warnings;

use Getopt::Long qw( :config require_order );

use File::Basename;
use File::Spec::Functions qw( file_name_is_absolute );
use Env::Path;
use App::Env;

our $VERSION = '0.09';
our $prog = basename( $0, '.pl' );

# program options; see parse_args();
our %opt;

eval { main() };

if ( $@ )
{
    print STDERR "# $prog: $_\n" foreach split /\n/, $@;
    exit 1;
}

exit 0;


sub main
{
    parse_args();

    help(1) if $opt{help};
    help(2) if $opt{usage};

    do { print "$prog $VERSION\n"; return }
      if $opt{version};

    eval { App::Env::import( $opt{env},
                           {
                            AppOpts => $opt{appopts},
                            ( defined $opt{site} ? (Site => $opt{site}) : () ),
                           } );
       };
    die( "error setting up environment `$opt{env}': $@\n" )
      if $@;

    if ( $opt{dumpenv} )
    {
	for my $env ( sort keys %ENV )
	{
	    my $out = "$env=";

	    if ( $ENV{$env} ne '' )
	    {
		if ( $opt{dumpenv} eq 'unquoted' )
		{
		    $out .= $ENV{$env};
		}
		else
		{
		    $out .= qq('$ENV{$env}');
		}
	    }

	    if ( $opt{dumpenv} eq 'bash' )
	    {
		print "export $out", "\n";
	    }

	    else
	    {
		print $out, "\n";
	    }
	}

    }

    print join(' ', @ARGV ), "\n"
      if $opt{verbose} & @ARGV;

    if ( @ARGV )
    {
	die( "$ARGV[0] does not exist, is not executable, or is not in PATH\n" )
	  unless
            ( file_name_is_absolute($ARGV[0]) && -e $ARGV[0] )
              || Env::Path->PATH->Whence($ARGV[0]);

	exec @ARGV
	  or die( "can't exec $ARGV[0]: not in path?\n" );
    }
}

sub parse_args
{

    %opt =
      (
       appopts  => {},
       verbose  => 0,
       version	=> 0,
       usage	=> 0,
       help	=> 0,
      );

    eval {
	local $SIG{ __WARN__ } = sub { die $_[0] };


	GetOptions ( \%opt,
		     qw/
			env=s
                        appopts|o=s%
			usage
			help
			dumpenv:s
                        site=s
			verbose
			version
			/,
		   );
    };

    die $@ if $@;

    return if $opt{version} || $opt{help} || $opt{usage};


    my @notset = grep { ! defined $opt{$_} } keys %opt;
    die( 'parameters `', join( "`, `", @notset ), "' are not set\n" )
      if @notset;

    # ensure that the dumpenv option is correct
    if ( exists $opt{dumpenv} )
    {
	$opt{dumpenv} = 'raw' unless $opt{dumpenv} ne '';
	die( "unsupported dumpenv format: $opt{dumpenv}\n" )
	  unless grep { $opt{dumpenv} eq $_ }
	    qw( bash raw unquoted );
    }

    # if --env wasn't specified, the first argument is the application
    # name
    $opt{env} = shift( @ARGV) unless defined $opt{env};

}


sub help
{
    my ( $verbose ) = @_;

    require Pod::Usage;
    Pod::Usage::pod2usage ( { -exitval => 0, -verbose => $verbose } );
}



__END__

=pod

=head1 NAME

appexec - execute a command under a specified environment

=head1 SYNOPSIS

B<appexec> --env environment I<[options]> program [I<program arguments>]

B<appexec> I<[options]> environment program [I<program arguments>]

=head1 DESCRIPTION

B<appexec> will execute a program with the specified arguments in the
specified environment.  The environment is generated by B<App::Env>,
so an appropriate B<App::Env> application module must exist.

=head1 OPTIONS AND ARGUMENTS

B<appexec> uses long options. Options may be abbreviated, and the "="
character shown below in the option templates may be replaced by
whitespace.

The environment to use may be specified either via the B<--env> option,
or as the first non-option argument.

The following options are available:

=over

=item B<--env>=I<name>

The name of the environment (or I<application> in B<App::Env> terminology)
in which to run the program.  The Perl module B<App::Env::name> must be exist.

If this option is not specified, the first non-option argument should be
the environment name

=item B<--appopts> I<key>=I<value> or B<-o> I<key>=I<value>

Specify a option key and value to be passed via B<AppOpts> to the
B<App::Env> application module.  This option may be specified multiple times.

=item B<--site>=I<site>

Specify a site. (See B<App::Env> for more information.)

=item B<--dumpenv>=I<format>

Output the environmental variables to the standard output stream.  The
possible formats are:

  bash
  raw
  unquoted

If I<format> is not specified, it defaults to C<raw>.

=item B<--verbose>

Print the command to be run to the standard output stream before running it.

=item B<--help>

Print a short help summary and exit.

=item B<--usage>

Print full documentation and exit.

=back


=head1 EXAMPLES

Real-life examples:

=over

=item 1

Run the B<fhelp> tool from the HEADAS FTOOLS application suite:

  appexec HEADAS fhelp fparkey

=item 2

Run the B<dmlist> tool from the CIAO version 3.4 application suite:

  appexec -o version=3.4 CIAO dmlist

=back


=head1 VERSION

This documents version 1.0.0 of B<appexec>.

=head1 COPYRIGHT AND LICENSE

This software is copyright the Smithsonian Astrophysical Observatory
and is released under the GNU General Public License.  You may find a
copy at L<http://www.fsf.org/copyleft/gpl.html>.


=head1 AUTHOR

Diab Jerius E<lt>djerius@cfa.harvard.eduE<gt>

