package GetOpt::OO; use strict; =pod TODO Rationalise the option-passing method - I don't like the GNU or standard methods very much. Like XML (but NOT!), we need something that's parsed the same no matter what the program actually does. Well, GetOpt::OO already does that, but it still tries to look like the traditional method, which is not necessarily a good thing. We could have alternative lexical schemes - e.g. a lispish one. Prevent the command-line from calling any other methods apart from those we want to make available, i.e. those listed in the short->long map. =cut sub import { my $pkg = shift; @_ or return; my $call_pkg = caller(0); @_ % 2 == 0 and do { my $all_short = 1; my $second; for (@_) { $second = !$second or next; if ($_ =~ /../) { $all_short = 0; last; } } $all_short; } or die "usage: use GetOpt::OO h => 'help', f => 'foo', s => 'long' ..."; no strict 'refs'; # TODO: how to get this line not to cause a warning?! %{"${call_pkg}::short_opt_to_long"} = @_; } sub new { my $package = shift; my $target = shift or die "usage: GetOpt::OO->new(\$target, s => 'long', ...)"; bless { _getoptoo_target => $target, _getoptoo_short_opt_to_long => { @_ }, }, $package; } sub argv { my $self = shift; my $target = $self->{_getoptoo_target} || $self; while (@_ and $_[0] =~ /^\-/) { (my $opt = shift) =~ s/^\-//; last if $opt eq "-"; if ($opt eq "") { $target->can("stdio") or die "we don't understand `-' (for stdio)\n"; $target->stdio; } else { my $long = $opt =~ s/^\-//; last if $opt eq ""; my @args; if ($opt =~ s/^([^:=]+)=(.*)/$1/) { @args = $2; } elsif ($opt =~ s/^([^:=]+):(.*)/$1/) { if ($2 ne "") { push @args, $2; } while (@_ and $_[0] !~ /^-/) { push @args, shift; } } elsif (!$long and $opt =~ /../) { ($opt, @args) = $opt =~ /(.)(.*)/; } $long or $opt = $target->short_opt_to_long($opt); $target->process_option($opt, @args); } } return @_; } sub short_opt_to_long { my $self = shift; my $target = $self->{_getoptoo_target} || $self; my $pkg = ref $self; my $short_opt = shift; $short_opt =~ /../ and die "short option `$short_opt' is not short!"; my $long_opt = ( $self->{_getoptoo_short_opt_to_long} || do { no strict 'refs'; \%{"${pkg}::short_opt_to_long"} } || {} )->{$short_opt}; defined $long_opt or die "unrecognised short option `$short_opt'\n"; $target->can($long_opt) or die "internal error: short option `$short_opt' maps to `$long_opt' but there is no corresponding method!\n"; return $long_opt; } sub process_option { my $self = shift; my $target = $self->{_getoptoo_target} || $self; my $option = shift; $target->can($option) or die "unrecognised option `$option'\n"; $target->$option(@_); } 1