From 4c866d39b6664343b7409b2f5a1b2babd3a850b6 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Sun, 3 Nov 2024 19:26:56 -0800 Subject: [PATCH] Fix white-space handling; add `suppress-no-output` --- applets/pbot-vm/guest/lib/SplitLine.pm | 79 ++++++++++++++++++------- applets/pbot-vm/host/lib/SplitLine.pm | 80 +++++++++++++++++++------- lib/PBot/Core/Factoids/Data.pm | 1 + lib/PBot/Core/Interpreter.pm | 54 +++++++++++++---- lib/PBot/Core/ProcessManager.pm | 13 ++--- lib/PBot/VERSION.pm | 2 +- 6 files changed, 166 insertions(+), 63 deletions(-) diff --git a/applets/pbot-vm/guest/lib/SplitLine.pm b/applets/pbot-vm/guest/lib/SplitLine.pm index 72ab6d1d..9ff4cc7e 100644 --- a/applets/pbot-vm/guest/lib/SplitLine.pm +++ b/applets/pbot-vm/guest/lib/SplitLine.pm @@ -16,60 +16,75 @@ our @EXPORT = qw(split_line); # splits line into arguments separated by unquoted whitespace. # handles unbalanced quotes by treating them as part of the # argument they were found within. -sub split_line ($line, %opts) { +sub split_line($line, %opts) { my %default_opts = ( - strip_quotes => 0, - keep_spaces => 0, - preserve_escapes => 1, + strip_quotes => 0, + keep_spaces => 0, + preserve_escapes => 0, + strip_commas => 0, ); + print STDERR "split: [$line]\n"; + %opts = (%default_opts, %opts); + return () if not length $line; + my @chars = split //, $line; my @args; - my $escaped = 0; + my $ch; + my $pos; my $quote; - my $token = ''; - my $last_token = ''; - my $ch = ' '; - my $i = 0; - my $pos = 0; + my $escaped = 0; + my $token = ''; + my $last_token = ''; + my $i = 0; my $ignore_quote = 0; - my $spaces = 0; + my $spaces = 0; + my $add_token = 0; + my $got_ch = 0; while (1) { if ($i >= @chars) { if (defined $quote) { # reached end, but unbalanced quote... reset to beginning of quote and ignore it - $i = $pos; + $i = $pos; $ignore_quote = 1; - $quote = undef; - $token = $last_token; + $quote = undef; + $token = $last_token; } else { # add final token and exit - push @args, $token if length $token; + $token .= '\\' if $escaped; + push @args, $token; last; } } $ch = $chars[$i++]; - my $dquote = $quote // 'undef'; $spaces = 0 if $ch ne ' '; if ($escaped) { + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + if ($opts{preserve_escapes}) { $token .= "\\$ch"; } else { $token .= $ch; } + $escaped = 0; next; } if ($ch eq '\\') { $escaped = 1; + $got_ch = 1; next; } @@ -86,34 +101,56 @@ sub split_line ($line, %opts) { } if (not defined $quote and ($ch eq "'" or $ch eq '"')) { + $got_ch = 1; + + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + if ($ignore_quote) { # treat unbalanced quote as part of this argument $token .= $ch; $ignore_quote = 0; } else { # begin potential quoted argument - $pos = $i - 1; - $quote = $ch; + $pos = $i - 1; + $quote = $ch; $last_token = $token; $token .= $ch unless $opts{strip_quotes}; } next; } - if ($ch eq ' ') { + if ($ch eq ' ' or $ch eq "\n" or $ch eq "\t" or ($opts{strip_commas} and $ch eq ',')) { if (++$spaces > 1 and $opts{keep_spaces}) { $token .= $ch; next; } else { - push @args, $token if length $token; - $token = ''; + if ($opts{keep_spaces} && $ch eq "\n") { + $token .= $ch; + } + + unless ($opts{strip_commas} and $token eq ',') { + $add_token = 1 if $got_ch;; + } next; } } + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + + $got_ch = 1; $token .= $ch; } + use Data::Dumper; + print STDERR "split: ", Dumper(\@args), "\n"; return @args; } diff --git a/applets/pbot-vm/host/lib/SplitLine.pm b/applets/pbot-vm/host/lib/SplitLine.pm index ec982e28..9ff4cc7e 100644 --- a/applets/pbot-vm/host/lib/SplitLine.pm +++ b/applets/pbot-vm/host/lib/SplitLine.pm @@ -16,39 +16,47 @@ our @EXPORT = qw(split_line); # splits line into arguments separated by unquoted whitespace. # handles unbalanced quotes by treating them as part of the # argument they were found within. -sub split_line ($line, %opts) { +sub split_line($line, %opts) { my %default_opts = ( - strip_quotes => 0, - keep_spaces => 0, - preserve_escapes => 1, + strip_quotes => 0, + keep_spaces => 0, + preserve_escapes => 0, + strip_commas => 0, ); + print STDERR "split: [$line]\n"; + %opts = (%default_opts, %opts); + return () if not length $line; + my @chars = split //, $line; my @args; - my $escaped = 0; + my $ch; + my $pos; my $quote; - my $token = ''; - my $last_token = ''; - my $ch = ' '; - my $i = 0; - my $pos = 0; + my $escaped = 0; + my $token = ''; + my $last_token = ''; + my $i = 0; my $ignore_quote = 0; - my $spaces = 0; + my $spaces = 0; + my $add_token = 0; + my $got_ch = 0; while (1) { if ($i >= @chars) { if (defined $quote) { # reached end, but unbalanced quote... reset to beginning of quote and ignore it - $i = $pos; + $i = $pos; $ignore_quote = 1; - $quote = undef; - $token = $last_token; + $quote = undef; + $token = $last_token; } else { # add final token and exit - push @args, $token if length $token; + $token .= '\\' if $escaped; + push @args, $token; last; } } @@ -58,17 +66,25 @@ sub split_line ($line, %opts) { $spaces = 0 if $ch ne ' '; if ($escaped) { + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + if ($opts{preserve_escapes}) { $token .= "\\$ch"; } else { $token .= $ch; } + $escaped = 0; next; } if ($ch eq '\\') { $escaped = 1; + $got_ch = 1; next; } @@ -85,34 +101,56 @@ sub split_line ($line, %opts) { } if (not defined $quote and ($ch eq "'" or $ch eq '"')) { + $got_ch = 1; + + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + if ($ignore_quote) { # treat unbalanced quote as part of this argument $token .= $ch; $ignore_quote = 0; } else { # begin potential quoted argument - $pos = $i - 1; - $quote = $ch; + $pos = $i - 1; + $quote = $ch; $last_token = $token; $token .= $ch unless $opts{strip_quotes}; } next; } - if ($ch eq ' ') { - if ($opts{keep_spaces} && (++$spaces > 1 || $ch eq "\n")) { + if ($ch eq ' ' or $ch eq "\n" or $ch eq "\t" or ($opts{strip_commas} and $ch eq ',')) { + if (++$spaces > 1 and $opts{keep_spaces}) { $token .= $ch; next; } else { - push @args, $token if length $token; - $token = ''; + if ($opts{keep_spaces} && $ch eq "\n") { + $token .= $ch; + } + + unless ($opts{strip_commas} and $token eq ',') { + $add_token = 1 if $got_ch;; + } next; } } + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + + $got_ch = 1; $token .= $ch; } + use Data::Dumper; + print STDERR "split: ", Dumper(\@args), "\n"; return @args; } diff --git a/lib/PBot/Core/Factoids/Data.pm b/lib/PBot/Core/Factoids/Data.pm index e709b8a8..6a08cc68 100644 --- a/lib/PBot/Core/Factoids/Data.pm +++ b/lib/PBot/Core/Factoids/Data.pm @@ -46,6 +46,7 @@ our %factoid_metadata = ( 'ref_user' => 'TEXT', 'require_explicit_args' => 'INTEGER', 'requires_arguments' => 'INTEGER', + 'suppress-no-output' => 'INTEGER', 'type' => 'TEXT', 'unquote_spaces' => 'INTEGER', 'usage' => 'TEXT', diff --git a/lib/PBot/Core/Interpreter.pm b/lib/PBot/Core/Interpreter.pm index 7e15f270..03593bff 100644 --- a/lib/PBot/Core/Interpreter.pm +++ b/lib/PBot/Core/Interpreter.pm @@ -392,6 +392,14 @@ sub interpret($self, $context) { } } + if ($self->{pbot}->{commands}->get_meta($keyword, 'suppress-no-output') + or $self->{pbot}->{factoids}->{data}->get_meta($fact_channel, $fact_trigger, 'suppress-no-output')) + { + $context->{'suppress_no_output'} = 1; + } else { + delete $context->{'suppress_no_output'}; + } + if ($self->{pbot}->{commands}->get_meta($keyword, 'dont-replace-pronouns') or $self->{pbot}->{factoids}->{data}->get_meta($fact_channel, $fact_trigger, 'dont-replace-pronouns')) { @@ -1203,18 +1211,19 @@ sub split_line($self, $line, %opts) { my @chars = split //, $line; my @args; - my $escaped = 0; - my $quote; - my $token = ''; - my $last_token = ''; - my $ch = ' '; - my $i = 0; + my $ch; my $pos; + my $quote; + my $escaped = 0; + my $token = ''; + my $last_token = ''; + my $i = 0; my $ignore_quote = 0; my $spaces = 0; + my $add_token = 0; + my $got_ch = 0; while (1) { - if ($i >= @chars) { if (defined $quote) { # reached end, but unbalanced quote... reset to beginning of quote and ignore it @@ -1225,7 +1234,7 @@ sub split_line($self, $line, %opts) { } else { # add final token and exit $token .= '\\' if $escaped; - push @args, $token if length $token; + push @args, $token; last; } } @@ -1235,17 +1244,25 @@ sub split_line($self, $line, %opts) { $spaces = 0 if $ch ne ' '; if ($escaped) { + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + if ($opts{preserve_escapes}) { $token .= "\\$ch"; } else { $token .= $ch; } + $escaped = 0; next; } if ($ch eq '\\') { $escaped = 1; + $got_ch = 1; next; } @@ -1262,6 +1279,14 @@ sub split_line($self, $line, %opts) { } if (not defined $quote and ($ch eq "'" or $ch eq '"')) { + $got_ch = 1; + + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + if ($ignore_quote) { # treat unbalanced quote as part of this argument $token .= $ch; @@ -1286,14 +1311,19 @@ sub split_line($self, $line, %opts) { } unless ($opts{strip_commas} and $token eq ',') { - push @args, $token if length $token; + $add_token = 1 if $got_ch;; } - - $token = ''; next; } } + if ($add_token) { + push @args, $token; + $token = ''; + $add_token = 0; + } + + $got_ch = 1; $token .= $ch; } @@ -1303,7 +1333,7 @@ sub split_line($self, $line, %opts) { # creates an array of arguments from a string sub make_args($self, $string, %opts) { my %default_opts = ( - keep_spaces => 1, + keep_spaces => 0, preserve_escapes => 1, ); diff --git a/lib/PBot/Core/ProcessManager.pm b/lib/PBot/Core/ProcessManager.pm index e2c4ab37..d8b9743f 100644 --- a/lib/PBot/Core/ProcessManager.pm +++ b/lib/PBot/Core/ProcessManager.pm @@ -153,8 +153,11 @@ sub process_pipe_reader($self, $pid, $buf) { # check for output if (not defined $context->{result} or not length $context->{result}) { $self->{pbot}->{logger}->log("No result from process.\n"); - return if $context->{suppress_no_output}; - $context->{result} = "No output."; + if ($context->{suppress_no_output}) { + $context->{result} = ''; + } else { + $context->{result} = "No output."; + } } # don't output unnecessary result if command was embedded within a message @@ -165,12 +168,6 @@ sub process_pipe_reader($self, $pid, $buf) { # handle code factoid result if (exists $context->{special} and $context->{special} eq 'code-factoid') { $context->{result} =~ s/\s+$//g; - - if (not length $context->{result}) { - $self->{pbot}->{logger}->log("No text result from code-factoid.\n"); - return; - } - $context->{original_keyword} = $context->{root_keyword}; $context->{result} = $self->{pbot}->{factoids}->{interpreter}->handle_action($context, $context->{result}); } diff --git a/lib/PBot/VERSION.pm b/lib/PBot/VERSION.pm index 877d8de1..a67adf9d 100644 --- a/lib/PBot/VERSION.pm +++ b/lib/PBot/VERSION.pm @@ -25,7 +25,7 @@ use PBot::Imports; # These are set by the /misc/update_version script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 4830, + BUILD_REVISION => 4831, BUILD_DATE => "2024-11-03", };