winupd/cmdline.pm
2017-11-12 07:29:03 +01:00

231 lines
5.7 KiB
Perl

package cmdline;
use strict;
require Exporter;
@cmdline::ISA = qw(Exporter);
@cmdline::EXPORT = qw(
parse_cmdline
print_usage
);
use logging;
sub store_option_value ($$$)
{
my ($spec, $arg, $value) = @_;
my $storeinto = $$spec{StoreInto};
if (defined $storeinto && ref($storeinto) eq 'SCALAR') {
print_log('global', DEBUG1, 'Storing option %s value: %s', $arg, $value);
$$storeinto = $value;
}
my $storefunc = $$spec{StoreFunc};
if (defined $storefunc && ref($storefunc) eq 'CODE') {
print_log('global', DEBUG1, 'Storing option %s value: %s', $arg, $value);
&{$storefunc}($value);
}
}
sub check_option_value ($$$)
{
my ($spec, $arg, $value) = @_;
my $type = $$spec{Type};
if ($type eq 'integer') {
if ($value !~ /^\d+$/o) {
print STDERR 'Invalid integer parameter for option '.$arg.': '.$value."\n";
return 0;
}
}
if ($type eq 'keyvalue') {
if ($value !~ /^[^=]+=/o) {
print STDERR 'Invalid key=value parameter syntax for option '.$arg.': '.$value."\n";
return 0;
}
}
return 1;
}
sub parse_cmdline ($)
{
my ($specs) = @_;
my $general = 0;
my $found = {};
my $shorts = {};
my $longs = {};
foreach my $spec (@$specs) {
$general++ if defined $$spec{General};
my $short = $$spec{Short};
my $long = $$spec{Long};
$$shorts{$short} = $spec if defined $short;
$$longs{$long} = $spec if defined $long;
}
my $left = [];
my $i = 0;
my $options = 1;
while (1) {
my $arg = $ARGV[$i++];
last unless defined $arg;
print_log('global', DEBUG2, 'Checking argument %s', $arg);
if ($options && $arg =~ /^--(.*)$/o) {
my $long = $1;
if ($long eq '') {
print_log('global', DEBUG2, 'No more options');
$options = 0;
next;
}
my $spec = $$longs{$long};
if (! defined $spec) {
print STDERR 'Unknown long option: '.$arg."\n";
return undef;
}
print_log('global', DEBUG2, 'Found long option: %s', $long);
my $param;
if ($$spec{Type} eq 'flag') {
$param = 1;
}
else {
$param = $ARGV[$i++];
if (! defined $param) {
print STDERR 'Missing parameter for long option: '.$arg."\n";
return undef;
}
return undef unless check_option_value($spec, $arg, $param);
}
$$found{$arg} = 1;
store_option_value($spec, $arg, $param);
next;
}
if ($options && $arg =~ /^-(.*)$/o) {
my $list = $1;
my $j = 0;
while ($j < length($list)) {
my $short = substr($list, $j++, 1);
if ($short eq '-') {
print_log('global', DEBUG2, 'No more options');
$options = 0;
next;
}
my $opt = '-'.$short;
my $spec = $$shorts{$short};
if (! defined $spec) {
print STDERR 'Unknown short option: '.$opt."\n";
return undef;
}
print_log('global', DEBUG2, 'Found short option: %s', $short);
my $param;
if ($$spec{Type} eq 'flag') {
$param = 1;
}
else {
$param = $ARGV[$i++];
if (! defined $param) {
print STDERR 'Missing parameter for short option: '.$opt."\n";
return undef;
}
return undef unless check_option_value($spec, $opt, $param);
}
$$found{$opt} = 1;
store_option_value($spec, $opt, $param);
}
next;
}
if (! $general) {
print STDERR 'Unexpected general parameter: '.$arg."\n";
return undef;
}
print_log('global', DEBUG1, 'Adding general cli parameter %s', $arg);
push @$left, $arg;
}
foreach my $spec (@$specs) {
next if defined $$spec{General};
my $default = $$spec{Default};
next unless defined $default;
my $arg;
my $long = $$spec{Long};
if (defined $long) {
$arg = '--'.$long;
next if defined $$found{$arg};
}
my $short = $$spec{Short};
if (defined $short) {
$arg = '-'.$short;
next if defined $$found{$arg};
}
print_log('global', DEBUG1, 'Using default for option %s: %s', $arg, $default);
store_option_value($spec, $arg, $default);
}
return $left;
}
sub print_usage ($)
{
my ($specs) = @_;
my $general = '';
my $longest = 0;
foreach my $spec (@$specs) {
my $len = 0;
if (defined $$spec{General}) {
my $option = $$spec{Option};
my $optional = $$spec{Optional};
$optional = defined $optional && $optional;
$len = length($option);
$general .= ' '.($optional ? '[' : '').$option.($optional ? ']' : '')
if $len > 0;
}
else {
my $short = $$spec{Short};
my $long = $$spec{Long};
my $shortlen = defined $short ? length($short) + 1 : 0;
my $longlen = defined $long ? length($long) + 2 : 0;
$len = $shortlen + $longlen;
$len += 2 if $shortlen > 0 && $longlen > 0;
}
$longest = $len if $len > $longest;
}
print STDERR 'Usage: '.$0.' [<options>]'.$general."\n";
foreach my $spec (@$specs) {
my $buffer = '';
my $desc = $$spec{Description};
if (defined $$spec{General}) {
my $option = $$spec{Option};
$buffer .= $option;
}
else {
my $short = $$spec{Short};
my $long = $$spec{Long};
my $type = $$spec{Type};
$type = '['.$type.']' if defined $type;
$desc = $desc.' '.$type if defined $type;
my $default = $$spec{Default};
$default = 'set' if $type eq 'Flag' && defined $default && $default;
$default = '(Default:'.$default.')' if defined $default;
$desc = $desc.' '.$default if defined $default;
$short = '-'.$short if defined $short;
$long = '--'.$long if defined $long;
$buffer .= $short if defined $short;
$buffer .= ', ' if defined $short && defined $long;
$buffer .= $long if defined $long;
}
$buffer .= ' ' x ($longest - length($buffer)) if length($buffer) < $longest;
$buffer = ' '.$buffer;
$buffer .= $longest + 4 + length($desc) < 80 ?
' '.$desc."\n" : "\n".' '.$desc."\n";
print STDERR $buffer
}
}
1;