Subject: [PATCH] Handle variables in the diversions checks by replacing them with a regex.

From: Raphael Geissert <[email-masked]>
Date: Sun, 29 Mar 2009 11:49:52 -0600

---
 checks/scripts                                     |  124 +++++++++++++++++---
 .../debian/debian/install                          |    4 +
 .../debian/debian/postrm                           |   24 ++++
 .../debian/debian/preinst                          |   29 +++++
 t/tests/scripts-diversions-variables/debian/orphan |    1 +
 .../scripts-diversions-variables/debian/some-file  |    1 +
 t/tests/scripts-diversions-variables/desc          |    9 ++
 t/tests/scripts-diversions-variables/tags          |    5 +
 8 files changed, 180 insertions(+), 17 deletions(-)
 create mode 100644 t/tests/scripts-diversions-variables/debian/debian/install
 create mode 100644 t/tests/scripts-diversions-variables/debian/debian/postrm
 create mode 100644 t/tests/scripts-diversions-variables/debian/debian/preinst
 create mode 100644 t/tests/scripts-diversions-variables/debian/orphan
 create mode 100644 t/tests/scripts-diversions-variables/debian/some-file
 create mode 100644 t/tests/scripts-diversions-variables/desc
 create mode 100644 t/tests/scripts-diversions-variables/tags

diff --git a/checks/scripts b/checks/scripts
index e9a6890..9542b78 100644
--- a/checks/scripts
+++ b/checks/scripts
@@ -525,7 +525,9 @@ open(SCRIPTS, '<', "control-scripts")
 # normal scripts above, because there were just enough differences to
 # make a shared function awkward.
 
-my (%added_diversions, @removed_diversions);
+my %added_diversions;
+my %removed_diversions;
+my $expand_diversions = 0;
 while (<SCRIPTS>) {
     chop;
 
@@ -880,12 +882,16 @@ while (<SCRIPTS>) {
 		# remove the leading / because it's not in the index hash
 		$divert =~ s,^/,,;
 
+		$divert = quotemeta($divert);
+
+		# For now just replace variables, they will later be normalised
+		$expand_diversions = 1 if $divert =~ s/\\\$\w+/.+/g;
+		$expand_diversions = 1 if $divert =~ s/\\\$\\{\w+\\}/.+/g;
+
 		if ($mode eq 'add') {
-		    $added_diversions{$divert} = $file;
-		    tag 'diversion-for-unknown-file', $divert, "$file:$."
-			unless (exists $info->index->{$divert});
+		    $added_diversions{$divert} = {'script' => $file, 'line' => $.};
 		} elsif ($mode eq 'remove') {
-		    push(@removed_diversions, [ $divert, $file, $. ]);
+		    push @{$removed_diversions{$divert}}, {'script' => $file, 'line' => $.};
 		} else {
 		    fail "Internal error: \$mode has unknown value: ".
 			"$mode";
@@ -913,24 +919,97 @@ while (<SCRIPTS>) {
 }
 close(SCRIPTS);
 
-for (@removed_diversions) {
-    my ($divert, $file, $line) = @$_;
+# If any of the maintainer scripts used a variable in the file or
+# diversion name normalise them all
+if ($expand_diversions) {
+    for my $divert (keys %removed_diversions, keys %added_diversions) {
+
+	# if a wider regex was found, the entries might no longer be there
+	unless (exists($removed_diversions{$divert})
+	    or exists($added_diversions{$divert})) {
+	    next;
+	}
+
+	my $widerrx = $divert;
+	my $wider = $widerrx;
+	$wider =~ s/\\//g;
+
+	# find the widest regex:
+	my @matches = grep {
+		    my $lrx = $_;
+		    my $l = $lrx;
+		    $l =~ s/\\//g;
+
+		    if ($wider =~ m/^$lrx$/) {
+			$widerrx = $lrx;
+			$wider = $l;
+			1;
+		    } elsif ($l =~ m/^$widerrx$/) {
+			1;
+		    } else {
+			0;
+		    }
+		} (keys %removed_diversions, keys %added_diversions);
+
+	# replace all the occurences with the widest regex:
+	for my $k (@matches) {
+	    next if ($k eq $widerrx);
+
+	    if (exists($removed_diversions{$k})) {
+		$removed_diversions{$widerrx} = $removed_diversions{$k};
+		delete $removed_diversions{$k};
+	    }
+	    if (exists($added_diversions{$k})) {
+		$added_diversions{$widerrx} = $added_diversions{$k};
+		delete $added_diversions{$k};
+	    }
+	}
+    }
+}
+
+for my $divert (keys %removed_diversions) {
 
     if (exists $added_diversions{$divert}) {
-	# do not really delete the entry, because a --remove
-	# might happen in two branches in the script, i.e. we
+	# just mark the entry, because a --remove might
+	# happen in two branches in the script, i.e. we
 	# see it twice, which is not a bug
-	undef $added_diversions{$divert};
-    } elsif ($file eq 'postrm') {
-	# Allow preinst and postinst to remove diversions the
-	# package doesn't add to clean up after previous
-	# versions of the package.
-	tag 'remove-of-unknown-diversion', $divert, "$file:$line";
+	$added_diversions{$divert}{'removed'} = 1;
+    } else {
+	for my $item (@{$removed_diversions{$divert}}) {
+	    my $script = $item->{'script'};
+	    my $line = $item->{'line'};
+
+	    next unless ($script eq 'postrm');
+
+	    # Allow preinst and postinst to remove diversions the
+	    # package doesn't add to clean up after previous
+	    # versions of the package.
+
+	    $divert = unquote($divert, $expand_diversions);
+
+	    tag 'remove-of-unknown-diversion', $divert, "$script:$line";
+	}
     }
 }
 
-for my $file (grep { defined $added_diversions{$_} } keys %added_diversions) {
-    tag 'orphaned-diversion', $file, $added_diversions{$file};
+for my $divert (keys %added_diversions) {
+    my $script = $added_diversions{$divert}{'script'};
+    my $line = $added_diversions{$divert}{'line'};
+
+    my $divertrx = $divert;
+    $divert = unquote($divert, $expand_diversions);
+
+    if ($expand_diversions) {
+	tag 'diversion-for-unknown-file', $divert, "$script:$line"
+	    unless (grep { $_ =~ m/$divertrx/ } keys %{$info->index});
+    } else {
+	tag 'diversion-for-unknown-file', $divert, "$script:$line"
+	    unless (exists $info->index->{$divert});
+    }
+
+    if (not exists $added_diversions{$divertrx}{'removed'}) {
+	tag 'orphaned-diversion', $divert, $script;
+    }
 }
 }
 
@@ -1043,6 +1122,17 @@ sub remove_comments {
     return $_;
 }
 
+sub unquote($$) {
+    my ($string, $replace_regex) = @_;
+
+    $string =~ s,\\,,g;
+    if ($replace_regex) {
+	$string =~ s,\.\+,*,g;
+    }
+
+    return $string;
+}
+
 1;
 
 # Local Variables:
diff --git a/t/tests/scripts-diversions-variables/debian/debian/install b/t/tests/scripts-diversions-variables/debian/debian/install
new file mode 100644
index 0000000..c29cc54
--- /dev/null
+++ b/t/tests/scripts-diversions-variables/debian/debian/install
@@ -0,0 +1,4 @@
+orphan usr/share/scripts/t1
+some-file usr/share/scripts/t1
+orphan usr/share/scripts/t2/foo
+some-file usr/share/scripts/t2/foo
diff --git a/t/tests/scripts-diversions-variables/debian/debian/postrm b/t/tests/scripts-diversions-variables/debian/debian/postrm
new file mode 100644
index 0000000..6a5800a
--- /dev/null
+++ b/t/tests/scripts-diversions-variables/debian/debian/postrm
@@ -0,0 +1,24 @@
+#!/bin/sh
+set -e
+
+if [ remove = "$1" ]; then
+    # Clean up the correct diversion from preinst.
+    dpkg-divert --package scripts-diversions --remove --rename \
+        --divert /usr/share/scripts/t2/foo/some-file.real \
+        /usr/share/scripts/t2/foo/some-file
+
+    d=scripts
+    # Clean up some other diversion that we didn't create.
+    dpkg-divert --package scripts-diversions --remove --rename \
+       /usr/lib/$d/old-file
+
+    # Clean up a diversion we did not create for a non-existent file.
+    dpkg-divert --remove --package scripts-diversions --rename \
+        --divert /usr/share/scripts/$v/no-such-file.real \
+        /usr/share/scripts/$v/no-such-file
+
+    # Clean up a diversion we did create for a non-existent file.
+    dpkg-divert --package foo --remove /etc/scripts/orphan
+fi
+
+#DEBHELPER#
diff --git a/t/tests/scripts-diversions-variables/debian/debian/preinst b/t/tests/scripts-diversions-variables/debian/debian/preinst
new file mode 100644
index 0000000..ad7ec6c
--- /dev/null
+++ b/t/tests/scripts-diversions-variables/debian/debian/preinst
@@ -0,0 +1,29 @@
+#!/bin/sh
+set -e
+
+if [ install = "$1"  ]; then
+    # This is a correct diversion.
+    dpkg-divert --package scripts-diversions --add --rename \
+        --divert /usr/share/scripts/t2/foo/some-file.real \
+        /usr/share/scripts/t2/foo/some-file
+
+    # This is broken -- can't use local.
+    dpkg-divert --local \
+		--add \
+		--divert \
+		/usr/share/scripts/other-file.real \
+		/usr/share/scripts/other-file
+
+    f=orphan
+    # This is also correct, but we're not going to clean it up.
+    dpkg-divert --package scripts-diversions --add --rename \
+        --divert /usr/share/scripts/t1/$f.real \
+        /usr/share/scripts/t1/$f
+
+    # Try to divert a file we don't ship
+    dpkg-divert --package scripts-diversions --add --rename \
+        --divert /etc/scripts/$f.real \
+        /etc/scripts/$f
+fi
+
+#DEBHELPER#
diff --git a/t/tests/scripts-diversions-variables/debian/orphan b/t/tests/scripts-diversions-variables/debian/orphan
new file mode 100644
index 0000000..934fd74
--- /dev/null
+++ b/t/tests/scripts-diversions-variables/debian/orphan
@@ -0,0 +1 @@
+Some file that we're going to divert and not clean up.
diff --git a/t/tests/scripts-diversions-variables/debian/some-file b/t/tests/scripts-diversions-variables/debian/some-file
new file mode 100644
index 0000000..649e369
--- /dev/null
+++ b/t/tests/scripts-diversions-variables/debian/some-file
@@ -0,0 +1 @@
+Some random file that we're going to install after diverting.
diff --git a/t/tests/scripts-diversions-variables/desc b/t/tests/scripts-diversions-variables/desc
new file mode 100644
index 0000000..b1266b4
--- /dev/null
+++ b/t/tests/scripts-diversions-variables/desc
@@ -0,0 +1,9 @@
+Testname: scripts-diversions-variables
+Sequence: 0000
+Version: 1.0
+Description: Tests of dpkg-divert checks with variables
+Test-For:
+ diversion-for-unknown-file
+ package-uses-local-diversion
+ orphaned-diversion
+ remove-of-unknown-diversion
diff --git a/t/tests/scripts-diversions-variables/tags b/t/tests/scripts-diversions-variables/tags
new file mode 100644
index 0000000..5fba84f
--- /dev/null
+++ b/t/tests/scripts-diversions-variables/tags
@@ -0,0 +1,5 @@
+E: scripts-diversions-variables: diversion-for-unknown-file etc/scripts/* preinst:26
+E: scripts-diversions-variables: orphaned-diversion usr/share/scripts/t1/* preinst
+E: scripts-diversions-variables: package-uses-local-diversion preinst:15
+E: scripts-diversions-variables: remove-of-unknown-diversion usr/lib/*/old-file postrm:13
+E: scripts-diversions-variables: remove-of-unknown-diversion usr/share/scripts/*/no-such-file postrm:18