A useful perl module for quick argument parsing.
git-svn-id: file:///humgen/gsa-scr1/gsa-engineering/svn_contents/trunk@1491 348d0f76-0448-11de-a6fe-93d51630548a
This commit is contained in:
parent
5d155440cd
commit
dab7b6e825
|
|
@ -0,0 +1,305 @@
|
|||
package StingArgs;
|
||||
|
||||
use strict;
|
||||
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
|
||||
use Term::ANSIColor qw(:constants);
|
||||
|
||||
require Exporter;
|
||||
|
||||
@ISA = qw(Exporter AutoLoader);
|
||||
|
||||
@EXPORT = qw( getCommandArguments printCommandArguments moduleArguments PrintCommandHeader );
|
||||
|
||||
sub _getFormattingCharacterMap {
|
||||
my %fcmap = (
|
||||
'section' => '',
|
||||
'arg' => '',
|
||||
'default' => '',
|
||||
'end' => ''
|
||||
);
|
||||
|
||||
if (defined($ENV{'ARACHNE_PRETTY_HELP'})) {
|
||||
if ($ENV{'ARACHNE_PRETTY_HELP'} eq 'Color') {
|
||||
$fcmap{'section'} = (RED . BOLD);
|
||||
$fcmap{'arg'} = (MAGENTA . BOLD);
|
||||
$fcmap{'default'} = BLUE;
|
||||
$fcmap{'end'} = RESET;
|
||||
} elsif ($ENV{'ARACHNE_PRETTY_HELP'} eq 'Bold') {
|
||||
$fcmap{'section'} = BOLD;
|
||||
$fcmap{'arg'} = BOLD;
|
||||
$fcmap{'arg'} = BOLD;
|
||||
$fcmap{'default'} = "";
|
||||
$fcmap{'end'} = RESET;
|
||||
}
|
||||
}
|
||||
|
||||
return %fcmap;
|
||||
}
|
||||
|
||||
sub _usage {
|
||||
my ($requiredArgsRef, $helpRef) = @_;
|
||||
my %requiredArgs = %$requiredArgsRef;
|
||||
my %help = (defined($helpRef)) ? %$helpRef : ();
|
||||
my %optionalArgs;
|
||||
my %fcmap = &_getFormattingCharacterMap();
|
||||
|
||||
print "\n$fcmap{'section'}Usage: $0 arg1=value1 arg2=value2 ...$fcmap{'end'}\n\n";
|
||||
|
||||
print "$fcmap{'section'}Required arguments:$fcmap{'end'}\n\n";
|
||||
|
||||
foreach my $key (sort { $a cmp $b } keys(%requiredArgs)) {
|
||||
next if ($key =~ /_postprocess/ || $key =~ /_preprocess/);
|
||||
if (defined($requiredArgs{$key})) { $optionalArgs{$key} = $requiredArgs{$key}; }
|
||||
else {
|
||||
print "$fcmap{'arg'}$key$fcmap{'end'}\n";
|
||||
|
||||
if (defined($help{$key})) {
|
||||
print " $help{$key}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
|
||||
return unless keys(%optionalArgs);
|
||||
|
||||
print "$fcmap{'section'}Optional arguments:$fcmap{'end'}\n\n";
|
||||
|
||||
foreach my $key (sort { $a cmp $b } keys(%optionalArgs)) {
|
||||
if (defined($requiredArgs{$key})) {
|
||||
print "$fcmap{'arg'}$key$fcmap{'end'} $fcmap{'default'}default: " . ((ref($requiredArgs{$key}) eq 'ARRAY') ? "\"{" . join(",", @{$requiredArgs{$key}}) . "}\"" : $requiredArgs{$key}) . "$fcmap{'end'}\n";
|
||||
|
||||
if (defined($help{$key})) {
|
||||
print " $help{$key}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Parse the command-line arguments in Arachne style (including the ability to have whitespace between an equal sign and the parameter.
|
||||
sub getCommandArguments {
|
||||
my %requiredArgs = @_;
|
||||
my %help;
|
||||
|
||||
# Clean up our required arguments
|
||||
foreach my $key (keys(%requiredArgs)) {
|
||||
if (ref($requiredArgs{$key}) eq 'HASH') {
|
||||
$help{$key} = ${$requiredArgs{$key}}{'help'};
|
||||
$requiredArgs{$key} = ${$requiredArgs{$key}}{'value'};
|
||||
}
|
||||
if (defined($requiredArgs{$key})) {
|
||||
$requiredArgs{$key} =~ s/[\r\n]//g;
|
||||
}
|
||||
}
|
||||
|
||||
# Set our required argument defaults.
|
||||
my %args = (
|
||||
'NO_HEADER' => 0,
|
||||
%requiredArgs,
|
||||
);
|
||||
|
||||
if (defined($requiredArgs{'NH'}) && $requiredArgs{'NH'} =~ /(True|true|1)/) {
|
||||
$args{'NO_HEADER'} = 1;
|
||||
delete($args{'NH'});
|
||||
delete($requiredArgs{'NH'});
|
||||
}
|
||||
|
||||
# Print usage and exit if we're not supplied with any arguments.
|
||||
if ($#ARGV == -1) {
|
||||
if (defined($requiredArgs{'_usage'})) { &{$requiredArgs{'_usage'}}(\%requiredArgs, \%help); }
|
||||
else { &_usage(\%requiredArgs, \%help); }
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
# Clean up the command-line arguments so that we can accept arguments with spaces and things like 'KEY= VALUE' in addition to the normal 'KEY=VALUE'.
|
||||
for (my $i = 0; $i <= $#ARGV; $i++) {
|
||||
my $arg = $ARGV[$i];
|
||||
if ($arg =~ /\w+=$/) {
|
||||
until (($i+1) > $#ARGV || $ARGV[$i+1] =~ /\w+=/) { $arg .= $ARGV[++$i]; }
|
||||
}
|
||||
|
||||
if ($arg =~ /(NO_HEADER|NH)=(\s)?(True|true|1)/) { # Turn off automatic banner
|
||||
$args{'NO_HEADER'} = 1;
|
||||
} elsif ($arg =~ /(.+)=(.+)/) {
|
||||
my ($key, $value) = ($1, $2);
|
||||
|
||||
# Store arguments that are of no interest to us in a separate variable. This makes it convenient to allow certain arguments to pass through to another script by simply appending this extra argument to its command-line.
|
||||
if (!exists($requiredArgs{$key})) {
|
||||
$args{'_extra'} .= " $key=\"$value\"";
|
||||
}
|
||||
|
||||
if ($value eq 'True' || $value eq 'true' || ($value =~ /^\d+$/ && $value == 1)) { $args{$key} = 1; } # Parse boolean values
|
||||
elsif ($value eq 'False' || $value eq 'false' || ($value =~ /^\d+$/ && $value == 0)) { $args{$key} = 0; }
|
||||
elsif ($value =~ /{(.+)}/) { # Parse array values
|
||||
$value = $1;
|
||||
$value =~ s/\s+//g;
|
||||
my @values = split(",", $value);
|
||||
$args{$key} = \@values;
|
||||
} else { # Parse a regular ol' KEY=VALUE pair
|
||||
$args{$key} = $value;
|
||||
}
|
||||
} elsif ($arg =~ /(.+)=$/) { # Parse a KEY=VALUE pair where VALUE is empty
|
||||
$args{$1} = "";
|
||||
} elsif ($arg =~ /-(h|help|\?)/) { # Print help
|
||||
if (defined($requiredArgs{'_usage'})) { &{$requiredArgs{'_usage'}}(\%requiredArgs, \%help); }
|
||||
else { &_usage(\%requiredArgs, \%help); }
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
# Pre-process arguments
|
||||
if (defined($requiredArgs{'_preprocess'})) {
|
||||
&{$requiredArgs{'_preprocess'}}(\%args);
|
||||
delete($args{'_preprocess'});
|
||||
}
|
||||
|
||||
# Print the header box with info about the command
|
||||
&PrintCommandHeader() unless ($args{'NO_HEADER'});
|
||||
|
||||
# Did the user forget any arguments?
|
||||
my $missingArgs = 0;
|
||||
foreach my $requiredArg (keys(%requiredArgs)) {
|
||||
if ($requiredArg !~ /_(pre|post)process/ && !defined($args{$requiredArg})) {
|
||||
print "$requiredArg must be supplied.\n";
|
||||
$missingArgs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($missingArgs) { die ("Error: some required arguments were not supplied.\n"); }
|
||||
|
||||
# Post-process arguments
|
||||
if (defined($requiredArgs{'_postprocess'})) {
|
||||
&{$requiredArgs{'_postprocess'}}(\%args);
|
||||
delete($args{'_postprocess'});
|
||||
}
|
||||
|
||||
# We're all good!
|
||||
return %args;
|
||||
}
|
||||
|
||||
# Print our command arguments
|
||||
sub printCommandArguments {
|
||||
my ($argsref) = @_;
|
||||
my %args = %$argsref;
|
||||
|
||||
foreach my $key (sort { $a cmp $b } keys(%args)) {
|
||||
if (ref($args{$key}) eq 'ARRAY') {
|
||||
print "$key => {" . join(",", @{$args{$key}}) . "}\n";
|
||||
} else {
|
||||
print "$key => $args{$key}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Returns a hash (by reference) with the required command-line arguments
|
||||
# for the named C++ module.
|
||||
# The trick is to run the module with no arguments, prompting the usage message
|
||||
# (defined in system/ParsedArgs.cc), and then capture and parse this message.
|
||||
sub moduleArguments($) {
|
||||
my ($module_name) = @_;
|
||||
my %args;
|
||||
my ($key, $value);
|
||||
|
||||
# Temporarily setenv ARACHNE_PRETTY_HELP to "Bold" - to help with parsing
|
||||
my $temp = $ENV{'ARACHNE_PRETTY_HELP'};
|
||||
$ENV{'ARACHNE_PRETTY_HELP'} = "Bold";
|
||||
|
||||
# Escape character - appears in output when ARACHNE_PRETTY_HELP="Bold"
|
||||
my $esc = chr(27);
|
||||
my $optional = 0;
|
||||
|
||||
open (FH, "$module_name |");
|
||||
|
||||
# Process each line of output into a command-line argument
|
||||
foreach my $line (<FH>) {
|
||||
|
||||
$optional = 1 if ($line =~ /Optional arguments/);
|
||||
|
||||
# Match line against the specific stdout format given in ParsedArgs
|
||||
next unless ($line =~ /$esc\[01m(.+?)$esc\[0m(.+)/);
|
||||
$key = $1;
|
||||
|
||||
# If an argument is optional, but no value is specified in the usage
|
||||
# message, this means that the default value is in fact an empty string
|
||||
$value = $optional ? "" : undef;
|
||||
|
||||
# Look for a default value in this line
|
||||
if ($2 =~ /default\: (.+)$/) {
|
||||
$value = $1;
|
||||
}
|
||||
|
||||
$args{$key} = $value;
|
||||
}
|
||||
close FH;
|
||||
$ENV{'ARACHNE_PRETTY_HELP'} = $temp;
|
||||
|
||||
|
||||
|
||||
return \%args;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# Print the fancy header box with info about the command,
|
||||
# including arguments supplied to it
|
||||
# This parallels the function PrintTheCommandPretty in system/ParsedArgs.cc
|
||||
sub PrintCommandHeader {
|
||||
|
||||
my ($fh, $prefix, $thickbar) = (*STDOUT, ''); # default values
|
||||
$fh = $_[0] if ($_[0]); # filehandle to print the header to
|
||||
$prefix = $_[1] if ($_[1]); # string to be prepended to every line of the header
|
||||
$thickbar = $_[2] if ($_[2]); # print a thicker version of the bar ('=')
|
||||
my $width = 80;
|
||||
|
||||
my @stat = stat $0;
|
||||
my $mtime = localtime($stat[9]);
|
||||
|
||||
my $bar = $thickbar ? '='x$width : '-'x$width;
|
||||
my $timestamp = localtime() . " run (pid=" . $$ . "), last modified $mtime";
|
||||
|
||||
my $command = $0;
|
||||
$command .= ' ' while length $command < $width - 1;
|
||||
$command .= "\\";
|
||||
|
||||
# Fill @args_parsed with lines of (parsed) info about the args
|
||||
my @args = @ARGV;
|
||||
my @args_parsed = ();
|
||||
my $line = ' ';
|
||||
while (@args) {
|
||||
my $arg = shift @args;
|
||||
|
||||
# Start a new line, if necessary
|
||||
if (length($line) + 1 + length($arg) >= $width - 1 &&
|
||||
$line ne '') {
|
||||
$line .= ' ' while length $line < $width - 1;
|
||||
$line .= "\\";
|
||||
push @args_parsed, $line;
|
||||
$line = ' ';
|
||||
}
|
||||
|
||||
$line .= "$arg ";
|
||||
}
|
||||
if ($line) {
|
||||
push @args_parsed, $line;
|
||||
}
|
||||
|
||||
|
||||
# We have prepared the output lines;
|
||||
# now, prepend each line with the prefix, and append a newline
|
||||
map {$_ = "$prefix$_\n"} ($bar, $timestamp, $command, @args_parsed);
|
||||
|
||||
# Print lines to filehandle
|
||||
print $fh $bar;
|
||||
print $fh $timestamp;
|
||||
print $fh $command;
|
||||
print $fh @args_parsed;
|
||||
print $fh $bar;
|
||||
print $fh "\n";
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
Loading…
Reference in New Issue