blob: 6693493bb762f7645ae22eb84ffe064dfd0c8698 [file] [log] [blame]
#!/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>");
}
}
}
}