/usr/local/CPAN/Getopt-Janus/Getopt/Janus/CLI.pm
require 5;
package Getopt::Janus::CLI;
# Get command-line interface options (yup, from @ARGV)
@ISA = ('Getopt::Janus::SessionBase');
$VERSION = '1.03';
use strict;
use Getopt::Janus (); # makes sure Getopt::Janus::DEBUG is defined
BEGIN { *DEBUG = \&Getopt::Janus::DEBUG }
use Getopt::Janus::SessionBase;
Getopt::Janus::DEBUG and print "Revving up ", __PACKAGE__, "\n";
sub open_new_files { } # block it happening
# TODO: make -h / --help produce help/longhelp (latter with license)
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
sub review_result { } # no need for this all
sub get_option_values {
my $self = shift;
if($^O =~ m/Win32/) {
while( @ARGV and !length $ARGV[-1] ) { pop @ARGV }
}
my $run_flag = 1;
my @args = @ARGV;
my %unknowns;
my @values;
$self->parse_values(\@values, \@args, \%unknowns, \$run_flag);
if( $run_flag ) {
DEBUG and print "parse_values has run_flag on, with values @values\n";
$self->consider_values( \@values );
} else {
DEBUG and print "parse_values has run_flag off\n values [@values]",
"\n unknowns [@{[sort keys %unknowns]}]\n args [@args]\n";
$self->complain_about( \@args, \%unknowns );
exit 1;
}
return;
}
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
sub parse_values {
my($self, $values, $args, $unknowns, $run_flag_s) = (@_);
my($long, $short) = @$self{ 'long', 'short' };
my $dummy = {'type' => 'yes_no', "_HACK_", 1};
local $_;
while(@$args) {
$_ = $args->[0];
last if $_ eq '-'; # not a switch at all
shift(@$args), last if $_ eq '--'; # switch meaning 'end of switches'
if( m/^-([_0-9a-zA-Z])$/s or m/^--?([-_0-9a-zA-Z]{2,})$/s ) { # -x or --xax
# And tolerate -xax
if(not( $short->{$1} || $long->{$1} )) {
++$unknowns->{$1};
DEBUG and print "Unknown option $1\n";
shift @$args;
} elsif(
'yes_no' eq ( $short->{$1} || $long->{$1} )->{'type'}
or 'HELP' eq ( $short->{$1} || $long->{$1} )->{'type'}
) {
push @$values, $1 => 1; # just note it as a true value and move on
shift @$args;
} else {
# It's a nonboolean value -- so snare the value and re-cycle
# it as a -x=foo or --xax=foo for the next pass
push @$args, '' if @$args == 1 and $^O =~ m/Win32/;
$args->[0] .= '=' . splice(@$args,1,1);
}
} elsif( m/^-([_0-9a-zA-Z])=(.*?)$/s ) { # -x=foo
unless( exists $short->{$1} ) {
++$unknowns->{$1};
} else {
push @$values, $1 => $2;
}
shift(@$args);
} elsif( m/^--?([-_0-9a-zA-Z]{2,})=(.*?)$/s ) { # --xax=foo
# and tolerate -xax=foo
unless( exists $long->{$1} ) {
++$unknowns->{$1};
} else {
push @$values, $1 => $2;
}
shift(@$args);
} else {
$$run_flag_s = 0;
last; # leaving things unprocessed
}
}
$$run_flag_s = 0 if keys %$unknowns or @$args;
return;
}
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
sub complain_about {
my( $self, $args, $unknowns ) = @_;
if( keys %$unknowns ) {
my @them = sort keys %$unknowns;
foreach (@them) { s/^(.)$/-$1/s or s/^(.+)$/--$1/s } # add the prefixes
print "Unknown options that you used: [@them]\n\n"
}
print "Arguments left unprocessed: [@$args]\n\n" if @$args;
print $self->short_help_message;
return;
}
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
sub consider_values {
my( $self, $values ) = @_;
my($long, $short) = @$self{ 'long', 'short' };
my %seen;
my($option, $key, $value, $type, $oldval);
DEBUG and print "Values: @$values\n";
while( @$values ) {
$key = $values->[0];
$option = ( (length($key) == 1) ? $short : $long )->{$key};
++$seen{$option};
$type = $option->{'type'};
my $slot_r = $option->{'slot'};
$oldval = $$slot_r;
$$slot_r = $values->[1];
splice @$values,0,2;
DEBUG and print "Option \"$key\" = \"$$slot_r\"\n";
if( $type eq 'HELP' ) {
print '', (length($key) == 1)
? $self->short_help_message : $self->long_help_message;
exit;
}
if( $seen{$option} > 1 ) {
print "Duplicate setting for option ",
join('/', grep defined($_), @$option{'short', 'long'}),
": \"$oldval\" and \"$$slot_r\".\n";
exit;
}
if( $type eq 'yes_no' ) {
DEBUG > 1 and print "(Type $type needs no checking.)\n";
} elsif( $type eq 'string' ) {
DEBUG > 1 and print "(Type $type needs no checking.)\n";
} elsif( $type eq 'new_file' ) {
if(!length $$slot_r) {
#die "Option $key can't take \"\" as a value" unless length $$slot_r;
# No, it's okay to set this to null.
} else {
# Any further checking?
}
} elsif( $type eq 'file' ) {
if(!length $$slot_r) {
#die "Option $key can't take \"\" as a value" unless length $$slot_r;
# No, it's okay to set this to null.
} else {
-e $$slot_r or die "Setting to a non-existent file in $key=$$slot_r\n";
-d _ and die "Setting to a directory in $key=$$slot_r\n";
-f _ or die "Setting to a non-file in $key=$$slot_r\n";
-r _ or die "Setting to an unreadable file in $key=$$slot_r\n";
DEBUG > 1 and print "File $$slot_r checks out.\n";
}
} elsif( $type eq 'choose' ) {
if( grep $_ eq $$slot_r, @{$option->{'from'}} ) {
DEBUG > 1 and print "Choice $$slot_r checks out.\n";
} else {
die(
"Option $key=$$slot_r needs to be one of: [" .
join( '|', @{$option->{'from'}}) . "]\n"
);
}
} else {
DEBUG and print "I don't know how to check an option of type $type\n";
}
}
return;
}
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
1;
__END__