1*f41ccc36Sespie# $OpenBSD: Getopt.pm,v 1.14 2023/07/06 08:29:26 espie Exp $ 2c84241d1Sespie 3c84241d1Sespie# Copyright (c) 2012 Marc Espie <espie@openbsd.org> 4c84241d1Sespie# 5c84241d1Sespie# Permission to use, copy, modify, and distribute this software for any 6c84241d1Sespie# purpose with or without fee is hereby granted, provided that the above 7c84241d1Sespie# copyright notice and this permission notice appear in all copies. 8c84241d1Sespie# 9c84241d1Sespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10c84241d1Sespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11c84241d1Sespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12c84241d1Sespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13c84241d1Sespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14c84241d1Sespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15c84241d1Sespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16c84241d1Sespie# 17c84241d1Sespie 18*f41ccc36Sespieuse v5.36; 19c84241d1Sespie 20c84241d1Sespiepackage Option; 21*f41ccc36Sespiesub factory($class, $o) 22c84241d1Sespie{ 2328252c27Safresh1 if ($o =~ m/^(.)$/) { 24c84241d1Sespie return Option::Short->new($1); 2528252c27Safresh1 } elsif ($o =~ m/^(.)\:$/) { 26c84241d1Sespie return Option::ShortArg->new($1); 2728252c27Safresh1 } elsif ($o =~ m/^(\-?.)(?:\:\!|\!\:)$/) { 285bf01557Sespie return Option::LongArg0->new($1); 2928252c27Safresh1 } elsif ($o =~ m/^(\-?.)\!$/) { 305bf01557Sespie return Option::Long->new($1); 3128252c27Safresh1 } elsif ($o =~ m/^(\-?.*)\=$/) { 32c84241d1Sespie return Option::LongArg->new($1); 3328252c27Safresh1 } elsif ($o =~ m/^(\-?.*)\:$/) { 34deab3a89Sespie return Option::LongArg0->new($1); 3528252c27Safresh1 } elsif ($o =~ m/^(\-?.*)$/) { 36c84241d1Sespie return Option::Long->new($1); 37c84241d1Sespie } 38c84241d1Sespie} 39c84241d1Sespie 40*f41ccc36Sespiesub new($class, $v) 41c84241d1Sespie{ 42c84241d1Sespie bless \$v, $class; 43c84241d1Sespie} 44c84241d1Sespie 45*f41ccc36Sespiesub setup($self, $opts, $isarray) 46c84241d1Sespie{ 47d38c979aSespie $opts->add_option_accessor($$self, $isarray); 48d38c979aSespie return $self; 49c84241d1Sespie} 50c84241d1Sespie 51d38c979aSespiepackage Option::Short; 52d38c979aSespieour @ISA = qw(Option); 53d38c979aSespie 54*f41ccc36Sespiesub match($self, $arg, $opts, $canonical, $code) 55c84241d1Sespie{ 5628252c27Safresh1 if ($arg =~ m/^\-\Q$$self\E$/) { 5728252c27Safresh1 &$code($opts, $canonical, 1, $arg); 58c84241d1Sespie return 1; 59c84241d1Sespie } 6028252c27Safresh1 if ($arg =~ m/^(\-\Q$$self\E)(.*)$/) { 616bcc3551Sespie unshift(@main::ARGV, "-$2"); 626bcc3551Sespie &$code($opts, $canonical, 1, $1); 63c84241d1Sespie return 1; 64c84241d1Sespie } 65c84241d1Sespie return 0; 66c84241d1Sespie} 67c84241d1Sespie 68c84241d1Sespiepackage Option::ShortArg; 69c84241d1Sespieour @ISA = qw(Option::Short); 70c84241d1Sespie 71*f41ccc36Sespiesub match($self, $arg, $opts, $canonical, $code) 72c84241d1Sespie{ 7328252c27Safresh1 if ($arg =~ m/^\-\Q$$self\E$/) { 7428252c27Safresh1 &$code($opts, $canonical, (shift @main::ARGV), $arg); 75c84241d1Sespie return 1; 76c84241d1Sespie } 7728252c27Safresh1 if ($arg =~ m/^(\-\Q$$self\E)(.*)$/) { 786bcc3551Sespie &$code($opts, $canonical, $2, $1); 79c84241d1Sespie return 1; 80c84241d1Sespie } 81c84241d1Sespie return 0; 82c84241d1Sespie} 83c84241d1Sespie 84c84241d1Sespiepackage Option::Long; 85c84241d1Sespieour @ISA = qw(Option); 86c84241d1Sespie 87*f41ccc36Sespiesub match($self, $arg, $opts, $canonical, $code) 88c84241d1Sespie{ 8928252c27Safresh1 if ($arg =~ m/^\-\Q$$self\E$/) { 9028252c27Safresh1 &$code($opts, $canonical, 1, $arg); 91c84241d1Sespie return 1; 92c84241d1Sespie } 93c84241d1Sespie return 0; 94c84241d1Sespie} 95c84241d1Sespie 96deab3a89Sespiepackage Option::LongArg0; 97c84241d1Sespieour @ISA = qw(Option::Long); 98*f41ccc36Sespiesub match($self, $arg, $opts, $canonical, $code) 99c84241d1Sespie{ 10028252c27Safresh1 if ($arg =~ m/^\-\Q$$self\E$/) { 101c84241d1Sespie if (@main::ARGV > 0) { 10228252c27Safresh1 &$code($opts, $canonical, (shift @main::ARGV), $arg); 103c84241d1Sespie return 1; 104c84241d1Sespie } else { 105c84241d1Sespie die "Missing argument for option -$$self\n"; 106c84241d1Sespie } 107c84241d1Sespie } 108deab3a89Sespie return 0; 109deab3a89Sespie} 110deab3a89Sespie 111deab3a89Sespiepackage Option::LongArg; 112deab3a89Sespieour @ISA = qw(Option::LongArg0); 113deab3a89Sespie 114*f41ccc36Sespiesub match($self, $arg, $opts, $canonical, $code) 115deab3a89Sespie{ 11628252c27Safresh1 if ($self->SUPER::match($arg, $opts, $canonical, $code)) { 117deab3a89Sespie return 1; 118deab3a89Sespie } 11928252c27Safresh1 if ($arg =~ m/^(-\Q$$self\E)\=(.*)$/) { 1206bcc3551Sespie &$code($opts, $canonical, $2, $1); 121c84241d1Sespie return 1; 122c84241d1Sespie } 123c84241d1Sespie return 0; 124c84241d1Sespie} 125c84241d1Sespie 12692d5e529Sespiepackage Option::Regexp; 127*f41ccc36Sespiesub new($class, $re, $code) 12892d5e529Sespie{ 12992d5e529Sespie bless {re => $re, code => $code}, $class; 13092d5e529Sespie} 13192d5e529Sespie 132*f41ccc36Sespiesub setup($self, $, $) 13392d5e529Sespie{ 134*f41ccc36Sespie return $self; 13592d5e529Sespie} 13692d5e529Sespie 137*f41ccc36Sespiesub match($self, $arg, $opts) 13892d5e529Sespie{ 13992d5e529Sespie if (my @l = ($arg =~ m/^$self->{re}$/)) { 14092d5e529Sespie &{$self->{code}}(@l); 14192d5e529Sespie return 1; 14292d5e529Sespie } else { 14392d5e529Sespie return 0; 14492d5e529Sespie } 14592d5e529Sespie} 14692d5e529Sespie 147c84241d1Sespiepackage Options; 148c84241d1Sespie 149*f41ccc36Sespiesub new($class, $string, $code) 150c84241d1Sespie{ 15192d5e529Sespie if (ref($string) eq 'Regexp') { 15292d5e529Sespie return Option::Regexp->new($string, $code); 15392d5e529Sespie } 154c84241d1Sespie my @alternates = split(/\|/, $string); 155c84241d1Sespie 156*f41ccc36Sespie bless { 157*f41ccc36Sespie alt => [map { Option->factory($_); } @alternates], 158*f41ccc36Sespie code => $code 159*f41ccc36Sespie }, $class; 160c84241d1Sespie} 161c84241d1Sespie 162*f41ccc36Sespiesub setup($self, $allopts, $isarray) 163c84241d1Sespie{ 164d38c979aSespie $self->{alt}[0]->setup($allopts, $isarray); 165c84241d1Sespie return $self; 166c84241d1Sespie} 167c84241d1Sespie 168*f41ccc36Sespiesub match($self, $arg, $opts) 169c84241d1Sespie{ 170c84241d1Sespie 171c84241d1Sespie my $canonical = ${$self->{alt}[0]}; 172c84241d1Sespie for my $s (@{$self->{alt}}) { 173c84241d1Sespie if ($s->match($arg, $opts, $canonical, $self->{code})) { 174c84241d1Sespie return 1; 175c84241d1Sespie } 176c84241d1Sespie } 177c84241d1Sespie return 0; 178c84241d1Sespie} 179c84241d1Sespie 180c84241d1Sespie# seems I spend my life rewriting option handlers, not surprisingly... 181c84241d1Sespiepackage LT::Getopt; 182fc08f4fdSespieuse LT::Util; 183c84241d1Sespie 184c84241d1Sespie 185c84241d1Sespie# parsing an option 'all-static' will automatically add an 186c84241d1Sespie# accessor $self->all_static that maps to the option. 187c84241d1Sespie 188*f41ccc36Sespiesub add_option_accessor($self, $option, $isarray) 189c84241d1Sespie{ 190c84241d1Sespie my $access = $option; 191c84241d1Sespie $access =~ s/^\-//; 192c84241d1Sespie $access =~ s/-/_/g; 193204852a3Sespie my $actual = $isarray ? 194*f41ccc36Sespie sub($self) { 195d38c979aSespie $self->{opt}{$option} //= []; 1960fa141abSespie if (wantarray) { 197204852a3Sespie return @{$self->{opt}{$option}}; 1980fa141abSespie } else { 1990fa141abSespie return scalar @{$self->{opt}{$option}}; 2000fa141abSespie } 201*f41ccc36Sespie } : sub($self) { 202c84241d1Sespie return $self->{opt}{$option}; 203c84241d1Sespie }; 204c84241d1Sespie my $callpkg = ref($self); 205c84241d1Sespie unless ($self->can($access)) { 206c84241d1Sespie no strict 'refs'; 207c84241d1Sespie *{$callpkg."::$access"} = $actual; 208c84241d1Sespie } 209c84241d1Sespie} 210c84241d1Sespie 211*f41ccc36Sespiesub create_options($self, @l) 212c84241d1Sespie{ 213c84241d1Sespie my @options = (); 214c84241d1Sespie # first pass creates accessors 21551d2374bSzhuk push(@l, '-tag=', sub { $self->add_tag($_[2]); }); 216c84241d1Sespie while (my $opt = shift @l) { 217204852a3Sespie my $isarray = ($opt =~ s/\@$//); 218c84241d1Sespie # default code or not 219c84241d1Sespie my $code; 220c84241d1Sespie if (@l > 0 && ref($l[0]) eq 'CODE') { 221c84241d1Sespie $code = shift @l; 222c84241d1Sespie } else { 223204852a3Sespie if ($isarray) { 224c84241d1Sespie $code = sub { 225c84241d1Sespie my ($object, $canonical, $value) = @_; 226c84241d1Sespie push(@{$object->{opt}{$canonical}}, $value); 227c84241d1Sespie }; 228c84241d1Sespie } else { 229c84241d1Sespie $code = sub { 230c84241d1Sespie my ($object, $canonical, $value) = @_; 231c84241d1Sespie $object->{opt}{$canonical} = $value; 232c84241d1Sespie }; 233c84241d1Sespie } 234c84241d1Sespie } 235*f41ccc36Sespie push(@options, 236*f41ccc36Sespie Options->new($opt, $code)->setup($self, $isarray)); 237c84241d1Sespie } 23869325921Sespie return @options; 23969325921Sespie} 24069325921Sespie 241*f41ccc36Sespiesub handle_options($self, @l) 24269325921Sespie{ 24369325921Sespie my @options = $self->create_options(@l); 244c84241d1Sespie 245c84241d1SespieMAINLOOP: 246c84241d1Sespie while (@main::ARGV > 0) { 24728252c27Safresh1 my $arg = shift @main::ARGV; 24828252c27Safresh1 if ($arg =~ m/^\-\-$/) { 249c84241d1Sespie last; 250c84241d1Sespie } 25128252c27Safresh1 if ($arg =~ m/^\-/) { 252c84241d1Sespie for my $opt (@options) { 25328252c27Safresh1 if ($opt->match($arg, $self)) { 254c84241d1Sespie next MAINLOOP; 255c84241d1Sespie } 256c84241d1Sespie } 25728252c27Safresh1 shortdie "Unknown option $arg\n"; 258c84241d1Sespie } else { 25928252c27Safresh1 unshift(@main::ARGV, $arg); 260d5a610c6Sespie last; 261c84241d1Sespie } 262c84241d1Sespie } 263c84241d1Sespie} 264c84241d1Sespie 265*f41ccc36Sespiesub handle_permuted_options($self, @l) 26669325921Sespie{ 26769325921Sespie my @options = $self->create_options(@l); 26869325921Sespie 26992d5e529Sespie $self->{kept} = []; 27092d5e529Sespie 27169325921SespieMAINLOOP2: 27269325921Sespie while (@main::ARGV > 0) { 27328252c27Safresh1 my $arg = shift @main::ARGV; 27428252c27Safresh1 if ($arg =~ m/^\-\-$/) { 27569325921Sespie next; # XXX ? 27669325921Sespie } 27728252c27Safresh1 if ($arg =~ m/^\-/) { 27869325921Sespie for my $opt (@options) { 27928252c27Safresh1 if ($opt->match($arg, $self)) { 28069325921Sespie next MAINLOOP2; 28169325921Sespie } 28269325921Sespie } 28369325921Sespie } 28428252c27Safresh1 $self->keep_for_later($arg); 28569325921Sespie } 28692d5e529Sespie @main::ARGV = @{$self->{kept}}; 28792d5e529Sespie} 28892d5e529Sespie 289*f41ccc36Sespiesub keep_for_later($self, @args) 29092d5e529Sespie{ 29192d5e529Sespie push(@{$self->{kept}}, @args); 29269325921Sespie} 29369325921Sespie 294*f41ccc36Sespiesub new($class) 295c84241d1Sespie{ 296c84241d1Sespie bless {}, $class; 297c84241d1Sespie} 298c84241d1Sespie 299c84241d1Sespie1; 300