From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- support/easylatex/Getopt/Declare.pm | 3189 +++++++++++++++++++++++++++++++++++ 1 file changed, 3189 insertions(+) create mode 100644 support/easylatex/Getopt/Declare.pm (limited to 'support/easylatex/Getopt') diff --git a/support/easylatex/Getopt/Declare.pm b/support/easylatex/Getopt/Declare.pm new file mode 100644 index 0000000000..df2f6afd70 --- /dev/null +++ b/support/easylatex/Getopt/Declare.pm @@ -0,0 +1,3189 @@ +package Getopt::Declare; + +use strict; +use vars qw($VERSION); +use UNIVERSAL qw(isa); + +$VERSION = '1.09'; + + +package Getopt::Declare::StartOpt; + +sub new { bless {} } +sub matcher { '(?:()' } +sub code { '' } +sub cachecode { '' } +sub trailer { undef } +sub ows { return $_[1]; } + +package Getopt::Declare::EndOpt; + +sub new { bless {} } +sub matcher { '())?' } +sub code { '' } +sub cachecode { '' } +sub trailer { undef } +sub ows { return $_[1]; } + +package Getopt::Declare::ScalarArg; + +my %stdtype = (); + +sub _reset_stdtype +{ + %stdtype = + ( + ':i' => { pattern => '(?:(?:%T[+-]?)%D+)' }, + ':n' => { pattern => '(?:(?:%T[+-]?)(?:%D+(?:%T\.%D*)?(?:%T[eE]%D+)?' + . '|%T\.%D+(?:%T[eE]%D+)?))' }, + ':s' => { pattern => '(?:%T(?:\S|\0))+' }, + ':qs' => { pattern => q{"(?:\\"|[^"])*"|'(?:\\'|[^"])*|(?:%T(?:\S|\0))+} }, + ':id' => { pattern => '%T[a-zA-Z_](?:%T\w)*' }, + ':if' => { pattern => '%F(?:%T(?:\S|\0))+', + action => '{reject(!defined $_VAL_ || $_VAL_ ne "-" && ! -r $_VAL_, "in parameter \'$_PARAM_\' (file \"$_VAL_\" is not readable)")}' }, + ':of' => { pattern => '%F(?:%T(?:\S|\0))+', + action => '{reject (!defined $_VAL_ || $_VAL_ ne "-" && -e $_VAL_ && ! -w $_VAL_ , "in parameter \'$_PARAM_\' (file \"$_VAL_\" is not writable)")}' }, + '' => { pattern => ':s', ind => 1 }, + + ':+i' => { pattern => ':i', + action => '{reject (!defined $_VAL_ || $_VAL_<=0, "in parameter \'$_PARAM_\' ($_VAR_ must be an integer greater than zero)")}', + ind => 1}, + + ':+n' => { pattern => ':n', + action => '{reject (!defined $_VAL_ || $_VAL_<=0, "in parameter \'$_PARAM_\' ($_VAR_ must be a number greater than zero)")}', + ind => 1}, + + ':0+i' => { pattern => ':i', + action => '{reject (!defined $_VAL_ || $_VAL_<0, "in parameter \'$_PARAM_\' ($_VAR_ must be an positive integer)")}', + ind => 1}, + + ':0+n' => { pattern => ':n', + action => '{reject (!defined $_VAL_ || $_VAL_<0, "in parameter \'$_PARAM_\' ($_VAR_ must be a positive number)")}', + ind => 1}, + + ); +} + +sub stdtype # ($typename) +{ + my $name = shift; + my %seen = (); + while (!$seen{$name} && $stdtype{$name} && $stdtype{$name}->{ind}) + { $seen{$name} = 1; $name = $stdtype{$name}->{pattern} } + + return undef if $seen{$name} || !$stdtype{$name}; + return $stdtype{$name}->{pattern}; +} + +sub stdactions # ($typename) +{ + my $name = shift; + my %seen = (); + my @actions = (); + while (!$seen{$name} && $stdtype{$name} && $stdtype{$name}->{ind}) + { + $seen{$name} = 1; + push @actions, $stdtype{$name}->{action} + if $stdtype{$name}->{action}; + $name = $stdtype{$name}->{pattern} + } + push @actions, $stdtype{$name}->{action} + if $stdtype{$name}->{action}; + + return @actions; +} + +sub addtype # ($abbrev, $pattern, $action, $ref) +{ + my $typeid = ":$_[0]"; + unless ($_[1] =~ /\S/) { $_[1] = ":s" , $_[3] = 1; } + $stdtype{$typeid} = {}; + $stdtype{$typeid}->{pattern} = "(?:$_[1])" if $_[1] && !$_[3]; + $stdtype{$typeid}->{pattern} = ":$_[1]" if $_[1] && $_[3]; + $stdtype{$typeid}->{action} = $_[2] if $_[2]; + $stdtype{$typeid}->{ind} = $_[3]; +} + +sub new # ($self, $name, $type, $nows) +{ + bless + { name => $_[1], + type => $_[2], + nows => $_[3], + }, ref($_[0])||$_[0]; +} + +sub matcher # ($self, $trailing) +{ + my ($self, $trailing) = @_; + #WAS: $trailing = $trailing ? '(?!\Q'.$trailing.'\E)' : ''; + $trailing = $trailing ? '(?!'.quotemeta($trailing).')' : ''; + my $stdtype = stdtype($self->{type}); + + if (!$stdtype && $self->{type} =~ m#\A:/([^/]+)/\Z#) { $stdtype = $1; } + if (!$stdtype) + { + die "Error: bad type in Getopt::Declare parameter variable specification near '<$self->{name}$self->{type}>'\n"; + } + $stdtype =~ s/\%D/(?:$trailing\\d)/g; + $stdtype =~ s/\%T/$trailing/g; + unless ($stdtype =~ s/\%F//) + { + $stdtype = Getopt::Declare::Arg::negflagpat().$stdtype; + } + return "(?:$stdtype)"; +} + +sub code # ($self, $pos, $package) +{ + my $code = ' + $_VAR_ = q|<' . $_[0]->{name} . '>|; + $_VAL_ = defined $' . ($_[1]+1) . '? $' . ($_[1]+1) . ': undef; + $_VAL_ =~ tr/\0/ / if $_VAL_;'; + + my @actions = stdactions($_[0]->{type}); + foreach ( @actions ) + { + s/(\s*\{)/$1 package $_[2]; /; + $code .= "\n\t\tdo $_;"; + } + + $code .= ' + my $' . $_[0]->{name} . ' = $_VAL_;'; + + return $code; +} + +sub cachecode # ($self, $ownerflag, $itemcount) +{ + return "\t\t\$self->{'$_[1]'}{'<$_[0]->{name}>'} = \$$_[0]->{name};\n" + if $_[2] > 1; + return "\t\t\$self->{'$_[1]'} = \$$_[0]->{name};\n"; +} + +sub trailer { '' }; # MEANS TRAILING PARAMETER VARIABLE + +sub ows +{ + return '(?:\s|\0)*('.$_[1].')' unless $_[0]->{nows}; + return '('.$_[1].')'; +} + + +package Getopt::Declare::ArrayArg; + +use vars qw { @ISA }; +@ISA = qw ( Getopt::Declare::ScalarArg ); + +sub matcher # ($self, $trailing) +{ + my ($self, $trailing) = @_; + my $suffix = (defined $trailing && !$trailing) ? '\s+' : ''; + my $scalar = $self->SUPER::matcher($trailing); + + return $scalar.'(?:\s+'.$scalar.')*'.$suffix; +} + +sub code # ($self, $pos, $package) +{ + my $code = ' + $_VAR_ = q|<' . $_[0]->{name} . '>|; + $_VAL_ = undef; + my @' . $_[0]->{name} . ' = + map { tr/\0/ /; $_ } split " ", $'.($_[1]+1)."||'';\n"; + + + my @actions = Getopt::Declare::ScalarArg::stdactions($_[0]->{type}); + if (@actions) + { + $code .= ' + foreach $_VAL_ ( @' . $_[0]->{name} . ' ) + {'; + foreach ( @actions ) + { + s/(\s*\{)/$1 package $_[2]; /; + $code .= "\t\t\tdo $_;\n"; + } + $code .= ' + }'; + } + return $code; +} + +sub cachecode # ($self, $ownerflag, $itemcount) +{ + return "\t\t\$self->{'$_[1]'}{'<$_[0]->{name}>'} = [] + unless \$self->{'$_[1]'}{'<$_[0]->{name}>'}; + push \@{\$self->{'$_[1]'}{'<$_[0]->{name}>'}}, \@$_[0]->{name};\n" + if $_[2] > 1; + return "\t\t\$self->{'$_[1]'} = [] + unless \$self->{'$_[1]'}; + push \@{\$self->{'$_[1]'}}, \@$_[0]->{name};\n"; +} + + +package Getopt::Declare::Punctuator; + +sub new # ($self, $text, $nows) +{ + bless { text => $_[1], nows => $_[2] } +} + +sub matcher # ($self, $trailing) +{ + #WAS: Getopt::Declare::Arg::negflagpat() . '\Q' . $_[0]->{text} . '\E'; + Getopt::Declare::Arg::negflagpat() . quotemeta($_[0]->{text}); +} + +sub code # ($self, $pos) +{ + " + \$_PUNCT_{'" . $_[0]->{text} . "'" . '} = $' . ($_[1]+1) . ";\n"; +} + +sub cachecode # ($self, $ownerflag, $itemcount) +{ + return "\t\t\$self->{'$_[1]'}{'$_[0]->{text}'} = \$_PUNCT_{'$_[0]->{text}'};\n" + if $_[2] > 1; + return "\t\t\$self->{'$_[1]'} = \$_PUNCT_{'$_[0]->{text}'};\n"; +} + +sub trailer { $_[0]->{text} }; + +sub ows +{ + return '(?:\s|\0)*('.$_[1].')' unless $_[0]->{nows}; + return '('.$_[1].')'; +} + + +package Getopt::Declare::Arg; + +use Text::Balanced qw( extract_bracketed ); + +my $nextID = 0; + +my @helpcmd = qw( -help --help -Help --Help -HELP --HELP -h -H ); +my %helpcmd = map { $_ => 1 } @helpcmd; + +sub besthelp { foreach ( @helpcmd ) { return $_ if exists $helpcmd{$_}; } } +sub helppat { return join '|', keys %helpcmd; } + +my @versioncmd = qw( -version --version -Version --Version + -VERSION --VERSION -v -V ); +my %versioncmd = map { $_ => 1 } @versioncmd; + +sub bestversion {foreach (@versioncmd) { return $_ if exists $versioncmd{$_}; }} +sub versionpat { return join '|', keys %versioncmd; } + +my @flags; +my $posflagpat = ''; +my $negflagpat = ''; +sub negflagpat +{ + $negflagpat = join '', map { "(?!".quotemeta($_).")" } @flags + if !$negflagpat && @flags; + return $negflagpat; +} + +sub posflagpat +{ + $posflagpat = '(?:'.join('|', map { quotemeta($_) } @flags).')' + if !$posflagpat && @flags; + return $posflagpat; +} + +sub new # ($class, $spec, $desc, $dittoflag) +{ + my ($class,$spec,$desc,$ditto) = @_; + my $first = 1; + my $arg; + my $nows; + + my $self = + { + flag => '', + args => [], + actions => [], + ditto => $ditto, + required => 0, + requires => '', + ID => $nextID++, + desc => $spec, + items => 0, + }; + + $self->{desc} =~ s/\A\s*(.*?)\s*\Z/$1/; + + while ($spec) + { + # OPTIONAL + if ($spec =~ s/\A(\s*)\[/$1/) + { + push @{$self->{args}}, new Getopt::Declare::StartOpt; + next; + } + elsif ($spec =~ s/\A\s*\]//) + { + push @{$self->{args}}, new Getopt::Declare::EndOpt; + next; + } + + # ARG + + ($arg,$spec,$nows) = extract_bracketed($spec,'<>'); + if ($arg) + { + $arg =~ m/\A(\s*)(<)([a-zA-Z]\w*)(:[^>]+|)>/ or + die "Error: bad Getopt::Declare parameter variable specification near '$arg'\n"; + + my @details = ( $3, $4, !$first && !length($nows) ); # NAME,TYPE,NOWS + + if ($spec =~ s/\A\.\.\.//) # ARRAY ARG + { + push @{$self->{args}}, + new Getopt::Declare::ArrayArg (@details); + } + else # SCALAR ARG + { + push @{$self->{args}}, + new Getopt::Declare::ScalarArg (@details); + } + $self->{items}++; + next; + } + + # PUNCTUATION + + elsif ( $spec =~ s/\A(\s*)((\\.|[^] \t\n[<])+)// ) + { + my ($ows, $punct) = ($1,$2); + $punct =~ s/\\(?!\\)(.)/$1/g; + if ($first) { $self->{flag} = $punct; push @flags, $punct; } + + else { push @{$self->{args}}, + new Getopt::Declare::Punctuator ($punct,!length($ows)); + $self->{items}++; } + + } + + else { last; } + + } + continue + { + $first = 0; + } + + delete $helpcmd{$self->{flag}} if exists $helpcmd{$self->{flag}}; + delete $versioncmd{$self->{flag}} if exists $versioncmd{$self->{flag}}; + + bless $self; +} + +sub code +{ + my ($self, $owner,$package) = @_; + + my $code = "\n"; + my $flag = $self->{flag}; + my $clump = $owner->{_internal}{clump}; + my $i = 0; + my $nocase = (Getopt::Declare::_nocase() || $self->{nocase} ? 'i' : ''); + + $code .= (!$self->{repeatable}) + ? q# param: while (!$_FOUND_{'# . $self->name . q#'}# + : q# param: while (1#; + + if ($flag && ($clump==1 && $flag !~ /\A[^a-z0-9]+[a-z0-9]\Z/i + || ($clump<3 && @{$self->{args}} ))) + { + $code .= q# && !$_lastprefix#; + } + + $code .= q#) + { + pos $_args = $_nextpos if defined $_args; + %_PUNCT_ = ();#; + + if ($flag) + { + #WAS: $_args =~ m/\G(?:\s|\0)*\Q# . $flag . q#\E/g# . $nocase + $code .= q# + + $_args && $_args =~ m/\G(?:\s|\0)*# . quotemeta($flag) . q#/g# . $nocase + . q# or last; + $_errormsg = q|incorrect specification of '# . $flag . q#' parameter| unless $_errormsg; + + #; + } + elsif ((Getopt::Declare::ScalarArg::stdtype($self->{args}[0]{type})||'') !~ /\%F/) + { + $code .= q# + + last if $_errormsg; + + #; + } + + $code .= q# + $_PARAM_ = '# . $self->name . q#'; + #; + my @trailer; + $#trailer = @{$self->{args}}; + for ($i=$#{$self->{args}} ; $i>0 ; $i-- ) + { + $trailer[$i-1] = $self->{args}[$i]->trailer(); + $trailer[$i-1] = $trailer[$i] unless defined $trailer[$i-1]; + } + + if (@{$self->{args}}) + { + $code .= ' + $_args && $_args =~ m/\G'; + + for ($i=0; $i < @{$self->{args}} ; $i++ ) + { + $code .= + $self->{args}[$i]->ows($self->{args}[$i]->matcher($trailer[$i])) + } + + $code .= '/gx' . $nocase . ' or last;' + } + + + for ($i=0; $i < @{$self->{args}} ; $i++ ) + { + $code .= $self->{args}[$i]->code($i,$package); #, $flag ???? + } + + if ($flag) + { + $code .= q# + if (exists $_invalid{'# . $flag . q#'}) + { + $_errormsg = q|parameter '# . $flag + . q#' not allowed with parameter '| + . $_invalid{'# . $flag . q#'} . q|'|; + last; + } + else + { + foreach (# + . ($owner->{_internal}{mutex}{$flag} + ? join(',', map {"'$_'"} @{$owner->{_internal}{mutex}{$flag}}) + : '()') + . q#) + { + $_invalid{$_} = '# . $flag . q#'; + } + } + # + } + + foreach my $action ( @{$self->{actions}} ) + { + $action =~ s{(\s*\{)} + { $1 package $package; }; + $code .= "\n\t\tdo " . $action . ";\n"; + } + + if ($flag && $self->{items}==0) + { + $code .= "\n\t\t\$self->{'$flag'} = '$flag';\n"; + } + foreach my $subarg ( @{$self->{args}} ) + { + $code .= $subarg->cachecode($self->name,$self->{items}); + } + + if ($flag =~ /\A([^a-z0-9]+)/i) { $code .= '$_lastprefix = "'.quotemeta($1).'";'."\n" } + else { $code .= '$_lastprefix = "";' } + + $code .= q# + $_FOUND_{'# . $self->name . q#'} = 1; + next arg if pos $_args; + $_nextpos = length $_args; + last arg; + } + + #; +} + +sub name +{ + my $self = shift; + return $self->{flag} || "<$self->{args}[0]{name}>"; +} + + +package Getopt::Declare; + +use Text::Balanced qw( :ALL ); +use Text::Tabs qw( expand ); + +# PREDEFINED GRAMMARS + +my %_predef_grammar = +( + "-PERL" => +q{ - Set $ to 1 [repeatable] + { no strict "refs"; ${"::$varname"} = 1 } + +}, + + "-AWK" => +q{ = Set $ to [repeatable] + {no strict "refs"; ${"::$varname"} = $val } + = Set $ to '' [repeatable] + {no strict "refs"; ${"::$varname"} = '' } + +}, +); +my $_predef_grammar = join '|', keys %_predef_grammar; + +sub _quoteat +{ + my $text = shift; + $text =~ s/\A\@/\\\@/; + $text =~ s/([^\\])\@/$1\\\@/; + $text; +} + +sub new # ($self, $grammar; $source) +{ +# HANDLE SHORT-CIRCUITS + + return 0 if @_==3 && (!defined($_[2]) || $_[2] eq '-SKIP'); + +# SET-UP + + my ($_class, $_grammar) = @_; + +# PREDEFINED GRAMMAR? + + if ($_grammar =~ /\A(-[A-Z]+)+/) + { + my $predef = $_grammar; + my %seen = (); + $_grammar = ''; + $predef =~ s{($_predef_grammar)}{ do {$_grammar .= $_predef_grammar{$1} unless $seen{$1}; $seen{$1} = 1; ""} }ge; + return undef if $predef || !$_grammar; + } + +# PRESERVE ESCAPED '['s + + $_grammar =~ s/\\\[/\255/g; + +# MAKE SURE GRAMMAR ENDS WITH A NEWLINE + + $_grammar =~ s/([^\n])\Z/$1\n/; + +# SET-UP + + local $_ = $_grammar; + my @_args = (); + my $_mutex = {}; + my $_action; + my $_strict = 0; + my $_all_repeatable = 0; + my $_lastdesc = undef; + _nocase(0); + Getopt::Declare::ScalarArg::_reset_stdtype(); + +# CONSTRUCT GRAMMAR + + while (length $_ > 0) + { + # COMMENT: + s/\A[ ]*#.*\n// and next; + + # TYPE DIRECTIVE: + + #WAS: if (m/\A\s*\[pvtype:/ and $_action = extract_codeblock($_,'[{}]')) + if (m/\A\s*\[pvtype:/ and $_action = extract_codeblock($_,'[]')) + { + $_action =~ s/.*?\[pvtype:\s*//; + _typedef($_action); + next; + } + + # ACTION + if ($_action = extract_codeblock) + { + # WAS: eval q{no strict;my $ref = sub }._quoteat($_action).q{;1} + my $_check_action = $_action; + $_check_action =~ s{(\s*\{)} + { $1 sub defer(&); sub finish(;\$); sub reject(;\$\$); }; + eval q{no strict;my $ref = sub }.$_check_action.q{;1} + or die "Error: bad action in Getopt::Declare specification:" + . "\n\n$_action\n\n$@\n"; + if ($#_args < 0) + { + die "Error: unattached action in Getopt::Declare specification:\n$_action\n" + . "\t(did you forget the tab after the preceding parameter specification?)\n" + } + push @{$_args[$#_args]->{actions}}, $_action; + next; + } + elsif (m/\A(\s*[{].*)/) + { + die "Error: incomplete action in Getopt::Declare specification:\n$1.....\n" + . "\t(did you forget a closing '}'?)\n"; + } + + # ARG + DESC: + if ( s/\A(.*?\S.*?)(\t.*\n)// ) + { + my $spec = $1; + my $desc = $2; + my $ditto; + $_strict ||= $desc =~ /\Q[strict]/; + + $desc .= $1 while s/\A((?![ ]*({|\n)|.*?\S.*?\t.*?\S).*?\S.*\n)//; + + $_lastdesc and $desc =~ s/\A\s*\[ditto\]/$_lastdesc/ + and $ditto = 1; + $_lastdesc = $desc; + + my $arg = new Getopt::Declare::Arg($spec,$desc,$ditto) ; + push @_args, $arg; + + _infer($desc, $arg, $_mutex); + next; + } + + + # OTHERWISE: DECORATION + + s/((?:(?!\[pvtype:).)*)(\n|(?=\[pvtype:))//; + my $decorator = $1; + $_strict ||= $decorator =~ /\Q[strict]/; + _infer($decorator, undef, $_mutex); + $_all_repeatable = 1 if $decorator =~ /\[repeatable\]/; + } + + my $_lastactions; + foreach ( @_args ) + { + if ($_lastactions && $_->{ditto} && !@{$_->{actions}}) + { $_->{actions} = $_lastactions } + else + { $_lastactions = $_->{actions} } + + if ($_all_repeatable) + { + $_->{repeatable} = 1; + } + } + + @_args = sort + { + length($b->{flag}) <=> length($a->{flag}) + or + $b->{flag} eq $a->{flag} and $#{$b->{args}} <=> $#{$a->{args}} + or + $a->{ID} <=> $b->{ID} + + } @_args; + +# CONSTRUCT OBJECT ITSELF + + my $clump = ($_grammar =~ /\[cluster:\s*none\s*\]/i) ? 0 + : ($_grammar =~ /\[cluster:\s*singles?\s*\]/i) ? 1 + : ($_grammar =~ /\[cluster:\s*flags?\s*\]/i) ? 2 + : ($_grammar =~ /\[cluster:\s*any\s*\]/i) ? 3 + : ($_grammar =~ /\[cluster:(.*)\s*\]/i) ? + die "Error: unknown clustering mode: [cluster:$1]\n" + : 3; + + my $self = bless + { + _internal => + { + args => [@_args], + mutex => $_mutex, + usage => $_grammar, + helppat => Getopt::Declare::Arg::helppat(), + verspat => Getopt::Declare::Arg::versionpat(), + strict => $_strict, + clump => $clump, + source => '', + 'caller' => scalar caller(), + } + }, ref($_class)||$_class; + + +# VESTIGAL DEBUGGING CODE + + open (CODE, ">.CODE") + and print CODE $self->code($self->{_internal}{'caller'}) + and close CODE + if $::Declare_debug; + +# DO THE PARSE (IF APPROPRIATE) + + + if (@_==3) { return undef unless defined $self->parse($_[2]) } + else { return undef unless defined $self->parse(); } + + return $self; +} + +sub _get_nextline { scalar <> } + +sub _load_sources # ( \$_get_nextline, @files ) +{ + my $text = ''; + my @found = (); + my $gnlref = shift; + foreach ( @_ ) + { + open FILE, $_ or next; + if (-t FILE) + { + push @found, ''; + $$gnlref = \&_get_nextline; + } + else + { + push @found, $_; + $text .= join "\n", ; + } + } + return undef unless @found; + $text = unless $text; + return ( $text, join(" or ",@found)); +} + + +sub parse # ($self;$source) +{ + my ( $self, $source ) = @_; + my $_args = (); + my $_get_nextline = sub { undef }; + if (@_>1) + { + if (!defined $source) + { + return 0; + } + elsif (isa($source,'CODE')) + { + $_get_nextline = $source; + $_args = &{$_get_nextline}($self); + $source = '[SUB]'; + } + elsif (isa($source,'GLOB')) + { + if (-t *$source) + { + $_get_nextline = \&_get_nextline ; + $_args = ; + $source = ''; + } + else + { + $_args = join ' ', (<$source>); + $_args =~ tr/\t\n/ /s; + $source = ref($source); + } + } + elsif (isa($source,'IO::Handle')) + { + if (!($source->fileno) && -t) + { + $_get_nextline = \&_get_nextline ; + $_args = ; + $source = ''; + } + else + { + $_args = join ' ', (<$source>); + $_args =~ tr/\t\n/ /s; + $source = ref($source); + } + } + elsif (ref($source) eq 'ARRAY') + { + if (@$source == 1 && (!defined($source->[0]) + || $source->[0] eq '-BUILD' + || $source->[0] eq '-SKIP') ) + { + return 0; + } + elsif (@$source == 1 && $source->[0] eq '-STDIN') + { + $_get_nextline = \&_get_nextline ; + $_args = ; + $source = ''; + } + elsif (@$source == 1 && $source->[0] eq '-CONFIG') + { + my $progname = "$0rc"; + $progname =~ s#.*/##; + ($_args,$source) = _load_sources(\$_get_nextline,"$ENV{HOME}/.$progname", ".$progname"); + } + else + { + my $stdin; + ($_args,$source) = _load_sources(\$_get_nextline,@$source); + } + } + else # LITERAL STRING TO PARSE + { + $_args = $source; + substr($source,7) = '...' if length($source)>7; + $source = "\"$source\""; + } + return 0 unless defined $_args; + $source = " (in $source)"; + } + else + { + foreach (@ARGV) { $_ =~ tr/ \t\n/\0\0\0/; } + $_args = join(' ', @ARGV); + $source = ''; + } + + $self->{_internal}{source} = $source; + + if (!eval $self->code($self->{_internal}{'caller'})) + { + die "Error: in generated parser code:\n$@\n" + if $@; + return undef; + } + + return 1; +} + +sub type # ($abbrev, $pattern, $action) +{ + &Getopt::Declare::ScalarArg::addtype; +} + +sub _enbool +{ + my $expr = shift; + $expr =~ s/\s*\|\|\s*/ or /g; + $expr =~ s/\s*&&\s*/ and /g; + $expr =~ s/\s*!\s*/ not /g; + return $expr; +} + +sub _enfound +{ + my $expr = shift; + my $original = $expr; + $expr =~ s/((?:&&|\|\|)?\s*(?:[!(]\s*)*)([^ \t\n|&\)]+)/$1\$_FOUND_{'$2'}/gx; + die "Error: bad condition in [requires: $original]\n" + unless eval 'no strict; my $ref = sub { '.$expr.' }; 1'; + return $expr; +} + +my $_nocase = 0; + +sub _nocase +{ + $_nocase = $_[0] if $_[0]; + return $_nocase; +} + +sub _infer # ($desc, $arg, $mutex) +{ + my ($desc, $arg, $mutex) = @_; + + _mutex($mutex, split(' ',$1)) + while $desc =~ s/\[mutex:\s*(.*?)\]//i; + + if ( $desc =~ m/\[no\s*case\]/i) + { + if ($arg) { $arg->{nocase} = 1 } + else { _nocase(1); } + } + + if (defined $arg) + { + _exclude($mutex, $arg->name, (split(' ',$1))) + if $desc =~ m/.*\[excludes:\s*(.*?)\]/i; + $arg->{requires} = $1 + if $desc =~ m/.*\[requires:\s*(.*?)\]/i; + + $arg->{required} = ( $desc =~ m/\[required\]/i ); + $arg->{repeatable} = ( $desc =~ m/\[repeatable\]/i ); + } + + _typedef($desc) while $desc =~ s/.*?\[pvtype:\s*//; +} + +sub _typedef +{ + my $desc = $_[0]; + my ($name,$pat,$action,$ind); + + ($name,$desc) = (extract_quotelike($desc))[5,1]; + do { $desc =~ s/\A\s*([^] \t\n]+)// and $name = $1 } unless $name; + die "Error: bad type directive (missing type name): [pvtype: " + . substr($desc,0,index($desc,']')||20). "....\n" + unless $name; + + ($pat,$desc,$ind) = (extract_quotelike($desc,'\s*:?\s*'))[5,1,2]; + do { $desc =~ s/\A\s*(:?)\s*([^] \t\n]+)// + and $pat = $2 and $ind = $1 } unless $pat; + $pat = '' unless $pat; + + $action = extract_codeblock($desc) || ''; + die "Error: bad type directive (expected closing ']' but found" + . "'$1' instead): [pvtype: $name " . ($pat?"/$pat/":'') + . " $action $1$2....\n" if $desc =~ /\A\s*([^] \t\n])(\S*)/; + + Getopt::Declare::ScalarArg::addtype($name,$pat,$action,$ind=~/:/); +} + +sub _ditto # ($originalflag, $orginaldesc, $extra) +{ + my ($originalflag, $originaldesc, $extra) = @_; + if ($originaldesc =~ /\n.*\n/) + { + $originaldesc = "Same as $originalflag "; + } + else + { + chomp $originaldesc; + $originaldesc =~ s/\S/"/g; + 1 while $originaldesc =~ s/"("+)"/ $1 /g; + $originaldesc =~ s/""/" /g; + } + return "$originaldesc$extra\n"; +} + +sub _mutex # (\%mutex, @list) +{ + my ($mref, @mutexlist) = @_; + + foreach my $flag ( @mutexlist ) + { + $mref->{$flag} = [] unless $mref->{$flag}; + foreach my $otherflag ( @mutexlist ) + { + next if ($flag eq $otherflag); + push @{$mref->{$flag}}, $otherflag; + } + } +} + +sub _exclude # (\%mutex, $excluded, @list) +{ + my ($mref, $excluded, @mutexlist) = @_; + + foreach my $flag ( @mutexlist ) + { + unless ($flag eq $excluded) + { + $mref->{$flag} = [] unless $mref->{$flag}; + push @{$mref->{$excluded}}, $flag; + push @{$mref->{$flag}}, $excluded; + } + } +} + +sub version +{ + # my $filedate = localtime(time - 86400 * -M $0); + my $filedate = localtime((stat $0)[9]); + if ($::VERSION) { print "\n\t$0: version $::VERSION ($filedate)\n\n" } + else { print "\n\t$0: version dated $filedate\n\n" } + exit $_[1] if defined $_[1]; +} + +sub usage +{ + my $self = $_[0]; + local $_ = $self->{_internal}{usage}; + + my $lastflag = undef; + my $lastdesc = undef; + + my $usage = ''; + my $uoff; + my $decfirst; + my $ditto; + + while (length $_ > 0) + { + # COMMENT: + s/\A[ ]*#.*\n// and next; + + # TYPE DIRECTIVE: + + if (m/\A\s*\[pvtype:/ and extract_codeblock($_,'[{}]')) + { + next; + } + + # ACTION + extract_codeblock + and do { s/\A[ ]*\n//; + $decfirst = 0 unless defined $decfirst; + next; }; + + # ARG + DESC: + if ( s/\A(.*?\S.*?\t+)(.*?\n)// ) + { + $decfirst = 0 unless defined $decfirst; + + my ($spec) = expand $1; + my ($desc) = expand $2; + + $desc .= (expand $1)[0] + while s/\A((?![ ]*({|\n)|.*?\S.*?\t.*?\S).*?\S.*\n)//; + + next if $desc =~ /\[undocumented\]/i; + + $uoff = 0; + $spec =~ s/(<[a-zA-Z]\w*):([^>]+)>/$uoff+=1+length $2 and "$1>"/ge; + $ditto = $desc =~ /\A\s*\[ditto\]/; + $desc =~ s/^\s*\[.*?\]\s*\n//gm; + $desc =~ s/\[.*?\]//g; + + if ($ditto) + { $desc = ($lastdesc? _ditto($lastflag,$lastdesc,$desc) : "" ) } + elsif ($desc =~ /\A\s*\Z/) + { next; } + else + { $lastdesc = $desc; } + $spec =~ /\A\s*(\S+)/ and $lastflag = $1; + + $usage .= $spec . ' ' x $uoff . $desc; + + next; + }; + + + # OTHERWISE, DECORATION + if (s/((?:(?!\[pvtype:).)*)(\n|(?=\[pvtype:))//) + { + my $desc = $1.($2||''); + $desc =~ s/^(\s*\[.*?\])+\s*\n//gm; + $desc =~ s/\[.*?\]//g; + $decfirst = 1 unless defined $decfirst + or $desc =~ m/\A\s*\Z/; + $usage .= $desc; + } + } + + my $required = ''; + + foreach my $arg ( @{$self->{_internal}{args}} ) + { + if ($arg->{required}) + { + $required .= ' ' . $arg->{desc} . ' '; + } + } + + $usage =~ s/\255/[/g; # REINSTATE ESCAPED '['s + + $required =~ s/<([a-zA-Z]\w*):[^>]+>/<$1>/g; + + my $helpcmd = Getopt::Declare::Arg::besthelp; + my $versioncmd = Getopt::Declare::Arg::bestversion; + + my $PAGER = \*STDOUT; + + if (eval { require IO::Pager }) + { + $PAGER = new IO::Pager ( resume => 1 ); + } + + unless ($self->{_internal}{source}) + { + print $PAGER "\nUsage: $0 [options] $required\n"; + print $PAGER " $0 $helpcmd\n" if $helpcmd; + print $PAGER " $0 $versioncmd\n" if $versioncmd; + print $PAGER "\n" unless $decfirst && $usage =~ /\A[ \t]*\n/; + } + print $PAGER "Options:\n" unless $decfirst; + print $PAGER $usage; + + exit $_[1] if defined $_[1]; +} + +sub unused { + return @{$_[0]->{_internal}{unused}} if wantarray; + return join " ", @{$_[0]->{_internal}{unused}}; +} + +sub flatten { + my ($val, $nested) = @_; + if (ref $val eq 'ARRAY') { + return join " ", map {flatten($_,1)} @$val; + } + elsif (ref $val eq 'HASH') { + return join " ", map { + $nested || /^-/ ? ($_, flatten($val->{$_},1)) + : (flatten($val->{$_},1)) + } keys %$val; + } + else { + return $val; + } +} + +sub used { + my $self = shift; + my @used = map { /^_/ ? () : ($_, $self->{$_}) } keys %$self; + return @used if wantarray; + return join " ", map { flatten $_ } @used; +} + +sub code +{ + my $self = shift; + my $package = shift||'main'; + my $code = q# + + do + { + my @_deferred = (); + my @_unused = (); + sub # . $package . q#::defer (&); + { + package # . $package . q#; local $^W; + *defer = sub (&) { push @_deferred, $_[0]; } + } + my %_FOUND_ = (); + my $_errors = 0; + my $_nextpos; + my %_invalid = (); + my $_lastprefix = ''; + my $_finished = 0; + my %_PUNCT_; + my $_errormsg; + my $_VAL_; + my $_VAR_; + my $_PARAM_; + + sub # . $package . q#::reject (;$$); + sub # . $package . q#::finish (;$); + + { + package # . $package . q#; local $^W; + *reject = sub (;$$) { local $^W; if (!@_ || $_[0]) { $_errormsg = $_[1] if defined $_[1]; last param; } }; + *finish = sub (;$) { if (!@_ || $_[0]) { $_finished = 1; } }; + } + + $_nextpos = 0; + arg: while (!$_finished) + { + $_errormsg = ''; + # . ( $self->{_internal}{clump} ? q# + while ($_lastprefix) + { + my $substr = substr($_args,$_nextpos); + $substr =~ m/\A(?!\s|\0|\Z)# + . Getopt::Declare::Arg::negflagpat() . q#/ + or do { $_lastprefix='';last}; + "$_lastprefix$substr" =~ m/\A(# + . Getopt::Declare::Arg::posflagpat() + . q#)/ + or do { $_lastprefix='';last}; + substr($_args,$_nextpos,0) = $_lastprefix; + last; + } + # : '') . q# + pos $_args = $_nextpos if defined $_args; + + $self->usage(0) if $_args && $_args =~ m/\G(# . $self->{_internal}{helppat} . q#)(\s|\0|\Z)/g; + $self->version(0) if $_args && $_args =~ m/(# . $self->{_internal}{verspat} . q#)(\s|\0|\Z)/; + + #; + + foreach my $arg ( @{$self->{_internal}{args}} ) + { + $code .= $arg->code($self,$package); + } + + $code .= q# + + if ($_lastprefix) + { + pos $_args = $_nextpos+length($_lastprefix); + $_lastprefix = ''; + next; + } + + pos $_args = $_nextpos; + $_args && $_args =~ m/\G(?:\s|\0)*(\S+)/g or last; + if ($_errormsg) { print STDERR "Error"."$self->{_internal}{source}: $_errormsg\n" } + + else { push @_unused, $1; } + $_errors++ if ($_errormsg); + } + continue + { + $_nextpos = pos $_args if defined $_args; + if (defined $_args and $_args =~ m/\G(\s|\0)*\Z/g) + { + $_args = &{$_get_nextline}($self); + last unless defined($_args); + $_nextpos = 0; + $_lastprefix = ''; + } + } + #; + + foreach my $arg ( @{$self->{_internal}{args}} ) + { + next unless $arg->{required}; + $code .= q# + do { print STDERR "Error"."$self->{_internal}{source}".': required parameter # . $arg->name . q# not found.',"\n"; $_errors++ } + unless $_FOUND_{'# . $arg->name . q#'}#; + if ($self->{_internal}{mutex}{$arg->name}) + { + foreach my $mutex (@{$self->{_internal}{mutex}{$arg->name}}) + { + $code .= q# or $_FOUND_{'# . $mutex . q#'}#; + } + } + $code .= ';'; + } + + foreach my $arg ( @{$self->{_internal}{args}} ) + { + if ($arg->{requires}) + { + $code .= q# + do { print STDERR q|Error|.$self->{_internal}{source}.q|: parameter '# . $arg->name + . q#' can only be specified with '# . _enbool($arg->{requires}) + . q#'|,"\n"; $_errors++ } + if $_FOUND_{'# . $arg->name . "'} && !(" . _enfound($arg->{requires}) . ');' + } + } + + $code .= q# + push @_unused, split(' ', substr($_args,$_nextpos)) + if $_args && $_nextpos && length($_args) >= $_nextpos; + #; + + if ($self->{_internal}{strict}) + { + $code .= q# + unless ($_nextpos < length($_args||'')) + { + foreach (@_unused) + { + tr/\0/ /; + print STDERR "Error"."$self->{_internal}{source}: unrecognizable argument ('$_')\n"; + $_errors++; + } + } + # + } + + $code .= q# + + if ($_errors && !$self->{_internal}{source}) + { + print STDERR "\n(try '$0 ".'# . Getopt::Declare::Arg::besthelp + . q#'."' for more information)\n"; + } + + $self->{_internal}{unused} = [map { tr/\0/ /; $_ } @_unused]; + @ARGV = @{$self->{_internal}{unused}} + unless $self->{_internal}{source}; + + unless ($_errors) { foreach (@_deferred) { &$_ } } + + !$_errors; + + } + #; +} + +1; +__END__ + +=head1 NAME + +Getopt::Declare - Declaratively Expressed Command-Line Arguments via Regular Expressions + +=head1 VERSION + +This document describes version 1.08 of Getopt::Declare, +released May 21, 1999. + +=head1 SYNOPSIS + + use Getopt::Declare; + + $args = new Getopt::Declare ($specification_string, $optional_source); + + +=head1 DESCRIPTION + +=head2 Overview + +F is I command-line argument parser, +one which is specifically designed to be powerful but exceptionally +easy to use. + +To parse the command-line in C<@ARGV>, one simply creates a +F object, by passing C a +specification of the various parameters that may be encountered: + + $args = new Getopt::Declare($specification); + +The specification is a single string such as this: + + $specification = q( + + -a Process all data + + -b Set mean byte length threshold to + { bytelen = $N; } + + +c Create new file + + --del Delete old file + { delold() } + + delete [ditto] + + e x Expand image to height and width + { expand($H,$W); } + + -F ... Process named file(s) + { defer {for (@file) {process()}} } + + =getrand [] Get a random number + (or, optionally, of them) + { $N = 1 unless defined $N; } + + -- Traditionally indicates end of arguments + { finish } + ); + +in which the syntax of each parameter is declared, along with a +description and (optionally) one or more actions to be performed when +the parameter is encountered. The specification string may also +include other usage formatting information (such as group headings or +separators) as well as standard Perl comments (which are ignored). + +Calling C parses the contents of the array C<@ARGV>, +extracting any arguments which match the parameters defined in the +specification string, and storing the parsed values as hash elements +within the new F object being created. + +Other features of the F package include: + +=over 4 + +=item * + +The use of full Perl regular expressions to constrain matching +of parameter components. + +=item * + +Automatic generation of error, usage and version information. + +=item * + +Optional conditional execution of embedded actions (i.e. only on +successful parsing of the entire command-line) + +=item * + +Strict or non-strict parsing (unrecognized command-line elements may either +trigger an error or may simply be left in C<@ARGV>) + +=item * + +Declarative specification of various inter-parameter relationships (for +example, two parameters may be declared mutually exclusive and this +relationship will then be automatically enforced). + +=item * + +Intelligent clustering of adjacent flags (for example: the +command-line sequence "S<-a -b -c>" may be abbreviated to "-abc", unless +there is also a C<-abc> flag declared). + +=item * + +Selective or global case-insensitivity of parameters. + +=item * + +The ability to parse files (especially configuration files) instead of +the command-line. + +=back + +=head2 Terminology + +The terminology of command-line processing is often confusing, with various +terms (such as "argument", "parameter", "option", "flag", etc.) +frequently being used interchangeably and inconsistently in the various +F packages available. In this documentation, the following +terms are used consistently: + +=over 4 + +=item "command-line" + +The space-separated concatenation of the elements of the array C<@ARGV> +at the time a F object is created. + +=item "parameter specification" (or just "parameter") + +A specification of a single entity which may appear in the +command-line. Always includes at least one syntax for the entity. +Optionally may include other (I) syntaxes, one or more +I of the entity, and/or I to be performed when +the entity is encountered. For example, the following is a single +parameter specification (with two variants): + + --window x Set window to by + { setwin($width,$height); } + + --window x@, Set window size and centroid + { setwin($w,$h,$x,$y); } + + +=item "argument" + +A substring of the command-line which matches a single parameter variant. +Unlike some other Getopt:: packages, in F an argument +may be a single element of C<@ARGV>, or part of a single C<@ARGV> element, +or the concatenation of several adjacent C<@ARGV> elements. + + +=item "parameter definition" + +A specification of one actual syntax variant matched by a parameter. Always +consists of a leading I or I, +optionally followed by one or more I (that is, +other parameter variables or I). In the above example, +CheightE x EwidthE>> is a parameter definition. + + +=item "parameter flag" (or just "flag") + +A sequence of non-space characters which introduces a parameter. Traditionally +a parameter flag begins with "-" or "--", but F allows +almost any sequence of characters to be used as a flag. In the above example, +C<--window> is the parameter flag. + + +=item "parameter variable" + +A place-holder (within a parameter specification) for a value that +will appear in any argument matching that parameter. In the above example, +CheightE>, CwidthE>, ChE>, CyE>, +CxE>, and CyE> are all parameter variables. + + +=item "parameter punctuator" (or just "punctuator") + +A literal sequence of characters (within a parameter specification) +which will appear in any argument matching that parameter. In the above +example, the literals C and C<@> are punctuators. + + +=item "parameter description" + +A textual description of the purpose and/or use of a particular variant of +parameter. In the above examples, the string: + + Set window to by + +is a parameter description. + + +=item "parameter action" (or just "action") + +A block of Perl code to be executed in response to encountering a specific +parameter. In the above example: + + { setwin($width,$height); } + +is a parameter action. + +=item "parameter variants" + +One or more different syntaxes for a single parameter, all sharing +the same leading flag, but having different trailing parameter +variables and/or punctuators. F considers all parameter +definitions with the same leading flag to be merely variant forms of +a single "underlying" parameter. The above example shows two parameter +variants for the C<--window> parameter. + +=back + + +=head2 Parameter definitions + +As indicated above, a parameter specification consists of three +parts: the parameter definition, a textual description, and any +actions to be performed when the parameter is matched. + +The parameter definition consists of a leading flag or parameter +variable, followed by any number of parameter variables or +punctuators, optionally separated by spaces. The parameter definition +is terminated by one or more tabs (at least one trailing tab I +be present). + +For example, all of the following are valid F parameter +definitions: + + -v + in= + +range .. + --lines - + ignore bad lines + + +Note that each of the above examples has at least one trailing tab +(even if you can't see them). Note too that this hodge-podge of +parameter styles is certainly not recommended within a single program, +but is shown so as to illustrate some of the range of parameter syntax +conventions F supports. + +The spaces between components of the parameter definition are optional but +significant, both in the definition itself and in the arguments that +the definition may match. If there is no space between components in the +specification, then no space is allowed between corresponding arguments +on the command-line. If there I space between components of the +specification, then space between those components is optional on the +command-line. + +For example, the C<--lines> parameter above matches: + + --lines1-10 + --lines 1-10 + --lines 1 -10 + --lines 1 - 10 + --lines1- 10 + +If it were instead specified as: + + --lines - + +then it would match only: + + --lines1-10 + --lines 1-10 + +Note that the optional nature of spaces in parameter specification implies that +flags and punctuators cannot contain the character '<' (which is taken as the +delimiter for a parameter variable) nor the character '[' (which +introduces an optional parameter component - see +L<"Optional parameter components">). + + +=head2 Types of parameter variables + +By default, a parameter variable will match a single blank-terminated +or comma-delimited string. For example, the parameter: + + -val + +would match any of the following the arguments: + + -value # <- "ue" + -val abcd # <- "abcd" + -val 1234 # <- "1234" + -val "a value" # <- "a value" + + +It is also possible to restrict the types of values which may be +matched by a given parameter variable. For example: + + -limit Set threshold to some (real) value + -count Set count to (must be an integer) + +If a parameter variable is suffixed with ":n", it will match any +reasonable numeric value, whilst the ":i" suffix restricts a +parameter variable to only matching integer values. +These two "type specifiers" are the simplest examples of a much more +powerful mechanism, which allows parameter variables to be restricted +to matching any specific regular expression. See L<"Defining new +parameter variable types">. + +Parameter variables are treated as scalars by default, but this too +can be altered. Any parameter variable immediately followed by +an ellipsis (C<...>) is treated as a list variable, and matches its +specified type sequentially as many times as possible. For example, +the parameter specification: + + -pages ... + +would match either of the following arguments: + + -pages 1 + -pages 1 2 7 20 + +Note that both scalar and list parameter variables are "respectful" of the +flags of other parameters as well as their own trailing punctuators. +For example, given the specifications: + + -a + -b ... + -c ... ; + +The following arguments will be parsed as indicated: + + -b -d -e -a # <- ("-d", "-e") + -b -d ; # <- ("-d", ";") + -c -d ; # <- ("-d") + +List parameter variables are also "repectful" of the needs of +subsequent parameter variables. That is, a parameter specification +like: + + -copy ... + +will behave as expected, putting all but the last string after the C<-copy> +flag into the parameter variable CfilesE>, whilst the very +last string is assigned to CdirE>. + + +=head2 Optional parameter components + +Except for the leading flag, any part of a parameter definition +may be made optional by placing it in square brackets. +For example: + + +range [..] [] + +which matches any of: + + +range 1..10 + +range 1.. + +range 1 10 + +range 1 + +List parameter variables may also be made optional (the ellipsis must +follow the parameter variable name immediately, so it goes I +the square brackets): + + -list [...] + +Two or more parameter components may be made jointly optional, by specifying +them in the same pair of brackets. Optional components may also be nested. For +example: + + -range [.. [] ] + +Scalar optional parameter variables (such as C<[EtoE]>) +are given undefined values if they are skipped during +a successful parameter match. List optional parameter variables (such as +C<[EpageE...]>) are assigned an empty list if unmatched. + +One important use for optional punctuators is to provide abbreviated +versions of specific flags. For example: + + -num[eric] # Match "-num" or "-numeric" + -lexic[ographic]al # Match "-lexical" or "-lexicographical" + -b[ells+]w[histles] # Match "-bw" or "-bells+whistles" + +Note that the actual flags for these three parameters are C<-num>, C<-lexic> +and C<-b>, respectively. + +=head2 Parameter descriptions + +Providing a textual description for each parameter (or parameter +variant) is optional, but strongly recommended. Apart from providing internal +documentation, parameter descriptions are used in the automatically-generated +usage information provided by F. + +Descriptions may be placed after the tab(s) following the +parameter definition and may be continued on subsequent lines, +provided those lines do not contain any tabs after the first +non-whitespace character (because any such line will instead be +treated as a new parameter specification). The description is +terminated by a blank line, an action specification (see below) or +another parameter specification. + +For example: + + -v Verbose mode + in= Specify input file + (will fail if file does not exist) + + +range .. Specify range of columns to consider + --lines - Specify range of lines to process + + ignore bad lines Ignore bad lines :-) + + Specify an output file + +The parameter description may also contain special directives which +alter the way in which the parameter is parsed. See the various +subsections of L<"ADVANCED FEATURES"> for more information. + + +=head2 Actions + +Each parameter specification may also include one or more blocks of +Perl code, specified in a pair of curly brackets (which I start on +a new line). + +Each action is executed as soon as the corresponding parameter is +successfully matched in the command-line (but see L<"Deferred actions"> +for a means of delaying this response). + +For example: + + -v Verbose mode + { $::verbose = 1; } + -q Quiet mode + { $::verbose = 0; } + +Actions are executed (as C blocks) in the package in which the +F object containing them was created. Hence they +have access to all package variables and functions in that namespace. + +In addition, each parameter variable belonging to the corresponding +parameter is made available as a (block-scoped) Perl variable with the +same name. For example: + + +range .. Set range + { setrange($from, $to); } + + -list ... Specify pages to list + { foreach (@page) + { + list($_) if $_ > 0; + } + } + +Note that scalar parameter variables become scalar Perl variables, +and list parameter variables become Perl arrays. + + +=head2 Predefined variables available in actions + +Within an action the following variables are also available: + +=over 4 + +=item C<$_PARAM_> + +Stores the identifier of the current parameter: either the leading +flag or, if there is no leading flag, the name of the first parameter +variable. + + +=item C<%_PUNCT_> + +Stores the substring matched by each punctuator in the current parameter. +The hash is indexed by the punctuator itself. The main purpose of this variable +is to allow actions to check whether optional punctuators were in fact matched. +For example: + + -v[erbose] Set verbose mode + (doubly verbose if full word used) + { if ($_PUNCT_{"erbose"}) { $verbose = 2; } + else { $verbose = 1; } + } + +=item C<%_FOUND_> + +This hash stores boolean values indicating whether or not a given +parameter has already been found. The hash keys are the leading flags +or parameter variables of each parameter. For instance, the following +specification makes the C<-q> and C<-v> parameters mutually exclusive +(but see L<"Parameter dependencies"> for a I easier way to achieve +this effect): + + -v Set verbose mode + { die "Can't be verbose *and* quiet!\n" + if $_FOUND_{"-q"}; + } + + -q Set quiet mode + { die "Can't be quiet *and* verbose!\n" + if $_FOUND_{"-v"}; + } + +For reasons that will be explained in L<"Rejection and termination">, +a given parameter is not marked as found until I its +associated actions are executed. That is, C<$_FOUND_{$_PARAM_}> will not +(usually) be true during a parameter action. + +=back + +Note that, although numerous other internal variables on which the +generated parser relies are also visible within parameter actions, +accessing any of them may have Dire Consequences. Moreover, these +other variables may no longer be accessible (or even present) in +future versions of F. All such internal variables +have names beginning with an underscore. Avoiding such variables names +will ensure there are no conflicts between actions and the parser +itself. + + +=head2 The command-line parsing process + +Whenever a F object is created, the current command-line +is parsed by sequentially, by attempting to match each parameter +in the object's specification string against the current elements in the +C<@ARGV> array (but see L<"Parsing from other sources">). The order +in which parameters are tried against C<@ARGV> is determined by +three rules: + +=over 4 + +=item 1. + +Parameters with longer flags are tried first. Hence the command-line +argument "-quiet" would be parsed as matching the parameter C<-quiet> +rather than the parameter CstringE>>, even if the C<-q> +parameter was defined first. + +=item 2. + +Parameter I with the most components are +matched first. Hence the argument "-rand 12345" would be parsed as matching +the parameter variant CseedE>>, rather than the +variant C<-rand>, even if the "shorter" C<-rand> variant was defined first. + +=item 3. + +Otherwise, parameters are matched in the order they are defined. + +=back + +Elements of C<@ARGV> which do not match any defined parameter are collected +during the parse and are eventually put back into C<@ARGV> +(see L<"Strict and non-strict command-line parsing">). + + +=head1 ADVANCED FEATURES + +=head2 Case-insensitive parameter matching + +By default, a F object parses the command-line in +a I manner. The C<[nocase]> directive enables a specific +parameter (or, alternatively, I parameters) to be matched +case-insensitively. + +If a C<[nocase]> directive is included in the description of a +specific parameter variant, then that variant (only) will be matched +without regard for case. For example, the specification: + + -q Quiet mode [nocase] + + -v Verbose mode + +means that the arguments "S<-q>" and "S<-Q>" will both match the C<-q> parameter, but +that only "S<-v>" (and I "S<-V>") will match the C<-v> parameter. + +If a C<[nocase]> directive appears anywhere I a parameter description, +then the entire specification is declared case-insensitive and all parameters +defined in that specification are matched without reagrd to case. + + +=head2 Termination and rejection + +It is sometimes useful to be able to terminate command-line +processing before all arguments have been parsed. To this end, +F provides a special local operator (C) which +may be used within actions. The C operator takes a single optional +argument. If the argument is true (or omitted), +command-line processing is terminated at once (although the current +parameter is still marked as having been successfully matched). For +example: + + -- Traditional argument list terminator + { finish } + + -no-- Use non-traditional terminator instead + { $nontrad = 1; } + + ## Non-traditional terminator (only valid if -no-- flag seen) + { finish($nontrad); } + +It is also possible to reject a single parameter match from within an +action (and then continue trying other candidates). This allows +actions to be used to perform more sophisticated tests on the type of +a parameter variable, or to implement complicated parameter +interdependencies. + +To reject a parameter match, the C operator is used. The +C operator takes an optional argument. +If the argument is true (or was omitted), the current parameter +match is immediately rejected. For example: + + -ar Set aspect ratio (must be in the range (0..1]) + { + $::sawaspect++; + reject $R <= 0 || $R > 1 ; + setaspect($R); + } + + -q Quiet option (not available on Wednesdays) + { + reject((localtime)[6] == 3); + $::verbose = 0; + } + +Note that any actions performed I the call to C will +still have effect (for example, the variable C<$::sawaspect> remains +incremented even if the aspect ratio parameter is subsequently rejected). + +The C operator may also take a second argument, which is +used as an error message if the rejected argument subsequently +fails to match any other parameter. For example: + + -q Quiet option (not available on Wednesdays) + { + reject((localtime)[6] == 3 => "Not today!"); + $::verbose = 0; + } + + +=head2 Specifying other parameter variable types + +As was mentioned in L<"Type of parameter variables">, parameter +variables can be restricted to matching only numbers or only integers +by using the type specifiers ":n" and ":i". F +provides seven other inbuilt type specifiers, as well as two mechanisms +for defining new restrictions on parameter variables. + +The other inbuilt type specifiers are: + +=over 4 + +=item :+i + +which restricts a parameter variable to matching positive, non-zero +integers (that is: 1, 2, 3, etc.) + +=item :+n + +which restricts a parameter variable to matching positive, non-zero +numbers (that is, floating point numbers strictly greater than zero). + +=item :0+i + +which restricts a parameter variable to matching non-negative integers (that +is: 0, 1, 2, 3, etc.) + +=item :0+n + +which restricts a parameter variable to matching non-negative numbers (that +is, floating point numbers greater than or equal to zero). + +=item :s + +which allows a parameter variable to match any quote-delimited or +whitespace-terminated string. Note that this specifier simply makes +explicit the default behaviour. + +=item :if + +which is used to match input file names. Like type ':s', type ':if' +matches any quote-delimited or whitespace-terminated string. However +this type does I respect other command-line flags and also +requires that the matched string is either "-" (indicating standard +input) or the name of a readable file. + +=item :of + +which is used to match output file names. It is exactly like type ':if' except +that it requires that the string is either "-" (indicating standard output) +or the name of a file that is either writable or non-existent. + +=item :s + +which allows a parameter variable to match any quote-delimited or +whitespace-terminated string. Note that this specifier simply makes +explicit the default behaviour. + +=back + +For example: + + -repeat Repeat times (must be > 0) + + -scale Set scaling factor (cannot be negative) + + +Alternatively, parameter variables can be restricted to matching a +specific regular expression, by providing the required pattern +explicitly (in matched "/" delimiters after the ":"). For example: + + -parity Set parity (

must be "even", + "odd" or "both") + + -file File name must have a three- + capital-letter extension + +If an explicit regular expression is used, there are three "convenience" +extensions available: + +=over 4 + +=item %T + +If the sequence C<%T> appears in a pattern, it is translated to a negative +lookahead containing the parameter variable's trailing context. +Hence the parameter definition: + + -find ; + +ensures that the command line argument "-find abcd;" causes CwhatE> +to match "abcd", I "abcd;". + + +=item %D + +If the sequence C<%D> appears in a pattern, it is translated into a subpattern +which matches any single digit (like a C<\d>), but only if that digit +would I match the parameter variable's trailing context. +Hence C<%D> is just a convenient short-hand for C<(?:%T\d)> (and is actually +implemented that way). + +=item %F + +By default, any explicit pattern is modified by F +so that it fails if the argument being matched represents some defined +parameter flag. If however the sequence C<%F> appears anywhere in a +pattern, it causes the pattern I to reject strings which would +otherwise match another flag. For example, the inbuilt types ':if' and +':of' use C<%F> to enable them to match filenames which happen to be +identical to parameter flags. + +=back + +=head2 Defining new parameter variable types + +Explicit regular expressions are very powerful, but also cumbersome to +use (or reuse) in some situations. F provides a general +"parameter variable type definition" mechanism to simplify such cases. + +To declare a new parameter variable type, the C<[pvtype:...]> directive +is used. A C<[pvtype...]> directive specifies the name, matching +pattern, and action for the new parameter variable type (though both +the pattern and action are optional). + +The name string may be I whitespace-terminated sequence of +characters which does not include a ">". The name may also be specified +within a pair of quotation marks (single or double) or within any Perl +quotelike operation. For example: + + [pvtype: num ] # Makes this valid: -count + [pvtype: 'a num' ] # Makes this valid: -count + [pvtype: q{nbr} ] # Makes this valid: -count + +The pattern is used in initial matching of the parameter variable. +Patterns are normally specified as a "/"-delimited Perl regular +expression: + + [pvtype: num /\d+/ ] + [pvtype: 'a num' /\d+(\.\d*)/ ] + [pvtype: q{nbr} /[+-]?\d+/ ] + +Alternatively the pattern associated with a new type may be specified +as a ":" followed by the name of another parameter variable type (in +quotes if necessary). In this case the new type matches the same +pattern (and action! - see below) as the named type. For example: + + [pvtype: num :+i ] # is the same as + [pvtype: 'a num' :n ] # is the same as + [pvtype: q{nbr} :'a num' ] # is also the same as + +As a third alternative, the pattern may be omitted altogether, in +which case the new type matches whatever the inbuilt pattern ":s" +matches. + +The optional action which may be included in any C<[pvtype:...]> +directive is executed I the corresponding parameter variable +matches the command line but I any actions belonging to the +enclosing parameter are executed. Typically, such type actions +will call the C operator (see L<"Termination and rejection">) +to test extra conditions, but any valid Perl code is acceptible. For +example: + + [pvtype: num /\d+/ { reject if (localtime)[6]==3 } ] + [pvtype: 'a num' :n { print "a num!" } ] + [pvtype: q{nbr} :'a num' { reject $::no_nbr } ] + +If a new type is defined in terms of another (for example, ":a num" +and ":nbr" above), any action specified by that new type is +I to the action of that other type. Hence: + +=over 4 + +=item * + +the new type ":num" matches any string of digits, but then rejects the +match if it's Wednesday. + +=item * + +the new type ":a num" matches any string of digits (like its parent +type ":num"), I prints out "a num!", I rejects the +match if it's Wednesday (like its parent type ":num"). + +=item * + +the new type ":nbr" matches any string of digits (like its parent type +":a num"), but then rejects the match if the global C<$::no_nbr> variable +is true. Otherwise it next prints out "a num!" (like its parent type +":a num"), and finally rejects the match if it's Wednesday (like its +grandparent type ":num"). + +=back + +When a type action is executed (as part of a particular parameter +match), three local variables are available: + +=over 4 + +=item C<$_VAL_> + +which contains the value matched by the type's pattern. It is this value which +is ultimately assigned to the local Perl variable which is available to +parameter actions. Hence if the type action changes the value of C<$_VAL_>, +that changed value becomes the "real" value of the corresponding parameter +variable (see the Roman numeral example below). + +=item C<$_VAR_> + +which contains the name of the parameter variable being matched. + +=item C<$_PARAM_> + +which contains the name of the parameter currently being matched. + +=back + +Here is a example of the use of these variables: + + $args = new Getopt::Declare <<'EOPARAM'; + + [pvtype: type /[OAB]|AB')/ ] + [pvtype: Rh? /Rh[+-]/ ] + [pvtype: days :+i { reject $_VAL_<14 " $_PARAM_ (too soon!)"} ] + + -donated Days since last donation + -applied Days since applied to donate + + -blood [] Specify blood type + and (optionally) rhesus factor + EOPARAM + +In the above example, the ":days" parameter variable type is defined +to match whatever the ":+i" type matches (that is positive, non-zero +integers), with the proviso that the matching value (C<$_VAL_>) must +be at least 14. If a shorter value is specified for CDE>, +or CAE> parameter variables, then F would +issue the following (respective) error messages: + + Error: -donated (too soon!) + Error: -applied (too soon!) + +Note that the "inbuilt" parameter variable types ("i", "n", etc.) are +really just predefined type names, and hence can be altered if necessary: + + $args = new Getopt::Declare <<'EOPARAM'; + + [pvtype: 'n' /[MDCLXVI]+/ { reject !($_VAL_=to_roman $_VAL_) } ] + + -index Index number + { print $data[$number]; } + EOPARAM + +The above C<[pvtype:...]> directive means that all parameter variables +specified with a type ":n" henceforth only match valid Roman +numerals, but that any such numerals are I converted to +ordinary numbers (by passing C<$_VAL_>) through the C +function). + +Hence the requirement that all ":n" numbers now must be Roman can be +imposed I, at least as far as the actual parameter +variables which use the ":n" type are concerned. Thus C<$number> can +be still used to index the array C<@data> despite the new restrictions +placed upon it by the redefinition of type ":n". + +Note too that, because the ":+n" and ":0+n" types are implicitly +defined in terms of the original ":n" type (as if the directives: + + [pvtype: '+n' :n { reject if $_VAL <= 0 } ] + [pvtype: '0+n' :n { reject if $_VAL < 0 } ] + +were included in every specification), the above redefinition of ":n" +affects those types as well. In such cases the format conversion is +performed I the "sign" tests (in other words, the "inherited" +actions are performed I any newly defined ones). + +Parameter variable type definitions may appear anywhere in a +F specification and are effective for the entire +scope of the specification. In particular, new parameter variable +types may be defined I they are used. + +=head2 Undocumented parameters + +If a parameter description is omitted, or consists entirely of +whitespace, or contains the special directive C<[undocumented]>, then +the parameter is still parsed as normal, but will not appear in the +automatically generated usage information (see L<"Usage information">). + +Apart from allowing for "secret" parameters (a dubious benefit), this +feature enables the programmer to specify some undocumented action +which is to be taken on encountering an otherwise unknown argument. +For example: + + + { handle_unknown($unknown); } + + +=head2 "Dittoed" parameters + +Sometimes it is desirable to provide two or more alternate flags for +the same behaviour (typically, a short form and a long form). To +reduce the burden of specifying such pairs, the special directive +C<[ditto]> is provided. If the description of a parameter I with +a C<[ditto]> directive, that directive is replaced with the +description for the immediately preceding parameter (including any +other directives). For example: + + -v Verbose mode + --verbose [ditto] (long form) + +In the automatically generated usage information this would be displayed as: + + -v Verbose mode + --verbose " " (long form) + +Furthermore, if the "dittoed" parameter has no action(s) specified, the +action(s) of the preceding parameter are reused. For example, the +specification: + + -v Verbose mode + { $::verbose = 1; } + --verbose [ditto] + +would result in the C<--verbose> option setting C<$::verbose> just like the +C<-v> option. On the other hand, the specification: + + -v Verbose mode + { $::verbose = 1; } + --verbose [ditto] + { $::verbose = 2; } + +would give separate actions to each flag. + + +=head2 Deferred actions + +It is often desirable or necessary to defer actions taken in response +to particular flags until the entire command-line has been parsed. The most +obvious case is where modifier flags must be able to be specified I +the command-line arguments they modify. + +To support this, F provides a local operator (C) which +delays the execution of a particular action until the command-line processing +is finished. The C operator takes a single block, the execution of which +is deferred until the command-line is fully and successfully parsed. If +command-line processing I for some reason (see L<"DIAGNOSTICS">), the +deferred blocks are never executed. + +For example: + + ... Files to be processed + { defer { foreach (@files) { proc($_); } } } + + -rev[erse] Process in reverse order + { $::ordered = -1; } + + -rand[om] Process in random order + { $::ordered = 0; } + +With the above specification, the C<-rev> and/or C<-rand> flags can be +specified I the list of files, but still affect the processing of +those files. Moreover, if the command-line parsing fails for some reason +(perhaps due to an unrecognized argument), the deferred processing will +not be performed. + + +=head2 Flag clustering + +Like some other F packages, F allows parameter +flags to be "clustered". That is, if two or more flags have the same +"flag prefix" (one or more leading non-whitespace, non-alphanumeric characters), +those flags may be concatenated behind a single copy of that flag prefix. +For example, given the parameter specifications: + + -+ Swap signs + -a Append mode + -b Bitwise compare + -c Create new file + +del Delete old file + +e Execute (at specified nice level) when complete + +The following command-lines (amongst others) are all exactly equivalent: + + -a -b -c newfile +e20 +del + -abc newfile +dele20 + -abcnewfile+dele20 + -abcnewfile +e 20del + +The last two alternatives are correctly parsed because +F allows flag clustering at I where the +remainder of the command-line being processed starts with a +non-whitespace character and where the remaining substring would not +otherwise immediately match a parameter flag. + +Hence the trailing "+dele20" in the third command-line example is parsed as +S<"+del +e20"> and not S<"-+ del +e20">. This is because the previous "-" +prefix is I propagated (since the leading "+del" I a valid flag). + +In contrast, the trailing S<"+e 20del"> in the fourth example is parsed as +S<"+e 20 +del"> because, after the S<" 20"> is parsed (as the integer +parameter variable CNICEE>), the next characters are "del", +which I form a flag themselves unless prefixed with the +controlling "+". + +In some circumstances a clustered sequence of flags on the command-line +might also match a single (multicharacter) parameter flag. For example, given +the specifications: + + -a Blood type is A + -b Blood type is B + -ab Blood type is AB + -ba Donor has a Bachelor of Arts + +A command-line argument "-aba" might be parsed as +S<"-a -b -a"> or S<"-a -ba"> or S<"-ab -a">. In all such +cases, F prefers the longest unmatched flag first. +Hence the previous example would be parsed as S<"-ab -a">, unless +the C<-ab> flag had already appeared in the command-line (in which +case, it would be parsed as S<"-a -ba">). + +These rules are designed to produce consistency and "least surprise", +but (as the above example illustrates) may not always do so. If the +idea of unconstrained flag clustering is too libertarian for a particular +application, the feature may be restricted (or removed completely), +by including a C<[cluster:...]> directive anywhere in the specification string. + +The options are: + +=over 8 + +=item C<[cluster: any]> + +This version of the directive allows any flag to be clustered (that is, +it merely makes explicit the default behaviour). + +=item C<[cluster: flags]> + +This version of the directive restricts clustering to parameters which are +"pure" flags (that is, those which have no parameter variables or punctuators). + +=item C<[cluster: singles]> + +This version of the directive restricts clustering to parameters which are +"pure" flags, and which consist of a flag prefix followed by a single +alphanumeric character. + +=item C<[cluster: none]> + +This version of the directive turns off clustering completely. + +=back + +For example: + + $args = new Getopt::Declare <<'EOSPEC'; + -a Append mode + -b Back-up mode + -bu [ditto] + -c Copy mode + -d [] Delete mode + -e[xec] Execute mode + + [cluster:singles] + EOSPEC + +In the above example, only the C<-a> and C<-b> parameters may be clustered. +The C<-bu> parameter is excluded because it consists of more than one +letter, whilst the C<-c> and C<-d> parameters are excluded because they +take (or may take, in C<-d>'s case) a variable. The C<-e[xec]> parameter +is excluded because it may take a trailing punctuator (C<[xec]>). + +By comparison, if the directive had been C<[cluster: flags]>, then +C<-bu> I be clustered, though C<-c>, C<-d> and C<-e[xec]> would +still be excluded since they are not "pure flags"). + + +=head2 Strict and non-strict command-line parsing + +"Strictness" in F refers to the way in which unrecognized +command-line arguments are handled. By default, F is +"non-strict", in that it simply skips silently over any unrecognized +command-line argument, leaving it in C<@ARGV> at the conclusion of +command-line processing (but only if they were originally parsed +from C<@ARGV>). + +No matter where they came from, the remaining arguments are also available +by calling the C method on the Getopt::Declare object, after it +has parsed. In a list context, this method returns a list of the +unprocessed arguments; in a scalar context a single string with the unused +arguments concatenated is returned. + +Likewise, there is a C method that returns the arguments that were +successfully processed by the parser. + +However, if a new F object is created with a +specification string containing the C<[strict]> directive (at any +point in the specification): + + $args = new Getopt::Declare <<'EOSPEC'; + + [strict] + + -a Append mode + -b Back-up mode + -c Copy mode + EOSPEC + +then the command-line is parsed "strictly". In this case, any +unrecognized argument causes an error message (see L<"DIAGNOSTICS">) to +be written to STDERR, and command-line processing to (eventually) +fail. On such a failure, the call to C returns +C instead of the usual hash reference. + +The only concession that "strict" mode makes to the unknown is that, +if command-line processing is prematurely terminated via the +C operator, any command-line arguments which have not yet +been examined are left in C<@ARGV> and do not cause the parse to fail (of +course, if any unknown arguments were encountered I the +C was executed, those earlier arguments I cause +command-line processing to fail). + +The "strict" option is useful when I possible parameters +can be specified in a single F object, whereas the +"non-strict" approach is needed when unrecognized arguments are either +to be quietly tolerated, or processed at a later point (possibly in a +second F object). + + +=head2 Parameter dependencies + +F provides five other directives which modify the +behaviour of the command-line parser in some way. One or more of these +directives may be included in any parameter description. In addition, +the C<[mutex:...]> directive may also appear in any usage "decoration" +(see L<"Usage information">). + +Each directive specifies a particular set of conditions that a +command-line must fulfil (for example, that certain parameters may not +appear on the same command-line). If any such condition is violated, +an appropriate error message is printed (see L<"DIAGNOSTICS">). +Furthermore, once the command-line is completely parsed, if any +condition was violated, the program terminates +(whilst still inside C). + +The directives are: + +=over 4 + +=item C<[required]> + +Specifies that an argument matching at least one variant of the +corresponding parameter I be specified somewhere in the +command-line. That is, if two or more required parameters share the +same flag, it suffices that I of them matches an argument +(recall that F considers all parameter specifications +with the same flag merely to be variant forms of a single "underlying" +parameter). + +If an argument matching a "required" flag is I found in the +command-line, an error message to that effect is issued, +command-line processing fails, and C returns +C. + + +=item C<[repeatable]> + +By default, F objects allow each of their parameters to +be matched only once (that is, once any variant of a particular +parameter matches an argument, I variants of that same parameter +are subsequently excluded from further consideration when parsing the +rest of the command-line). + +However, it is sometimes useful to allow a particular parameter to match +more than once. Any parameter whose description includes the directive +C<[repeatable]> is I excluded as a potential argument match, no matter +how many times it has matched previously: + + -nice Increase nice value (linearly if repeated) + [repeatable] + { set_nice( get_nice()+1 ); } + + -w Toggle warnings [repeatable] for the rest + of the command-line + { $warn = !$warn; } + +As a more general mechanism is a C<[repeatable]> directive appears in a +specification anywhere other than a flag's description, then I parameters +are marked repeatable: + + [repeatable] + + -nice Increase nice value (linearly if repeated) + { set_nice( get_nice()+1 ); } + + -w Toggle warnings for the rest of the command-line + { $warn = !$warn; } + + +=item C<[mutex: Eflag listE]> + +The C<[mutex:...]> directive specifies that the parameters whose +flags it lists are mutually exclusive. That is, no two or more of them +may appear in the same command-line. For example: + + -case set to all lower case + -CASE SET TO ALL UPPER CASE + -Case Set to sentence case + -CaSe SeT tO "RAnSom nOTe" CasE + + [mutex: -case -CASE -Case -CaSe] + +The interaction of the C<[mutex:...]> and C<[required]> directives is +potentially awkward in the case where two "required" arguments are +also mutually exclusive (since the C<[required]> directives insist +that both parameters must appear in the command-line, whilst the +C<[mutex:...]> directive expressly forbids this). + +F resolves such contradictory constraints by +relaxing the meaning of "required" slightly. If a flag is marked +"required", it is considered "found" for the purposes of error +checking if it or I appears on the command-line. + +Hence the specifications: + + -case set to all lower case [required] + -CASE SET TO ALL UPPER CASE [required] + -Case Set to sentence case [required] + -CaSe SeT tO "RAnSom nOTe" CasE [required] + + [mutex: -case -CASE -Case -CaSe] + +mean that I of these four flags must appear on the +command-line, but that the presence of any one of them will suffice +to satisfy the "requiredness" of all four. + +It should also be noted that mutual exclusion is only tested for +I a parameter has been completely matched (that is, after the +execution of its actions, if any). This prevents "rejected" parameters +(see L<"Termination and rejection">) from incorrectly generating +mutual exclusion errors. However, it also sometimes makes it necessary +to defer the actions of a pair of mutually exclusive parameters (for +example, if those actions are expensive or irreversible). + + +=item C<[excludes: Eflag listE]> + +The C<[excludes:...]> directive provides a "pairwise" version of +mutual exclusion, specifying that the current parameter is mutually exclusive +with all the other parameters lists, but those other parameters are not +mutually exclusive with each other. That is, whereas the specification: + + -left Justify to left margin + -right Justify to right margin + -centre Centre each line + + [mutex: -left -right -centre] + +means that only one of these three justification alternatives can ever be used +at once, the specification: + + -left Justify to left margin + -right Justify to right margin + -centre Centre each line [excludes: -left -right] + +means that C<-left> and C<-right> can still be used together +(probably to indicate "left I right" justification), but that +neither can be used with C<-centre>. Note that the C<[excludes:...]> +directive also differs from the C<[mutex:...]> in that it is always +connected with a paricular parameter, I +using the flag of that parameter as the target of exclusion. + + +=item C<[requires: EconditionE]> + +The C<[requires]> directive specifies a set of flags which +must also appear in order for a particular flag to be permitted in the +command-line. The condition is a boolean expression, in which the +terms are the flags or various parameters, and the operations are +C<&&>, C<||>, C, and bracketting. For example, the specifications: + + -num Use numeric sort order + -lex Use "dictionary" sort order + -len Sort on length of line (or field) + + -field Sort on value of field + + -rev Reverse sort order + [requires: -num || -lex || !(-len && -field)] + +means that the C<-rev> flag is allowed only if either the C<-num> or the +C<-lex> parameter has been used, or if it is not true that +I the C<-len> I the C<-field> parameters have been used. + +Note that the operators C<&&>, C<||>, and C retain their normal +Perl precedences. + + +=head2 Parsing from other sources + +F normally parses the contents of C<@ARGV>, but can +be made to parse specified files instead. To accommodate this, +C takes an optional second parameter, which specifies +a file to be parsed. The parameter may be either: + +=over 8 + +=item A C reference or a filehandle GLOB reference + +in which case C reads the corresponding handle until +end-of-file, and parses the resulting text (even if it is an empty string). + +=item An ARRAY reference containing the single string C<'-CONFIG'> + +in which case C looks for the files +F<$ENV{HOME}/.${progname}rc> and F<$ENV{PWD}/.${progname}rc>, +concatenates their contents, and parses that. + +If neither file is found (or if both are inaccessible) +C immediately returns zero. If a +file is found but the parse subsequently fails, C is returned. + +=item An ARRAY reference containing the single string C<'-BUILD'> + +in which case C builds a parser from the +supplied grammar and returns a reference to it, but does not parse anything. +See L<"The Getopt::Declare::code() method"> and +L<"The Getopt::Declare::parse() method">. + +=item An ARRAY reference containing the single string C<'-SKIP'> or the single value C or nothing + +in which case C immediately returns zero. +This alternative is useful when using a C: + + my $args = new Getopt::Declare($grammar, + new FileHandle ($filename) || -SKIP); + +because it makes explicit what happens if C fails. Of course, +if the C<-SKIP> alternative were omitted, would +still return immediately, having found C as its second argument. + +=item Any other ARRAY reference + +in which case C treats the array elements as a +list of filenames, concatenates the contents of those files, and parses that. + +If the list does not denote any accessible file(s) +C immediately returns zero. If matching files +are found, but not successfully parsed, C is returned. + +=item A string + +in which case C parses that string directly. + +=back + +Note that when C parses from a +source other than C<@ARGV>, unrecognized arguments are I +placed back in C<@ARGV>. + + +=head2 Using F objects after command-line processing + +After command-line processing is completed, the object returned by +C will have the following features: + +=over 4 + +=item Parameter data + +For each successfully matched parameter, the F object +will contain a hash element. The key of that element will be the leading flag +or parameter variable name of the parameter. + +The value of the element will be a reference to another hash which contains +the names and values of each distinct parameter variable and/or +punctuator which was matched by the parameter. Punctuators generate +string values containing the actual text matched. Scalar parameter +variables generate scalar values. List parameter variables +generate array references. + +As a special case, if a parameter consists of a single component +(either a single flag or a single parameter variable), then the value for the +corresponding hash key is not a hash reference, but the actual value matched. + +The following example illustrates the various possibilities: + + $args = new Getopt::Declare, q{ + + -v [etc] One or more values + Input file [required] + -o ... Output files + }; + + if ( $args->{'-v'} ) + { + print "Using value: ", $args->{'-v'}{''}; + print " (et cetera)" if $args->{'-v'}{'etc'}; + print "\n"; + } + + open INFILE, $args->{''} or die; + @data = ; + + foreach $outfile ( @{$args->{'-o'}{''}} ) + { + open OUTFILE, ">$outfile" or die; + print OUTFILE process(@data); + close OUTFILE; + } + + +The values which are assigned to the various hash elements are copied from +the corresponding blocked-scoped variables which are available within +actions. In particular, if the value of any of those block-scoped variables +is changed within an action, that changed value is saved in the hash. For +example, given the specification: + + $args = new Getopt::Declare, q{ + + -ar Set aspect ratio (will be clipped to [0..1]) + { + $R = 0 if $R < 0; + $R = 1 if $R > 1; + } + }; + +then the value of C<$args-E{'-ar'}{'ERE'}> +will always be between zero and one. + + +=item The C<@ARGV> array + +In its "non-strict" mode, once a F object has +completed its command-line processing, it pushes any unrecognized +arguments back into the emptied command-line array C<@ARGV> (whereas +all I arguments will have been removed). + +Note that these remaining arguments will be in sequential elements +(starting at C<$ARGV[0]>), I in their original positions in +C<@ARGV>. + + +=item The C method + +Once a F object is created, its C method may be called +to explicitly print out usage information corresponding to the specification +with which it was built. See L<"Usage information"> for more details. +If the C method is called with an argument, that argument is passed +to C after the usage information is printed (the no-argument version of +C simply returns at that point). + + +=item The C method + +Another useful method of a F object is C, +which prints out the name of the enclosing program, the last time it +was modified, and the value of C<$::VERSION>, if it is defined. +Note that this implies that I F objects in a +single program will print out identical version information. + +Like the C method, if C is passed an argument, it +will exit with that value after printing. + + +=item The C method + +It is possible to separate the construction of a F +parser from the actual parsing it performs. If +C is called with a second parameter C<'-BUILD'> +(see L<"Parsing from other sources">, it constructs and returns a +parser, without parsing anything. +The resulting parser object can then be used to parse multiple sources, +by calling its C method. + +C takes an optional parameter which specifies +the source of the text to be parsed (it parses C<@ARGV> if the +parameter is omitted). This parameter takes the same set of values as the +optional second parameter of C (see L<"Parsing +from other sources">). + +C returns true if the source is located and +parsed successfully. It returns a defined false (zero) if the source is +not located. An C is returned if the source is located, but not +successfully parsed. + +Thus, the following code first constructs a parsers for a series of alternate +configuration files and the command line, and then parses them: + + # BUILD PARSERS + my $config = Getopt::Declare::new($config_grammar, -BUILD); + my $cmdline = Getopt::Declare::new($cmdline_grammar, -BUILD); + + # TRY STANDARD CONFIG FILES + $config->parse(-CONFIG) + + # OTHERWISE, TRY GLOBAL CONFIG + or $config->parse('/usr/local/config/.demo_rc') + + # OTHERWISE, TRY OPENING A FILEHANDLE (OR JUST GIVE UP) + or $config->parse(new FileHandle (".config") || -SKIP); + + # NOW PARSE THE COMMAND LINE + + $cmdline->parse() or die; + + +=item The C method + +It is also possible to retreive the command-line parsing code generated +internally by C. The C +method returns a string containing the complete command-line processing +code, as a single C block plus a leading C declaration. + +C takes as its sole argument a string +containing the complete name of the package (for the leading +C declaration in the generated code). If this string is empty +or undefined, the package name defaults to "main". + +Since the default behaviour of C is to execute +the command-line parsing code it generates, if the goal is only to +generate the parser code, the optional second '-BUILD' parameter +(see L<"Parsing from other sources">) should be specified when calling +. + +For example, the following program "inlines" a C +specification, by extracting it from between the first "=for +Getopt::Declare" and the next "=cut" appearing on C: + + use Getopt::Declare; + + sub encode { return new Getopt::Declare (shift,-BUILD)->code() || die } + + undef $/; + if (<>) + { + s {^=for\s+Getopt::Declare\s*\n(.*?)\n=cut} + {'my (\$self,$source) = ({});'.encode($1).' or die "\n";'} + esm; + } + + print; + +Note that the generated inlined version expects to find a lexical variable +named C<$source>, which tells it what to parse (this variable is +normally set by the optional parameters of C or +C). + +The inlined code leaves all extracted parameters in the lexical +variable C<$self> and the does not autogenerate help or version flags +(since there is no actual F object in the inlined code +through which to generate them). + +=back + + +=head1 AUTOGENERATED FEATURES + +=head2 Usage information + +The specification passed to C is used (almost +verbatim) as a "usage" display whenever usage information is +requested. + +Such requests may be made either by specifying an argument matching +the help parameter (see L<"Help parameters">) or by explicitly calling +the C method (through an action or after +command-line processing): + + $args = new Getopt::Declare, q{ + + -usage Show usage information and exit + { $self->usage(0); } + + +usage Show usage information at end of program + }; + + # PROGRAM HERE + + $args->usage() if $args->{'+usage'}; + + +The following changes are made to the original specification before +it is displayed: + +=over 4 + +=item * + +All actions and comments are deleted, + +=item * + +any C<[ditto]> directive is converted to an appropriate set of "ditto" marks, + +=item * + +any text in matching square brackets (including any directive) is deleted, + +=item * + +any parameter variable type specifier (":i", ":n", ":/pat/", etc.) is deleted. + +=back + +Otherwise, the usage information displayed retains all the formatting +present in the original specification. + +In addition to this information, if the input source is @ARGV, +F displays three sample command-lines: one indicating +the normal usage (including any required parameter variables), one +indicating how to invoke help (see L<"Help parameters">), and one +indicating how to determine the current version of the program (see +L<"Version parameters">). + +The usage information is printed to C and (since F +tends to encourage longer and better-documented parameter lists) if +the F package is available, an F object is used to +page out the entire usage documentation. + +=head2 Usage "decoration" + +It is sometimes convenient to add other "decorative" features to a +program's usage information, such as subheadings, general notes, +separators, etc. F accommodates this need by ignoring +such items when interpreting a specification string, but printing them +when asked for usage information. + +Any line which cannot be interpreted as either a parameter +definition, a parameter description, or a parameter action, is treated +as a "decorator" line, and is printed verbatim (after any square +bracketted substrings have been removed from it). + +The key to successfully decorating F usage +information is to ensure that decorator lines are separated from +any preceding parameter specification, either by an action or by an +empty line. In addition, like a parameter description, a decorator +line cannot contain a tab character after the first non-whitespace +character (because it would then be treated as a parameter +specification). + +The following specification demonstrates various forms of usage +decoration. In fact, there are only four actual parameters (C<-in>, +C<-r>, C<-p>, and C<-out>) specified. Note in particular that I tabs +are perfectly acceptible in decorator lines. + + $args = new Getopt::Declare (<<'EOPARAM'); + + ============================================================ + Required parameter: + + -in Input file [required] + + ------------------------------------------------------------ + + Optional parameters: + + (The first two are mutually exclusive) [mutex: -r -p] + + -r[and[om]] Output in random order + -p[erm[ute]] Output all permutations + + --------------------------------------------------- + + -out Optional output file + + ------------------------------------------------------------ + Note: this program is known to run very slowly of files with + long individual lines. + ============================================================ + EOPARAM + + +=head2 Help parameters + +By default, F automatically defines I of the following +parameters: + + -help Show usage information [undocumented] + { $self->usage(0); } + -Help [ditto] + -HELP [ditto] + --help [ditto] + --Help [ditto] + --HELP [ditto] + -h [ditto] + -H [ditto] + +Hence, most attempts by the user to get help will automatically work +successfully. + +Note however that, if a parameter with any of these flags is +explicitly specified in the string passed to C, +that flag (only) is removed from the list of possible help flags. For +example: + + -w Specify width in pixels + -h Specify height in pixels + +would cause the C<-h> help parameter to be removed (although help +would still be accessible through the other seven alternatives). + + +=head2 Version parameters + +F also automatically creates a set of parameters which can be +used to retreive program version information: + + -version Show version information [undocumented] + { $self->version(0); } + -Version [ditto] + -VERSION [ditto] + --version [ditto] + --Version [ditto] + --VERSION [ditto] + -v [ditto] + -V [ditto] + +As with the various help commands, explicitly specifying a parameter +with any of the above flags removes that flag from the list of version +flags. + + +=head1 DIAGNOSTICS + +F may issue the following diagnostics whilst parsing a +command-line. All of them are fatal (the first five, instantly so): + +=over 4 + +=item "Error: bad Getopt::Declare parameter variable specification near %s" + +A matching pair of angle brackets were specified as part of a +parameter definition, but did not form a valid parameter variable +specification (that is, it wasn't in the form: > or +:I>). + +=item "Error: bad type in Getopt::Declare parameter variable specification near %s" + +An unknown type specifier was used in a parameter variable type suffix. + +=item "Error: bad action in Getopt::Declare specification:\n %s" + +A Perl syntax error was detected in the indicated action. + +=item "Error: unattached action in Getopt::Declare specification:\n %s" + +An action was found for which there was no preceding parameter specification. +This usually occurs because the trailing tab was omitted from the preceding +parameter specification. + +=item "Error: incomplete action in Getopt::Declare specification:\n %s" + +An action was found, but it was missing one or more closing '}'s. + +=item "Error: bad condition in directive [requires: %s]\n" + +The condition specified as part of the indicated C<[requires:...]> +directive was not a well-formed boolean expression. Common problems +include: omitting a C<&&>/C<||> operator between two flags, +mismatched brackets, or using C/C instead of C<&&>/C<||>. + +=item "Error: in generated command-line parser code:\n %s" + +Either there was a Perl syntax error in one some action (which was +not caught by the previous diagnostic), or (less likely) there is a +bug in the code generator inside F. + +=item "Error: incorrect specification of %s parameter" + +The flag for the indicated parameter was found, but the argument did not +then match any of that parameter's variant syntaxes. + +=item "Error: parameter %s not allowed with %s" + +Two mutually exclusive flags were specified together. + +=item "Error: required parameter %s not found" + +No argument matching the specified "required" parameter was found +during command-line processing. + +=item "Error: parameter %s can only be specified with %s" + +The indicated parameter has a C<[requires:...]> directive, which +was not satisfied. + +=item "Error: unknown command-line argument (%s)" + +A command-line argument was encountered which did not match any +specified parameter. This diagnostic can only only appear if the +"strict" option is in effect. + +=item "Error: in parameter %s (%s must be an integer greater than zero)" + +A parameter variable in the indicated parameter was declared with the +type ":+i" (or a type derived from it), but the corresponding +argument was not a positive, non-zero integer. + +=item "Error: in parameter %s (%s must be a number greater than zero)" + +A parameter variable in the indicated parameter was declared with the +type ":+n" (or a type derived from it), but the corresponding +argument was not a positive, non-zero number. + +=item "Error: in parameter %s (%s must be an positive integer)" + +A parameter variable in the indicated parameter was declared with the +type ":0+i" (or a type derived from it), but the corresponding +argument was not a positive integer. + +=item "Error: in parameter %s (%s must be a positive number)" + +A parameter variable in the indicated parameter was declared with the +type ":0+n" (or a type derived from it), but the corresponding +argument was not a positive number. + +=back + +=head1 AUTHOR + +Damian Conway (damian@conway.org) + + +=head1 BUGS AND ANNOYANCES + +There are undoubtedly serious bugs lurking somewhere in this code. + +If nothing else, it shouldn't take 1500 lines to explain a +package that was designed for intuitive ease of use! + +Bug reports and other feedback are most welcome. + + +=head1 COPYRIGHT + + Copyright (c) 1997-2000, Damian Conway. All Rights Reserved. + This module is free software. It may be used, redistributed + and/or modified under the terms of the Perl Artistic License + (see http://www.perl.com/perl/misc/Artistic.html) -- cgit v1.2.3