| #!/usr/bin/perl |
| |
| # ifpluginize-scores |
| # |
| # determine each rule's required ifplugin line (if any) and insert the ifplugin |
| # line (or move the score line inside an existing ifplugin line) into the score |
| # file, dumping the altered score file on STDOUT |
| # |
| # usage: ifpluginize-scores rules/ rules/50_scores.cf > rules/50_scores.cf.new |
| # ifpluginize-scores rules/ rules/72_scores.cf > rules/72_scores.cf.new |
| # |
| # <@LICENSE> |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to you under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at: |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # </@LICENSE> |
| # |
| # note: this only supports one ifplugin line per rule (no nested ifplugins) |
| # and it might not work on Windows due to line ending differences |
| # |
| # TODO: this is a quick hack to fix the currently broken 3.2.0 updates that |
| # currently have 102 scores missing ifplugin lines in the base release |
| # 50_scores.cf and 2 scores missing ifplugin lines in 72_scores.cf |
| # |
| # ideally this should be done way before now, like in whatever that |
| # combine-scores script is called |
| # |
| # also, after using and inserting any ifplugin lines that we are aware |
| # of in the actual rule cf files, we should probably brute force the |
| # detection of missing ifplugin lines by disabling one plugin at a time |
| # to see if anything breaks... fixing this case in the update |
| # automatically would be do-able, but it's probably better fixed in the |
| # sandbox/wherever (and just aborting the update) |
| |
| use strict; |
| use warnings; |
| |
| # parameters: /path/to/rules/dir /path/to/scores.cf |
| my $rulesDir = shift @ARGV; |
| my $scoreFile = shift @ARGV; |
| |
| # find out what plugins we need for each rule based on the rule files |
| my %ruleIfPlugins = (); |
| opendir(RULESDIR, $rulesDir) or die "Cannot open rules directory: $!\n"; |
| while (my $file = readdir(RULESDIR)) { |
| next unless $file =~ /\.cf$/; |
| open(RULEFILE, "$rulesDir/$file") or die "Cannot open rule file: $!\n"; |
| |
| my $currentPlugin; |
| while(<RULEFILE>) { |
| $_ =~ s/\015\012//; |
| chomp $_; |
| |
| $_ =~ s/^lang\s+\S+\s+//; # strip lang whatever describe to just describe |
| |
| next if ($_ =~ /^\s*(?:#.*)?$/); # comments |
| next if ($_ =~ /^shortcircuit\b/); # not handled, easiest to catch this sort of stuff by brute force (unload plugins and lint) |
| |
| if ($_ =~ /^(ifplugin\s+\S+)$/) { |
| $currentPlugin = $1; |
| } elsif ($_ =~ /^endif(?:$|\b)/) { |
| $currentPlugin = undef; |
| } elsif (defined $currentPlugin && $_ =~ /^\S+\s+(\S+)\s+/) { |
| if (exists $ruleIfPlugins{$1}) { |
| next if $ruleIfPlugins{$1} eq $currentPlugin; |
| warn "more than one ifplugin per rule not supported by this script!\n". |
| "rule: $1 requires $ruleIfPlugins{$1} and\n". |
| "$_\n"; |
| } else { |
| $ruleIfPlugins{$1} = $currentPlugin; |
| } |
| } |
| } |
| close RULEFILE; |
| } |
| close RULESDIR; |
| |
| # load the score file into memory |
| open(SCOREFILE, $scoreFile) or die "Cannot open score file: $scoreFile: $!\n"; |
| my @sf; |
| while (<SCOREFILE>) { |
| $_ =~ s/\015\012//; |
| chomp $_; |
| push (@sf, $_); |
| } |
| close SCOREFILE; |
| |
| # find any rules needing ifplugin lines and move them inside existing ifplugin |
| # lines for the required plugin, pay attention to mutable flags for fun |
| my %rulesNeedingNewIfplugins = (); |
| my %rulesNeedingNewIfpluginsIsMutable = (); |
| RULE: foreach my $rule (sort keys %ruleIfPlugins) { |
| # some vars to track where a score line appears |
| # note that we assume that when there are both ifplugin and gen:mutable lines |
| # around a rule that the ifplugin lines will enclose the gen:mutable lines |
| my $insideIfplugin = 0; |
| my $ifpluginStartLine = 0; |
| my $ifpluginEndLine = 0; |
| my $insideGenMutable = 0; |
| my $genMutableStartLine = 0; |
| my $genMutableEndLine = 0; |
| my $ruleIsMutable = 0; |
| my $ruleAtLine = 0; |
| my $line = 0; |
| foreach my $cfLine (@sf) { |
| $line++; # line is at array index + 1 so that we can be lazy and use 0 in the above vars |
| |
| # handle rules that fall in existing ifplugin lines, we'll handle new ifplugin lines later |
| if ($ruleAtLine && $ifpluginStartLine && $ifpluginEndLine) { |
| if ($ruleIsMutable) { |
| if ($genMutableStartLine && $genMutableEndLine) { |
| # existing ifplugin + mutable flags, just move the rule |
| moveRule(\@sf, $rule, $ruleAtLine-1, $genMutableStartLine, $genMutableEndLine-2); |
| } else { |
| # mutable rule, but no existing mutable flags, we'll add them |
| moveRule(\@sf, $rule, $ruleAtLine-1, $ifpluginStartLine-1, $ifpluginStartLine-1, 1); |
| } |
| } else { |
| if ($genMutableStartLine && $genMutableEndLine) { |
| # existing ifplugin + mutable flags, but the rule isn't mutable so we'll move the |
| # rule to just after the closing mutable flag but still inside the ifplugin |
| moveRule(\@sf, $rule, $ruleAtLine-1, $genMutableEndLine, $ifpluginEndLine-2); |
| } else { |
| # existing ifplugin and no mutable flags, just move the rule |
| moveRule(\@sf, $rule, $ruleAtLine-1, $ifpluginStartLine, $ifpluginEndLine-2); |
| } |
| } |
| # once we move the rule we move to the next one, reseting the tracking vars (above) |
| next RULE; |
| } |
| |
| # determine where we are (inside ifplugin, gen:mutable) |
| # again, we assume that if there's ifplugin lines any gen:mutable lines will be inside the ifplugin |
| if ($cfLine =~ /^$ruleIfPlugins{$rule}\b/) { # only looks for the ifplugin line we need |
| $insideIfplugin = 1; |
| $ifpluginStartLine = $line; |
| } elsif ($cfLine =~ /^endif/) { |
| $ifpluginEndLine = $line if $insideIfplugin; |
| $insideIfplugin = 0; |
| } elsif ($cfLine =~ /<gen:mutable>/) { |
| $insideGenMutable = 1; |
| $genMutableStartLine = $line if $insideIfplugin; |
| } elsif ($cfLine =~ /<\/gen:mutable>/) { |
| $insideGenMutable = 0; |
| $genMutableEndLine = $line if $insideIfplugin; |
| } elsif ($cfLine =~ /^score $rule\b/) { |
| $ruleAtLine = $line; |
| $ruleIsMutable = $insideGenMutable; |
| if ($insideIfplugin) { |
| # great, the rule is inside the ifplugin line it needs, check the next rule |
| #delete $ruleIfPlugins{$rule}; # careful, we're looping! just for debug anyway |
| next RULE; |
| } |
| } |
| } |
| # if we couldn't find somewhere to move the rule we'll add it to the end of the file later |
| $rulesNeedingNewIfplugins{$rule} = $ruleAtLine-1 if $ruleAtLine; |
| $rulesNeedingNewIfpluginsIsMutable{$rule} = $ruleIsMutable; |
| |
| #print "$rule : $ruleAtLine : $ifpluginStartLine : $ifpluginEndLine : $ruleIsMutable : $genMutableStartLine : $genMutableEndLine\n"; # if $ruleAtLine; |
| } |
| |
| # move any rules that require ifplugin lines that weren't already in the score |
| # file to the end of the score file and wrap them in their own ifplugin lines |
| my $i = 0; |
| foreach my $rule (sort { $rulesNeedingNewIfplugins{$a} <=> $rulesNeedingNewIfplugins{$b} } keys %rulesNeedingNewIfplugins) { |
| #print "rule: $rule at line: $rulesNeedingNewIfplugins{$rule} needs '$ruleIfPlugins{$rule}'\n"; |
| |
| my $line = $sf[$rulesNeedingNewIfplugins{$rule}-$i]; |
| splice(@sf, $rulesNeedingNewIfplugins{$rule}-$i, 1); |
| |
| if ($rulesNeedingNewIfpluginsIsMutable{$rule}) { |
| push (@sf, ("", "$ruleIfPlugins{$rule}", "# <gen:mutable>", $line, "# </gen:mutable>", "endif")); |
| } else { |
| push (@sf, ("", "$ruleIfPlugins{$rule}", $line, "endif")); |
| } |
| $i++; |
| } |
| |
| # output the result |
| foreach (@sf) { |
| print $_, "\n"; |
| } |
| |
| |
| |
| # move rules around in the array reference passed |
| # add mutable flags where appropriate |
| sub moveRule { |
| my ($aref, $rule, $current, $toStart, $toEnd, $addGenMutable) = @_; |
| $addGenMutable ||= 0; |
| |
| # warn "debug moveRule: $rule : $current : $toStart : $toEnd\n"; |
| |
| if ($current < $toStart) { |
| # add first, then delete |
| my $added = 0; |
| for (my $i = $toStart; $i <= $toEnd; $i++) { |
| next if $sf[$i] !~ /^score (\S+)\b/; |
| |
| if (($1 cmp $rule) == 1) { |
| splice(@{$aref}, $i, 0, @{$aref}[$current]); |
| if ($addGenMutable) { |
| splice(@{$aref}, $i, 0, "# <gen:mutable>"); |
| splice(@{$aref}, $i+2, 0, "# </gen:mutable>"); |
| } |
| $added = 1; |
| last; |
| } |
| } |
| unless ($added) { |
| splice(@{$aref}, $toEnd+1, 0, @{$aref}[$current]); |
| if ($addGenMutable) { |
| splice(@{$aref}, $toEnd+1, 0, "# <gen:mutable>"); |
| splice(@{$aref}, $toEnd+3, 0, "# </gen:mutable>"); |
| } |
| } |
| splice(@{$aref}, $current, 1); |
| |
| } else { |
| # delete first, then add |
| my $line = splice(@{$aref}, $current, 1); |
| |
| my $added = 0; |
| for (my $i = $toStart; $i <= $toEnd; $i++) { |
| next if $sf[$i] !~ /^score (\S+)\b/; |
| |
| if (($1 cmp $rule) == 1) { |
| splice(@{$aref}, $i, 0, @{$aref}[$current]); |
| if ($addGenMutable) { |
| splice(@{$aref}, $i, 0, "# <gen:mutable>"); |
| splice(@{$aref}, $i+2, 0, "# </gen:mutable>"); |
| } |
| $added = 1; |
| last; |
| } |
| } |
| unless ($added) { |
| splice(@{$aref}, $toEnd+1, 0, $line); |
| if ($addGenMutable) { |
| splice(@{$aref}, $toEnd+1, 0, "# <gen:mutable>"); |
| splice(@{$aref}, $toEnd+3, 0, "# </gen:mutable>"); |
| } |
| } |
| } |
| } |