Getopt::Tabular - table-driven argument parsing for Perl 5 |
Getopt::Tabular - table-driven argument parsing for Perl 5
use Getopt::Tabular;
(or)
use Getopt::Tabular qw/GetOptions SetHelp SetHelpOption SetError GetError/;
...
&Getopt::Tabular::SetHelp (long_help, usage_string);
@opt_table = ( [section_description, "section"], [option, type, num_values, option_data, help_string], ... ); &GetOptions (\@opt_table, \@ARGV [, \@newARGV]) || exit 1;
Getopt::Tabular is a Perl 5 module for table-driven argument parsing,
vaguely inspired by John Ousterhout's Tk_ParseArgv. All you really need
to do to use the package is set up a table describing all your
command-line options, and call &GetOptions with three arguments: a
reference to your option table, a reference to @ARGV
(or something
like it), and an optional third array reference (say, to @newARGV
).
&GetOptions will process all arguments in @ARGV
, and copy any
leftover arguments (i.e. those that are not options or arguments to some
option) to the @newARGV
array. (If the @newARGV
argument is not
supplied, GetOptions
will replace @ARGV
with the stripped-down
argument list.) If there are any invalid options, GetOptions
will
print an error message and return 0.
Before I tell you all about why Getopt::Tabular is a wonderful thing, let me explain some of the terminology that will keep popping up here.
@ARGV
array.
-foo
is a scalar-valued integer option, and
-foo 3
appears on the command line, then 3
will be the argument to
-foo
.
GetOptions
deals with an option and the arguments that
follow it. (Actually, for most option types, the type interacts with the
num_values
field, which determines whether the option is scalar- or
vector-valued. This will be fully explained in due course.)
Now for the advertising, i.e. why Getopt::Tabular is a good thing.
You can parse options in a "spoof" mode that has no side-effects -- this is useful for making a validation pass over the command line without actually doing anything.
In general, I have found that Getopt::Tabular tends to encourage programs with long lists of sophisticated options, leading to great flexibility, intelligent operation, and the potential for insanely long command lines.
The basic operation of Getopt::Tabular is driven by an option table,
which is just a list of option descriptions (otherwise known as option
table entries, or just entries). Each option description tells
GetOptions
everything it needs to know when it encounters a particular
option on the command line. For instance,
["-foo", "integer", 2, \@Foo, "set the foo values"]
means that whenever -foo
is seen on the command line, GetOptions
is
to make sure that the next two arguments are integers, and copy them into
the caller's @Foo
array. (Well, really into the @Foo
array where the
option table is defined. This is almost always the same as GetOptions
'
caller, though.)
Typically, you'll group a bunch of option descriptions together like this:
@options = (["-range", "integer", 2, \@Range, "set the range of allowed values"], ["-file", "string", 1, \$File, "set the output file"], ["-clobber", "boolean", 0, \$Clobber, "clobber existing files"], ... );
and then call GetOptions
like this:
&GetOptions (\@options, \@ARGV) || exit 1;
which replaces @ARGV
with a new array containing all the arguments
left-over after options and their arguments have been removed. You can
also call GetOptions
with three arguments, like this:
&GetOptions (\@options, \@ARGV, \@newARGV) || exit 1;
in which case @ARGV
is untouched, and @newARGV
gets the leftover
arguments.
In case of error, GetOptions
prints enough information for the user to
figure out what's going wrong. If you supply one, it'll even print out a
brief usage message in case of error. Thus, it's enough to just exit 1
when GetOptions
indicates an error by returning 0.
Detailed descriptions of the contents of an option table entry are given next, followed by the complete run-down of available types, full details on error handling, and how help text is generated.
The fields in the option table control how arguments are parsed, so it's
important to understand each one in turn. First, the format of entries in
the table is fairly rigid, even though this isn't really necessary with
Perl. It's done that way to make the Getopt::Tabular code a little easier;
the drawback is that some entries will have unused values (e.g. the
num_values
field is never used for boolean options, but you still have
to put something there as a place-holder). The fields are as follows:
string
, integer
, and float
all imply copying values from the
command line to a variable; constant
, boolean
, copy
,
arrayconst
, and hashconst
all imply copying some pre-defined data
into a variable; call
and eval
allow the execution of some arbitrary
subroutine or chunk of code; and help
options will cause GetOptions
to print out all available help text and return 0.
string
, integer
, and float
options, this determines whether
the option is a scalar (num_values = 1) or vector (num_values > 1)
option. (Note that whether the option is scalar- or vector-valued has an
important influence on what you must supply in the option_data field!)
For constant
, copy
, arrayconst
, and hashconst
option types,
num_values is a bit of a misnomer: it actually contains the value (or a
reference to it, if array or hash) to be copied when the option is
encountered. For call
options, num_values can be used to supply
extra arguments to the called subroutine. In any case, though, you can
think of num_values as an input value. For boolean
and eval
options, num_values is ignored and should be undef
or 0.
string
, integer
, float
, boolean
, constant
, copy
,
arrayconst
, and hashconst
types, this must be a reference to the
variable into which you want GetOptions
to copy the appropriate thing.
The ``appropriate thing'' is either the argument(s)
following the option, the
constant supplied as num_values, or 1 or 0 (for boolean options).
For boolean
, constant
, copy
, and scalar-valued string
,
integer
, and float
options, this must be a scalar reference. For
vector-valued string
, integer
, and float
options (num_values >
1), and for arrayconst
options, this must be an array reference. For
hashconst
options, this must be a hash reference.
Finally, option_data is also used as an input value for call
and
eval
options: for call
, it should be a subroutine reference, and for
eval
options, it should be a string containing valid Perl code to
evaluate when the option is seen. The subroutine called by a call
option should take at least two arguments: a string, which is the actual
option that triggered the call (because the same subroutine could be tied
to many options), and an array reference, which contains all command line
arguments after that option. (Further arguments can be supplied in the
num_values field.) The subroutine may freely modify this array, and
those modifications will affect the behaviour of GetOptions
afterwards.
The chunk of code passed to an eval
option is evaluated in the package
from which GetOptions
is called, and does not have access to any
internal Getopt::Tabular data.
GetOptions
has to print out your help, it will do so
quite nicely without any intervention. If the help string is not defined,
then that option will not be included in the option help text. (However,
you could supply an empty string -- which is defined -- to make GetOptions
just print out the option name, but nothing else.)
GetOptions
can act fairly
intelligently when formatting a help message. See HELP TEXT for more
information.
The option type field is the single-most important field in the table, as
the type for an option -foo
determines (along with num_values) what
action GetOptions
takes when it sees -foo
on the command line: how many
following arguments become -foo
's arguments, what regular expression
those arguments must conform to, or whether some other action should be
taken.
As mentioned above, there are three main classes of argument types:
eval
or a subroutine to call.
-foo
to take a single string as
an argument, with that string being copied to the scalar variable
$Foo
, then you would have this entry in your option table:
["-foo", "string", 1, \$Foo]
(For conciseness, I've omitted the help_string and argdesc entries in all of the example entries in this section. In reality, you should religiously supply help text in order to make your programs easier to use and easier to maintain.)
If num_values is some n greater than one, then the option_data
field must be an array reference, and n arguments are copied from
the command line into that array. (The array is clobbered each time
-foo
is encountered, not appended to.) In this case, -foo
is
referred to as a vector-valued option, as it must be followed by a
fixed number of arguments. (Eventually, I plan to add list-valued
options, which take a variable number of arguments.) For example an
option table like
["-foo", "string", 3, \@Foo]
would result in the @Foo
array being set to the three strings
immediately following any -foo
option on the command line.
The only difference between string, integer, and float options is
how picky GetOptions
is about the value(s)
it will accept. For
string options, anything is OK; for integer options, the values must
look like integers (i.e., they must match /[+-]?\d+/
); for float
options, the values must look like C floating point numbers (trust me, you
don't want to see the regexp for this). Note that since string options
will accept anything, they might accidentally slurp up arguments that are
meant to be further options, if the user forgets to put the correct string.
For instance, if -foo
and -bar
are both scalar-valued string options,
and the arguments -foo -bar
are seen on the command-line, then ``-bar''
will become the argument to -foo
, and never be processed as an option
itself. (This could be construed as either a bug or a feature. If you
feel really strongly that it's a bug, then complain and I'll consider doing
something about it.)
If not enough arguments are found that match the required regular
expression, GetOptions
prints to standard error a clear and useful error
message, followed by the usage summary (if you supplied one), and returns
0. The error messages look something like ``-foo option must be followed by
an integer'', or ``-foo option must be followed by 3 strings'', so it really
is enough for your program to exit 1
without printing any further
message.
&Getopt::Tabular::AddPatternType
as
follows:
&Getopt::Tabular::AddPatternType ("upperstring", "[A-Z]+", "uppercase string")
Note that the third parameter is optional, and is only supplied to make
error messages clearer. For instance, if you now have a scalar-valued
option -zap
of type upperstring
:
["-zap", "upperstring", 1, \$Zap]
and the user gets it wrong and puts an argument that doesn't consist of
all uppercase letters after -zap
, then GetOptions
will complain
that ``-zap option must be followed by an uppercase string''. If you
hadn't supplied the third argument to &AddType
, then the error
message would have been the slightly less helpful ``-zap option must be
followed by an upperstring''. Also, you might have to worry about how
GetOptions
pluralizes your description: in this case, it will simply
add an ``s'', which works fine much of the time, but not always.
Alternately, you could supply a two-element list containing the singular
and plural forms:
&Getopt::Tabular::AddPatternType ("upperstring", "[A-Z]+", ["string of uppercase letters", "strings of uppercase letters"])
So, if -zap
instead expects three upperstring
s, and the user
goofs, then the error message would be (in the first example) ``-zap
option must be followed by 3 uppercase strings'' or ``-zap option must be
followed by three strings of uppercase letters'' (second example).
Of course, if you don't intend to have vector-valued options of your new type, pluralization hardly matters. Also, while it might seem that this is a nice stab in the direction of multi-lingual support, the error messages are still hard-coded to English in other places. Maybe in the next version...
undef
or 0).
Booleans are slightly weird in that every boolean option implies two
possible arguments that will be accepted on the command line, called the
positive and negative alternatives. The positive alternative (which is
what you specify as the option name) results in a true value, while the
negative alternative results in false. Most of the time, you can let
GetOptions
pick the negative alternative for you: it just inserts
``no'' after the option prefix, so ``-clobber'' becomes ``-noclobber''. (More
precisely, GetOptions
tests all option prefixes until one of them
matches at the beginning of the option name. It then inserts ``no''
between this prefix and the rest of the string. So, if you want to
support both GNU-style options (like --clobber
) and one-hyphen
options (-c
), be sure to give ``--'' first when setting the option
patterns with &SetOptionPatterns
. Otherwise, the negative
alternative to ``--clobber'' will be ``-no-clobber'', which might not be
what you wanted.) Sometimes, though, you want to explicitly specify the
negative alternative. This is done by putting both alternatives in the
option name, separated by a vertical bar, e.g. ``-verbose|-quiet''.
For example, the above two examples might be specified as
["-clobber", "boolean", undef, \$Clobber], ["-verbose|-quiet", "boolean", undef, \$Verbose],...);
If -clobber
is seen on the command line, $Clobber
will be set to 1;
if -noclobber
is seen, then $Clobber
will be set to 0. Likewise,
-verbose
results in $Verbose
being set to 1, and -quiet
will set
$Verbose
to 0.
["-foo", "const", "hello there", \$Foo]
On encountering -foo
, GetOptions
will copy ``hello there'' to $Foo
.
["-foo", "arrayconst", [3, 6, 2], \@Foo]
On encountering -foo
, GetOptions
will copy the array (3,6,2)
into
@Foo
.
["-foo", "hashconst", { "Perl" => "Larry Wall", "C" => "Dennis Ritchie", "Pascal" => "Niklaus Wirth" }, \%Inventors]
On encountering -foo
, GetOptions
will copy into %Inventors
a hash
relating various programming languages to the culprits primarily
responsible for their invention.
undef
value that would be copied under these circumstances with a
const option. This is useful when one program accepts options that
it simply passes to a sub-program; for instance, if prog1 calls
prog2, and prog2 might be run with the -foo option, then
prog1's argument table might have this option:
["-foo", "copy", undef, \$Foo, "run prog2 with the -foo option"]
and later on, you would run prog2 like this:
system ("prog2 $Foo ...");
That way, if -foo
is never seen on prog1's command line, $Foo
will
be untouched, and will expand to the empty string when building the command
line for prog2.
If num_values is anything other than undef
, then copy options
behave just like constant options.
sub process_foo { my ($opt, $args, $dest) = @_;
$$dest = shift @$args; # not quite right! (see below) }
with a corresponding option table entry:
["-foo", "call", [\$Foo], \&process_foo]
and then -foo
would act just like a scalar-valued string option that
copies into $Foo
. (Well, almost ... read on.)
A subtle point that might be missed from the above code: the value returned
by &process_foo
does matter: if it is false, then GetOptions
will
return 0 to its caller, indicating failure. To make sure that the user
gets a useful error message, you should supply one by calling SetError
;
doing so will prevent GetOptions
from printing out a rather mysterious
(to the end user, at least) message along the lines of ``subroutine call
failed''. The above example has two subtle problems: first, if the argument
following -foo
is an empty string, then process_foo
will return
the empty string---a false value---thus causing GetOptions
to fail
confusingly. Second, if there no arguments after -foo
, then
process_foo
will return undef
---again, a false value, causing
GetOptions
to fail.
To solve these problems, we have to define the requirements for the
-foo
option a little more rigorously. Let's say that any string
(including the empty string) is valid, but that there must be something
there. Then process_foo
is written as follows:
sub process_foo { my ($opt, $args, $dest) = @_;
$$dest = shift @$args; (defined $$dest) && return 1; &Getopt::Tabular::SetError ("bad_foo", "$opt option must be followed by a string"); return 0; }
The SetError
routine actually takes two arguments: an error class and
an error message. This is explained fully in the ERROR HANDLING
section, below. And, if you find yourself writing a lot of routines
like this, SetError
is optionally exported from Getopt::Tabular
,
so you can of course import it into your main package like this:
use Getopt::Tabular qw/GetOptions SetError/;
eval
'd) when the option is encountered on the command line. The
code is supplied (as a string) in the option_data field; again,
num_values is ignored. For example:
["-foo", "eval", undef, 'print "-foo seen on command line\n"']
will cause GetOptions
to print out (via an eval
) the string ``-foo seen
on the command line\n'' when -foo is seen. No other action is taken
apart from what you include in the eval string. The code is evaluated
in the package from which GetOptions
was called, so you can access
variables and subroutines in your program easily. If any error occurs
in the eval
, GetOptions
complains loudly and returns 0.
Note that the supplied code is always evaluated in a no strict
environment---that's because Getopt::Tabular is itself use
strict
-compliant, and I didn't want to force strictness on every quick
hack that uses the module. (Especially since eval options seem to be
used mostly in quick hacks.) (Anyone who knows how to fetch the
strictness state for another package or scope is welcome to send me
hints!) However, the -w state is untouched.
Generally, handling errors in the argument list is pretty transparent:
GetOptions
(or one of its minions) generates an error message and
assigns an error class, GetOptions
prints the message to the standard
error, and returns 0. You can access the error class and error message
using the GetError
routine:
($err_class, $err_msg) = &Getopt::Tabular::GetError ();
(Like SetError
, GetError
can also be exported from
Getopt::Tabular.) The error message is pretty simple---it is an
explanation for the end user of what went wrong, which is why
GetOptions
just prints it out and forgets about it. The error class
is further information that might be useful for your program; the
current values are:
SetError
itself.
Note that most of these are errors on the end user's part, such as bad
or missing arguments. There are also errors that can be caused by you,
the programmer, such as bad or missing values in the option table; these
generally result in GetOptions
croaking so that your program dies
immediately with enough information that you can figure out where the
mistake is. bad_eval is a borderline case; there are conceivably
cases where the end user's input can result in bogus code to evaluate,
so I grouped this one in the ``user errors'' class. Finally, asking for
help isn't really an error, but the assumption is that you probably
shouldn't continue normal processing after printing out the help---so
GetOptions
returns 0 in this case. You can always fetch the error
class with GetError
if you want to treat real errors differently from
help requests.
One of Getopt::Tabular's niftier features is the ability to generate and
format a pile of useful help text from the snippets of help you include
in your option table. The best way to illustrate this is with a couple
of brief examples. First, it's helpful to know how the user can trigger
a help display. This is quite simple: by default, GetOptions
always
has a ``-help'' option, presence of which on the command line triggers a
help display. (Actually, the help option is really your preferred
option prefix plus ``help''. So, if you like to make GNU-style options to
take precedence as follows:
&Getopt::Tabular::SetOptionPatterns qw|(--)([\w-]+) (-)(\w+)|;
then the help option will be ``--help''. There is only one help option
available, and you can set it by calling &SetHelpOption
(another
optional export).
Note that in addition to the option help embedded in the option table,
GetOptions
can optionally print out two other messages: a descriptive
text (usually a short paragraph giving a rough overview of what your
program does, possibly referring the user to the fine manual page), and
a usage text. These are both supplied by calling &SetHelp
, e.g.
$Help = <<HELP; This is the foo program. It reads one file (specified by -infile), operates on it some unspecified way (possibly modified by -threshold), and does absolutely nothing with the results. (The utility of the -clobber option has yet to be established.) HELP
$Usage = <<USAGE; usage: foo [options] foo -help to list options USAGE
&Getopt::Tabular::SetHelp ($Help, $Usage)
Note that either of the long help or usage strings may be empty, in
which case GetOptions
simply won't print them. In the case where both
are supplied, the long help message is printed first, followed by the
option help summary, followed by the usage. GetOptions
inserts enough
blank lines to make the output look just fine on its own, so you
shouldn't pad either the long help or usage message with blanks. (It
looks best if each ends with a newline, though, so setting the help
strings with here-documents---as in this example---is the recommended
approach.)
As an example of the help display generated by a typical option table, let's take a look at the following:
$Verbose = 1; $Clobber = 0; undef $InFile; @Threshold = (0, 1);
@argtbl = (["-verbose|-quiet", "boolean", 0, \$Verbose, "be noisy"], ["-clobber", "boolean", 0, \$Clobber, "overwrite existing files"], ["-infile", "string", 1, \$InFile, "specify the input file from which to read a large " . "and sundry variety of data, to which many " . "interesting operations will be applied", "<f>"], ["-threshold", "float", 2, \@Threshold, "only consider values between <v1> and <v2>", "<v1> <v2>"]);
Assuming you haven't supplied long help or usage strings, then when
GetOptions
encounters the help option, it will immediately stop
parsing arguments and print out the following option summary:
Summary of options: -verbose be noisy [default] -quiet opposite of -verbose -clobber overwrite existing files -noclobber opposite of -clobber [default] -infile <f> specify the input file from which to read a large and sundry variety of data, to which many interesting operations will be applied -threshold <v1> <v2> only consider values between <v1> and <v2> [default: 0 1]
There are a number of interesting things to note here. First, there are three option table fields that affect the generation of help text: option, help_string, and argdesc. Note how the argdesc strings are simply option placeholders, usually used to 1) indicate how many values are expected to follow an option, 2) (possibly) imply what form they take (although that's not really shown here), and 3) explain the exact meaning of the values in the help text. argdesc is just a string like the help string; you can put whatever you like in it. What I've shown above is just my personal preference (which may well evolve).
A new feature with version 0.3 of Getopt::Tabular is the inclusion of
default values with the help for certain options. A number of
conditions must be fulfilled for this to happen for a given option:
first, the option type must be one of the ``argument-driven'' types, such
as integer
, float
, string
, or a user-defined type. Second, the
option data field must refer either to a defined scalar value (for
scalar-valued options) or to a list of one or more defined values (for
vector-valued options). Thus, in the above example, the -infile
option doesn't have its default printed because the $InFile
scalar is
undefined. Likewise, if the @Threshold
array were the empty list
()
, or a list of undefined values (undef,undef)
, then the default
value for -threshold
also would not have been printed.
The formatting is done as follows: enough room is made on the right hand
side for the longest option name, initially omitting the argument
placeholders. Then, if an option has placeholders, and there is room
for them in between the option and the help string, everything (option,
placeholders, help string) is printed together. An example of this is
the -infile
option: here, ``-infile <f>'' is just small enough to fit
in the 12-character column (10 characters because that is the length of
the longest option, and 2 blanks), so the help text is placed right
after it on the same line. However, the -threshold
option becomes
too long when its argument placeholders are appended to it, so the help
text is pushed onto the next line.
In any event, the help string supplied by the caller starts at the same
column, and is filled to make a nice paragraph of help. GetOptions
will
fill to the width of the terminal (or 80 columns if it fails to find the
terminal width).
Finally, you can have pseudo entries of type section, which are important to make long option lists readable (and one consequence of using Getopt::Tabular is programs with ridiculously long option lists -- not altogether a bad thing, I suppose). For example, this table fragment:
@argtbl = (..., ["-foo", "integer", 1, \$Foo, "set the foo value", "f"], ["-enterfoomode", "call", 0, \&enter_foo_mode, "enter foo mode"], ["Non-foo related options", "section"], ["-bar", "string", 2, \@Bar, "set the bar strings (which have nothing whatsoever " . "to do with foo", "<bar1> <bar2>"], ...);
results in the following chunk of help text:
-foo f set the foo value -enterfoomode enter foo mode -- Non-foo related options --------------------------------- -bar b1 b2 set the bar strings (which have nothing whatsoever to do with foo
(This example also illustrates a slightly different style of argument placeholder. Take your pick, or invent your own!)
Since callbacks from the command line (call
and eval
options) can
do anything, they might be quite expensive. In certain cases, then, you
might want to make an initial pass over the command line to ensure that
everything is OK before parsing it ``for real'' and incurring all those
expensive callbacks. Thus, Getopt::Tabular
provides a ``spoof'' mode
for parsing a command line without side-effects. In the simplest case,
you can access spoof mode like this:
use Getopt::Tabular qw(SpoofGetOptions GetOptions); . . . &SpoofGetOptions (\@options, \@ARGV, \@newARGV) || exit 1;
and then later on, you would call GetOptions
with the original
@ARGV
(so it can do what SpoofGetOptions
merely pretended to do):
&GetOptions (\@options, \@ARGV, \@newARGV) || exit 1;
For most option types, any errors that GetOptions
would catch should
also be caught by SpoofGetOptions
-- so you might initially think
that you can get away without that || exit 1
after calling
GetOptions
. However, it's a good idea for a couple of reasons.
First, you might inadvertently changed @ARGV
-- this is usually a bug
and a silly thing to do, so you'd probably want your program to crash
loudly rather than fail mysteriously later on. Second, and more likely,
some of those expensive operations that you're initially avoiding by
using SpoofGetOptions
might themselves fail -- which would cause
GetOptions
to return false where SpoofGetOption
completes without
a problem. (Finally, there's the faint possiblity of bugs in
Getopt::Tabular
that would cause different behaviour in spoof mode
and real mode -- this really shouldn't happen, though.)
In reality, using spoof mode requires a bit more work. In particular,
the whole reason for spoof argument parsing is to avoid expensive
callbacks, but since callbacks can eat any number of command line
arguments, you have to emulate them in some way. It's not possible for
SpoofGetOptions
to do this for you, so you have to help out by
supplying ``spoof'' callbacks. As an example, let's say you have a
callback option that eats one argument (a filename) and immediately
reads that file:
@filedata = ();
sub read_file { my ($opt, $args) = @_;
warn ("$opt option requires an argument\n"), return 0 unless @$args; my $file = shift @$args; open (FILE, $file) || (warn ("$file: $!\n"), return 0); push (@filedata, <FILE>); close (FILE); return 1; }
@options = (['-read_file', 'call', undef, \&read_file]);
Since -read_file
could occur any number of times on the command line,
we might end up reading an awful lot of files, and thus it might be a
long time before we catch errors late in the command line. Thus, we'd
like to do a ``spoof'' pass over the command line to catch all errors. A
simplistic approach would be to supply a spoof callback that just eats
one argument and returns success:
sub spoof_read_file { my ($opt, $args) = @_; (warn ("$opt option requires an argument\n"), return 0) unless @$args; shift @$args; return 1; }
Then, you have to tell Getopt::Tabular
about this alternate callback
with no side-effects (apart from eating that one argument):
&Getopt::Tabular::SetSpoofCodes (-read_file => \&spoof_read_file);
(SetSpoofCodes
just takes a list of key/value pairs, where the keys
are call
or eval
options, and the values are the ``no side-effects''
callbacks. Naturally, the replacement callback for an eval
option
should be a string, and for a call
option it should be a code
reference. This is not actually checked, however, until you call
SpoofGetOptions
, because SetSpoofCodes
doesn't know whether
options are call
or eval
or what.)
A more useful spoof_read_file
, however, would actually check if the
requested file exists -- i.e., we should try to catch as many errors as
possible, as early as possible:
sub spoof_read_file { my ($opt, $args) = @_; warn ("$opt option requires an argument\n"), return 0 unless @$args; my $file = shift @$args; warn ("$file does not exist or is not readable\n"), return 0 unless -r $file; return 1; }
Finally, you can frequently merge the ``real'' and ``spoof'' callback into one subroutine:
sub read_file { my ($opt, $args, $spoof) = @_;
warn ("$opt option requires an argument\n"), return 0 unless @$args; my $file = shift @$args; warn ("$file does not exist or is not readable\n"), return 0 unless -r $file; return 1 if $spoof; open (FILE, $file) || (warn ("$file: $!\n"), return 0); push (@filedata, <FILE>); close (FILE); return 1; }
And then, when specifying the replacement callback to SetSpoofCodes
,
just create an anonymous sub that calls read_file
with $spoof
true:
&Getopt::Tabular::SetSpoofCodes (-read_file => sub { &read_file (@_[0,1], 1) }); Even though this means a bigger and more complicated callback, you only need I<one> such callback -- the alternative is to carry around both C<read_file> and C<spoof_read_file>, which might do redundant processing of the argument list.
Greg Ward <greg@bic.mni.mcgill.ca>
Started in July, 1995 as ParseArgs.pm, with John Ousterhout's Tk_ParseArgv.c as a loose inspiration. Many many features added over the ensuing months; documentation written in a mad frenzy 16-18 April, 1996. Renamed to Getopt::Tabular, revamped, reorganized, and documentation expanded 8-11 November, 1996.
Copyright (c) 1995-97 Greg Ward. All rights reserved. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
The documentation is bigger than the code, and I still haven't covered option patterns or extending the type system (apart from pattern types). Yow!
No support for list-valued options, although you can roll your own with call options. (See the demo program included with the distribution for an example.)
Error messages are hard-coded to English.
Getopt::Tabular - table-driven argument parsing for Perl 5 |