rendered paste body# (C) Bryan Steimel 2007 and beyond
package SvD::GetOpt;
use strict;
use warnings;
use Carp qw(croak);
use Exporter qw(import);
our %EXPORT_TAGS = (
constants => [ grep {/^(?:OPT_|ARG_)/} %::SvD::GetOpt:: ],
subs => [ qw(fsit) ]
);
my %exists;
$EXPORT_TAGS{all} = [ grep {!$exists{$_}++} map {@$_} values %EXPORT_TAGS ];
undef %exists;
our @EXPORT_OK = @{$EXPORT_TAGS{all}};
use constant {
true => 1,
false => 0
};
use constant {
OPT_OPTIONAL => 0x0001,
OPT_MANDATORY => 0x0002,
ARG_NONE => 0x0004,
ARG_STR => 0x0008,
ARG_FLOAT => 0x0010,
ARG_BOOL => 0x0020,
ARG_NOTNULL => 0x0040,
ARG_INT => 0x0080
};
use constant {
BITS_SHORTN => 0,
BITS_FLAGS => 1,
BITS_REGEX => 2,
BITS_DEFAULT => 3
};
our $REG = {
ARG_STR, qr//,
ARG_INT, qr/^\d+$/,
ARG_FLOAT, qr/^\d+(?:\.\d+)?$/,
ARG_BOOL, qr/^[01]$/,
ARG_NOTNULL, qr/.+/
};
our $OPT = {};
AUTOLOAD {
no strict 'refs';
our $AUTOLOAD;
my($sub) = $AUTOLOAD =~ /.*::(.*)/;
$sub =~ y/A-Z/a-z/;
exists $OPT->{$sub}
or croak "Call to undefined subroutine &$AUTOLOAD";
*$AUTOLOAD = sub {
return $_[0]->{ARGV}->{$sub}
unless defined $_[1];
$_[0]->{ARGV}->{$sub} = $_[1]
};
&$AUTOLOAD
}
sub fsit { # thats, FORK STUCK IN TOASTER
my($script) = $0 =~ m#.*[\\/](.*)#i;
print "\n";
print '-' . " " . $script . ': ' . $_ . "\n"
for(@_);
exit(1);
}
sub grab {
my $Class = ref $_[0] ? ref shift : shift;
my %args = @_;
defined($OPT = delete $args{Rules})
&& ref $OPT eq 'HASH'
or croak 'Required argument MAP was not supplied';
my $Self = bless {
Rules => $OPT,
Format => ((ref $args{Format} eq 'ARRAY' && @{$args{Format}} && delete $args{Format})
or croak sprintf "%s->new(FORMAT, must be array reference, and not empty.", __PACKAGE__),
ALLOW_SHORT_OPTS => (scalar grep {defined $_ && $_ ne ''} map {ref $OPT->{$_}->[0] ? @{$OPT->{$_}->[0]} : $OPT->{$_}->[0]} keys %$OPT) ? true : false,
ARGV => {}
}, $Class;
$Self->{DASH_OPT_LEN} = $Self->{ALLOW_SHORT_OPTS} ? '1,2' : 2;
$OPT->{$_}->[BITS_FLAGS] & OPT_MANDATORY
&& defined $OPT->{$_}->[BITS_DEFAULT]
&& croak 'Arguments marked as *MANDATORY* cannot be assigned a default value'
foreach(keys %$OPT);
$Self->parse;
$Self
}
sub parse($) {
my $Self = shift;
my($format, $argv) = ($Self->{Format}, {});
argv:while(defined(local $_ = shift @ARGV)) {
if(s/\-{$Self->{DASH_OPT_LEN}}//) {
y/A-Z/a-z/;
my $key = $_;
($key) = (exists $OPT->{$_} ? $_ : grep {grep {defined $_ && $key eq $_} ref $OPT->{$_}->[0] ? @{$OPT->{$_}->[0]} : $OPT->{$_}->[0]} keys %$OPT)
or fsit 'Unknown argument: ' . $& . $_;
my $parm = shift @ARGV
unless SvD::GetOpt::isarg($ARGV[0]) || $OPT->{$key}->[BITS_FLAGS] & ARG_NONE;
fsit 'Bad data supplied for argument "' . $_ . '"'
unless $Self->checkparm($key, $parm);
$Self->{ARGV}->{$key} = $parm || $OPT->{$key}->[BITS_DEFAULT];
@$format = grep {$key ne $_} @$format
} else {
scalar @$format
or fsit 'What am I intended to do with "' . $_ . '"?';
fsit 'Bad data supplied for argument "' .$format->[0] . '"'
unless $Self->checkparm($format->[0], $_);
$Self->{ARGV}->{(shift @$format)} = $_ || $OPT->{$_}->[BITS_DEFAULT] # this should never happen
}
}
defined $Self->{ARGV}->{$_} or ($Self->{ARGV}->{$_} = $OPT->{$_}->[BITS_DEFAULT] || undef)
foreach(@$format); # set any [leftover] defaults before checking for null..
(defined $Self->{ARGV}->{$_}
&& $Self->{ARGV}->{$_} ne '')
|| ($OPT->{$_}->[BITS_FLAGS] & ARG_NOTNULL
&& fsit 'Required argument "' . $_ . '", was not supplied.')
for(grep {$OPT->{$_}->[BITS_FLAGS] & OPT_MANDATORY} keys %$OPT);
print $_ . ' => ' . (defined $Self->{ARGV}->{$_} ? $Self->{ARGV}->{$_} : '(undefined)') . "\n" foreach(keys %{ $Self->{ARGV} });
}
sub isarg {
defined $_[1]
&& ($_[1] =~ /\-{$_[0]->{DASH_OPT_LEN}}/
? true
: false
) || undef;
}
sub checkparm {
my($Self, $bit, $data, $ok) = (@_[0..2], true);
exists $Self->{Rules}->{$bit}
or fsit 'Unknown argument "' . $bit . '"';
my $rules = $Self->{Rules}->{$bit};
($rules->[BITS_FLAGS] & $_)
&& ($ok = ($data =~ /$REG->{$_}/)
? true
: false
) for(keys %$REG);
$ok = ($data =~ /$rules->[BITS_REGEX]/)
if defined $rules->[BITS_REGEX];
$ok
}
true
__END__