#!/bin/bash
# vim: cindent:shiftwidth=4:tabstop=4:smarttab:textwidth=100

set -o errexit
set -o pipefail
set -o nounset
#set -o xtrace

#$title$ APT package availability status
#$check$ installed packages are sourced from a *network* repository
#$ref$ /etc/apt/sources.list.d/
#$author$ Rafal Rzeczkowski
#$version$ 0.9.5

level_check long

#CHANGELOG
#0.50	initial
#0.60	errexit compatibility, logic flow redesign
#0.70	complete rewrite to optimize speed and align functionality with intended goals
#0.71	metadata headers
#0.80	complete parser rewrite (AWK) to reduce memory usage and align functionality with intended goals
#0.90	complete parser rewrite (Perl) to use version comparison library Dpkg::Version
#0.91	correct failed package count (separate ingested array elements by new lines)
#0.92	replace version variable with a text changelog
#0.93	report failures using the "caution" priority level
#0.94	integrate with the the FIX API
#0.9.5	add new line to Perl output to allow parsing of a single item

declare -r -a PROBLEMS=(PACKAGES_WITHOUT_NETWORK_SOURCE)

TMP_PERL=$(mktemp)
perl -e'
use strict;
use warnings;

use Carp;
use English qw{-no_match_vars};
use feature qw{say};

use Dpkg::Version;

$INPUT_RECORD_SEPARATOR  = "\n\n";
$OUTPUT_FIELD_SEPARATOR  = q{ };
my $INPUT_FIELD_SEPARATOR = "\n";

my ( %dpkg, %apt, %failed );

RECORD: while (<>) {
	chomp; # strip record separator
	my @fields = split $INPUT_FIELD_SEPARATOR, $_;

	my ( $package, $version );

	foreach my $field (@fields) {
		if ( substr( $field, 1, 1 ) eq q{ } ) {
			next RECORD;
		}
		my ( $key, $value ) = split ":[[:space:]]", $field;
		if ( $key eq "Package" ) {
			$package = $value;
		}
		if ( $key eq "Version" ) {
			$version = $value;
			if ($package) {
				if ( $ARGV eq "/var/lib/dpkg/status" ) {
					$dpkg{$package} = $version;
				}
				else {
					if ( exists $dpkg{$package} ) {
						if ( defined $apt{$package} ) {
							my $incoming_version_newer = defined $Dpkg::Version::VERSION ?
								Dpkg::Version->new($version) > Dpkg::Version->new( $apt{$package} ) :
								Dpkg::Version::compare_versions( $version, q{>}, $apt{$package} );
							$apt{$package} = $version
								if ($incoming_version_newer);
						}
						else {
							$apt{$package} = $version;
						}
					}
				}
			}
			else {
				croak "missing package name for version " . $value;
			}
		}
	}

}

foreach my $pkgname ( keys %dpkg ) {
	$apt{$pkgname} //= "MISSING";
	if ( $dpkg{$pkgname} ne $apt{$pkgname} ) {
		$failed{$pkgname} = "sys:" . $dpkg{$pkgname} . ", net:" . $apt{$pkgname};
	}
}
say keys %failed;
' '/var/lib/dpkg/status' /var/lib/apt/lists/*_Packages > $TMP_PERL

read -a PKGS_FAILED < $TMP_PERL
rm $TMP_PERL
if [[ ${#PKGS_FAILED[@]} -gt 0 ]]; then
	PACKAGES=$(plural_text ${#PKGS_FAILED[@]} 'package')
	fail caution "no network sources for ${#PKGS_FAILED[@]} $PACKAGES: ${PKGS_FAILED[*]}"
	helpmsg 'manually installed packages are not permitted; consult packages maintainer'
	problem PACKAGES_WITHOUT_NETWORK_SOURCE $(printf '%s;' ${PKGS_FAILED[@]})
else
	ok
fi
