From f0bcb39a65ff55a0cbe3fced66903c730679150a Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Fri, 24 Jul 2015 18:00:32 +0100 Subject: [PATCH 1/1] lib/Odin.pm, bin/pastebin.userv: Use our own simple option parser. Perl's Getopt::Std doesn't let us handle options which are repeated. Getopt::Long can do this, but it involves a lot of messing about to set it up, and it has slightly bizarre option-cuddling behaviour. So we implement a very simple option parser and use it instead. --- bin/pastebin.userv | 36 ++++++++++++++++++++++++------------ lib/Odin.pm | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/bin/pastebin.userv b/bin/pastebin.userv index 4d5d4c0..3c87cb5 100755 --- a/bin/pastebin.userv +++ b/bin/pastebin.userv @@ -6,7 +6,6 @@ use Odin; use DBI; use Encode; use Encode::Locale; -use Getopt::Std; use POSIX; my $BAD = 0; @@ -53,11 +52,17 @@ EOF $t, $tag, $lang, encode locale => $title; } } elsif ($op eq "new") { - my %o; - getopts "l:t:", \%o and @ARGV == 0 - or Odin::fail "usage: new [-l LANG] [-t TITLE]"; - my %p = (title => decode(locale => $o{t}), lang => $o{l} // "txt", - content => read_content); + my $op = Odin::OptParse->new(@ARGV); + my $p = (title => undef, lang => "txt"); + while (my $o = $op->get) { + if ($o eq "l") { $p{lang} = $op->arg; } + elsif ($o eq "t") { $p{title} = decode locale => $op->arg; } + else { $op->unk; } + } + @ARGV = $op->rest; + $op->bad if @ARGV; + $op->ok or Odin::fail "usage: new [-l LANG] [-t TITLE]"; + $p{content} = read_content; my $db = Odin::open_db; my $c = ""; while (read STDIN, my $buf, 8192) { $c .= $buf; } @@ -85,12 +90,19 @@ EOF @ARGV or Odin::fail "usage: del TAG ..."; Odin::delete_pastebin map { $_, undef } @ARGV; } elsif ($op eq "update") { - my %o; - getopts "cl:t:", \%o and @ARGV == 1 - or Odin::fail "usage: update [-c] [-l LANG] [-t TITLE] TAG"; - my ($tag) = @ARGV; - my %p = (title => decode(locale => $o{t}), lang => $o{l}); - if ($o{c}) { $p{content} = read_content; } + my $op = Odin::OptParse->new(@ARGV); + my %p = (); + my $contentp = 0; + while (my $o = $op->get) { + if ($o eq "c") { $contentp = 1; } + elsif ($o eq "l") { $p{lang} = $op->arg; } + elsif ($o eq "t") { $p{title} = decode locale => $op->arg; } + else { $op->unk; } + } + @ARGV = $op->rest; + $op->bad if @ARGV; + $op->ok or Odin::fail "usage: new [-l LANG] [-t TITLE]"; + $p{content} = read_content if $contentp; Odin::update_pastebin $tag, undef, %p or Odin::fail "nothing changed"; } else { Odin::fail "unknown operation `$op'"; diff --git a/lib/Odin.pm b/lib/Odin.pm index 52c7dde..28118c8 100644 --- a/lib/Odin.pm +++ b/lib/Odin.pm @@ -431,6 +431,50 @@ sub tidy_pastebin_content ($) { return $content; } +###-------------------------------------------------------------------------- +### Simple option parser. + +package Odin::OptParse; + +sub new { + my ($cls, @args) = @_; + return bless { + cur => "", + args => \@args, + opt => undef, + ok => 1 + }, $cls; +} + +sub get { + my ($me) = @_; + if (!length $me->{cur}) { + my $args = $me->{args}; + if (!@$args) { return undef; } + elsif ($args->[0] =~ /^[^-]|^-$/) { return undef; } + elsif ($args->[0] eq "--") { shift @$args; return undef; } + $me->{cur} = substr shift @$args, 1; + } + my $o = $me->{opt} = substr $me->{cur}, 0, 1; + $me->{cur} = substr $me->{cur}, 1; + return $o; +} + +sub arg { + my ($me) = @_; + my $a; + if (length $me->{cur}) { $a = $me->{cur}; $me->{cur} = ""; } + elsif (@{$me->{args}}) { $a = shift @{$me->{args}}; } + else { $a = undef; $me->err("option `-$me->{opt}' requires an argument"); } + return $a; +} + +sub rest { return @{$_[0]->{args}}; } +sub ok { return $_[0]->{ok}; } +sub bad { $_[0]->{ok} = 0; } +sub err { $_[0]->bad; print STDERR "$PROG: $_[1]\n"; } +sub unk { $_[0]->err("unknown option `-$_[0]->{opt}'"); } + ###----- That's all, folks -------------------------------------------------- 1; -- 2.11.0