# Take version number comparing stuff from Dpkg::Version to sort kernel
# versions correctly (i.e. 4.0 > 3.19.x).
# Closes: #781657
---
 perl/lib/NeedRestart/Kernel.pm          | 63 +++++++++++++++++++++++++++++++++
 perl/lib/NeedRestart/Kernel/Linux.pm    |  2 +-
 perl/lib/NeedRestart/Kernel/kFreeBSD.pm |  2 +-
 3 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/perl/lib/NeedRestart/Kernel.pm b/perl/lib/NeedRestart/Kernel.pm
index 5c601ac..cacd7e7 100644
--- a/perl/lib/NeedRestart/Kernel.pm
+++ b/perl/lib/NeedRestart/Kernel.pm
@@ -42,6 +42,7 @@ our @ISA = qw(Exporter);
 
 our @EXPORT = qw(
     nr_kernel_check
+    nr_kernel_vcmp
     NRK_UNKNOWN
     NRK_NOUPGRADE
     NRK_ABIUPGRADE
@@ -74,4 +75,66 @@ sub nr_kernel_check($$) {
     return (NRK_UNKNOWN, %vars);
 }
 
+## The following version number comparing stuff was taken from Dpkg::Version.
+## The code has been adopted to be usable in needrestart w/o any additional
+## dependencies.
+
+sub _nr_kversion_order {
+    my ($x) = @_;
+
+    if ($x eq '~') {
+        return -1;
+    } elsif ($x =~ /^\d$/) {
+        return $x * 1 + 1;
+    } elsif ($x =~ /^[A-Za-z]$/) {
+        return ord($x);
+    } else {
+        return ord($x) + 256;
+    }
+}
+
+sub _nr_kversion_strcmp($$) {
+    my @a = map { _nr_kversion_order($_); } split(//, shift);
+    my @b = map { _nr_kversion_order($_); } split(//, shift);
+
+    while (1) {
+        my ($a, $b) = (shift @a, shift @b);
+        return 0 unless(defined($a) || defined($b));
+
+        $a ||= 0; # Default order for "no character"
+        $b ||= 0;
+
+        return 1 if($a > $b);
+        return -1 if($a < $b);
+    }
+}
+
+sub nr_kernel_vcmp($$) {
+    # sort well known devel tags just as grub does
+    my @v = map {
+	my $v = $_;
+	$v =~ s/[._-](pre|rc|test|git|old|trunk)/~$1/g;
+	$v;
+    } @_;
+
+    my @a = split(/(?<=\d)(?=\D)|(?<=\D)(?=\d)/, shift(@v));
+    my @b = split(/(?<=\d)(?=\D)|(?<=\D)(?=\d)/, shift(@v));
+
+    while(1) {
+	my ($a, $b) = (shift @a, shift @b);
+	return 0 unless(defined($a) || defined($b));
+
+	$a ||= 0;
+	$b ||= 0;
+	if($a =~ /^\d+$/ && $b =~ /^\d+$/) {
+	    my $cmp = $a <=> $b;
+	    return $cmp if($cmp);
+	}
+	else {
+	    my $cmp = _nr_kversion_strcmp($a, $b);
+	    return $cmp if($cmp);
+	}
+    }
+}
+
 1;
diff --git a/perl/lib/NeedRestart/Kernel/Linux.pm b/perl/lib/NeedRestart/Kernel/Linux.pm
index 4152c6d..5100335 100644
--- a/perl/lib/NeedRestart/Kernel/Linux.pm
+++ b/perl/lib/NeedRestart/Kernel/Linux.pm
@@ -124,7 +124,7 @@ sub nr_kernel_check_real($$) {
 	return (NRK_UNKNOWN, %vars);
     }
 
-    ($vars{EVERSION}) = reverse nsort keys %kernels;
+    ($vars{EVERSION}) = reverse sort { nr_kernel_vcmp($a, $b); } keys %kernels;
     print STDERR "$LOGPREF Expected kernel version: $vars{EVERSION}\n" if($debug);
 
     return (NRK_VERUPGRADE, %vars) if($vars{KVERSION} ne $vars{EVERSION});
diff --git a/perl/lib/NeedRestart/Kernel/kFreeBSD.pm b/perl/lib/NeedRestart/Kernel/kFreeBSD.pm
index c1b0f65..6c2fbd8 100644
--- a/perl/lib/NeedRestart/Kernel/kFreeBSD.pm
+++ b/perl/lib/NeedRestart/Kernel/kFreeBSD.pm
@@ -79,7 +79,7 @@ sub nr_kernel_check_real($$) {
 	return (NRK_UNKNOWN, %vars);
     }
 
-    ($vars{EVERSION}) = reverse nsort keys %kernels;
+    ($vars{EVERSION}) = reverse sort { nr_kernel_vcmp($a, $b); } keys %kernels;
     print STDERR "$LOGPREF Expected kernel version: $vars{EVERSION}\n" if($debug);
 
     return (NRK_VERUPGRADE, %vars) if($vars{KVERSION} ne $vars{EVERSION});
-- 
2.1.4

