use strict;
use warnings;
package Getopt::Easy;
our $VERSION = 0.1;
use Exporter;
our @ISA = qw/Exporter/;
our %O;
our @EXPORT = qw/get_options %O/;
sub get_options {
my ($optstr, $usage, $helpchars) = @_;
$helpchars ||= "";
$optstr =~ s/^\s*//;
my $err = 0;
my (%options, %valid);
my ($l, $word);
for (split(/\s+/, $optstr)) {
($l, $word) = /^(.)-(.*)$/
or die "$_: syntax error - must be like this: l-length\n";
#
# check for the = sign.
# there are two different uses of it
#
if ($word =~ s/=$//) {
$l .= '=';
} elsif ($word =~ s/=(.+)//) {
$l .= '=';
$valid{$word} = $1;
}
$options{$l} = $word;
$O{$word} = ($l =~ /=/)? "": 0 unless exists $O{$word};
}
exit if $err;
#
# with %options and %valid and %O and $helpchars initialized properly
# we are now ready to examine @ARGV
#
my ($arg, $let, $val);
$err = "";
ARGV: while (@ARGV and $ARGV[0] =~ s/^-//) {
$arg = shift @ARGV;
last if $arg eq "-"; # stop processing options
while ($arg =~ s/^(.)//) {
$let = $1;
if (index($helpchars, $let) >= 0) { # help
require "Pod/Text.pm";
Pod::Text->new->parse_from_file($0);
exit;
} elsif (exists $options{$let}) { # boolean
$O{$options{$let}} = 1;
} elsif (exists $options{"$let="}) { # with value
if ($arg eq "" and not $arg = shift @ARGV) {
$err .= "missing argument for -$let\n";
next ARGV;
} elsif ($arg =~ /^-/) {
$err .= "value $arg for -$let begins with a dash\n";
next ARGV;
} else {
$O{$options{"$let="}} = $arg;
$arg = "";
}
if (my $v = $valid{$options{"$let="}}) { # debugging style
#
# $v now contains the only valid options for -$let
#
my $opts = $O{$options{"$let="}};
$opts =~ s/[$v]//g; # remove the good ones
if ($opts) {
my $plural = (length($opts) > 1)? "s": "";
$err .= "for -$let: illegal option$plural: ".
"$opts, valid ones are: $v\n";
}
}
} else {
$err .= "unknown option: -$let\n";
}
}
}
if ($err) {
if ($usage) {
#
# make sure there is a newline
# else we'll get "at line ..."
#
chomp $usage;
$err .= "$usage\n";
}
die $err;
}
}
1;
=head1 NAME
Getopt::Easy - parses command line options in a simple but capable way.
=head1 SYNOPSIS
use Getopt::Easy;
get_options "v-verbose f-fname= D-debug=uSX",
"usage => "usage: prog [-v] [-f fname] [-D [uSX]] [-H]",
"H";
print "reading $O{fname}\n" if $O{verbose};
print "SQL: $sql\n" if $O{debug} =~ /S/;
=head1 DESCRIPTION
Perl puts the command line parameters in the array @ARGV
allowing the user to examine and manipulate it like any
other array. There is a long tradition of putting optional
single character flags (preceded by a dash) in front of
other parameters like so:
% ls -ltr *.h *.c
% tar -tvf all.tar
% ps -ax -U jsmith
Many Getopt::* modules exist to help with the
parsing of these flags out of @ARGV.
For the author, Getopt::Std was visually too cryptic and
Getopt::Long was too large and complex for most normal applications.
Getopt::Easy is small, easy to understand, and provides a visual clarity.
There are two things exported: get_options() and %O.
get_options has 1 required parameter and 2 optional ones.
The first is a string describing the kind of options that
are expected. It is a space separated list of terms like this:
get_options "v-verbose f-fname=";
If the -v option is given on the command
line %O{verbose} will be set to 1 (true).
If the -f option is given then another argument is expected
which will be assigned to $O{fname}.
Before parsing @ARGV, $O{verbose} will be initialized to 0 (false) and
$O{fname} to "" (unless they already have a value).
If you give an unknown option get_options() will complain and exit:
% prog -vX
unknown option: -X
%
These conventions are implemented by Getopt::Easy:
=over 4
=item *
The options can come in any order.
=item *
Multiple boolean options can be bundled together.
=item *
A command line argument of '--' will cause argument parsing to stop
so you can parse the rest of the options yourself.
=item *
Parsed arguments are removed from @ARGV.
=back
These invocations are equivalent:
% prog -v -f infile
% prog -f infile -v # different order
% prog -v -finfile
% prog -vf infile
% prog -vfinfile
This shows that the space between -f and infile is optional
and that you I<can> bundle -f with -v but -f must be
the I<last> option in the bundle.
The optional second parameter to get_options() is
a usage message to be printed when an illegal option is given.
get_options "v-verbose f-fname=",
"usage: prog [-v] [-f fname]";
Now if an unknown option is given, the same
error message will be printed, as above, followed
by the usage message.
% prog -vX
unknown option: -X
usage: prog [-v] [-f fname]
%
=head2 HELP
Sometimes the usage message is not enough and the
user needs more detailed and elaborate help. This is
where the 3rd optional parameter comes in.
get_options "v-verbose f-fname=",
"usage: prog [-v] [-f fname] [-H]",
"H";
Giving the -H option will cause the POD for the module
to be echoed to STDOUT - as if the user had typed
'perldoc prog'. See 'perldoc perlpod'.
=head2 DEBUGGING
There are various ways to implement a debugging option:
GOOD:
get_options "d-debug";
print "val = $val\n" if $O{debug};
BETTER:
get_options "d-debug=";
print "SQL = $sql\n" if $O{debug} >= 2;
print "val = $val\n" if $O{debug} >= 3;
With this method there are various I<levels> of debugging.
Unfortunately, they often end up ranging from
'not enough' to 'too much' :(.
BEST:
get_options "d-debug=eSvL";
print "SQL = $sql\n" if $O{debug} =~ /S/;
print "val = $val\n" if $O{debug }=~ /v/;
With this kind of term the letters after the equal sign '=' are the
debugging options that are valid. Now the user can choose exactly
what kind of debugging output they wish to see.
% prog -d SL
Giving an illegal debugging option will
result in an error message:
% prog -deXSf
for -d: illegal options: Xf, valid ones are: eSvL
%
=head1 ACCESS ELSEWHERE
If you want access to the %O hash from other files simply put:
use Getopt::Easy;
at the top of those files; the %O hash will again be exported into the
current package. You need to have:
get_options ...;
only once in the main file before anyone needs to look at the %O hash.
=head1 STRICT
It is easy to misspell a key for the %O hash. Tie::StrictHash
can help with this:
use GetOpt::Easy;
use Tie::StrictHash;
get_options "v-verbose f-fname=";
strict_hash %O;
print "file name is $O{filename}\n";
This will give a fatal error message:
key 'filename' does not exist at prog line 6
=head1 SEE ALSO
Config::Easy allows configuration file entries
to be overidden with command line arguments.
Tie::StrictHash protects against misspelling of key names.
Date::Simple is an elegant way of dealing with dates.
=head1 AUTHOR
Jon Bjornstad <jon@icogitate.com>