#!/usr/bin/env perl
#
# oldfiles - show outdated packages, sources, build logs on a CRUX system
#
# by Simone Rota <sip@varlock.com>,
#    Mark Rosenstand <mark@borkware.net>
# License: GNU GPL v2
# 
# Requires prt-get by Johannes Winkelmann
#
# User beware:
# - Ports not using signatures for their manifests will have all their 
# source files listed. Contact those port maintainers and get them to 
# abandon the deprecated md5sums.
# - Deactivating a repository in prt-get.conf will allow all its packages
# and source files to be reported by this script.

use warnings;
use strict;

my %options = %{getoptions(@ARGV)};
my $pkgdir = "";
my $srcdir = "";
my $logdir = "";
my $logtemplate = "";
my $writelog = "disabled";
my $compress = "xz";
my %wanted;
my %keepme;

# Assume packages and sources if no options specified (historical behaviour).
# Users who want to search for build logs (feature added in Sep 2023)
#  must pass the -l flag explicitly
if ( $options{"-p"} + $options{"-s"} + $options{"-l"} == 0 ) {
    $options{"-p"} = 1;
    $options{"-s"} = 1;
    $options{"-l"} = 0;
}

# Read pkgmk.conf
open CONFIG, "/etc/pkgmk.conf" or die "Could not open /etc/pkgmk.conf";
while (<CONFIG>) {
    $srcdir = $1 if m/^\s*PKGMK_SOURCE_DIR=(.*)(#|\n)/;
    $pkgdir = $1 if m/^\s*PKGMK_PACKAGE_DIR=(.*)(#|\n)/;
    $compress = $1 if m/^\s*PKGMK_COMPRESSION_MODE=(.*)(#|\n)/;
}
close CONFIG;
# Remove quotation marks and trailing whitespace
$srcdir =~ s/['"]//g; $srcdir =~ s/\s+$//;
$pkgdir =~ s/['"]//g; $pkgdir =~ s/\s+$//;
$compress =~ s/['"]//g; $compress =~ s/\s+$//;

# Check if dirs are specified / exists
if ( $options{"-p"} ) {
    if ($pkgdir eq ""){
        print STDERR "error: no PKGMK_PACKAGE_DIR specified in /etc/pkgmk.conf\n";
        print STDERR "       skipping the search for old packages.\n";
        $options{"-p"} = 0;
    } elsif (! -d $pkgdir) {
        print STDERR "error: $pkgdir is not a directory\n";
        print STDERR "       skipping the search for old packages.\n";
        $options{"-p"} = 0;
    }
}
if ( $options{"-s"} ) {
    if ($srcdir eq ""){
        print STDERR "error: no PKGMK_SOURCE_DIR specified in /etc/pkgmk.conf\n";
        print STDERR "       skipping the search for old sources.\n";
        $options{"-s"} = 0;
    } elsif (! -d $srcdir) {
        print STDERR "error: $srcdir is not a directory\n";
        print STDERR "       skipping the search for old sources.\n";
        $options{"-s"} = 0;
    }
}
if ( $options{"-l"} ) {
    open PGCONF, "/etc/prt-get.conf" or die "cannot open prt-get.conf";
    while (<PGCONF>) {
        $writelog = $1 if m/^\s*writelog\s+(enabled|disabled)/;
        $logtemplate = $1 if m/^\s*logfile\s+(.*)(#|\n)/;
    }
    close PGCONF;
    if ($logtemplate ne "") {
        $logdir = $logtemplate;
        $logdir =~ s/[^\/]+$//;
        $logdir =~ s/\/$//;
    }
    if (($writelog eq "disabled") or ($logdir =~ m/(%p|%n|%v|%r)/)) {
        print STDERR "warning: build logs were not written to a shared directory,\n";
        print STDERR "         skipping the search for old logs.\n";
        $options{"-l"} = 0;
    }
}

# Early exit if no valid dirs are found
if ($options{"-s"}+$options{"-p"}+$options{"-l"}==0) { exit 1; }

# Collect current sources / packages / log files
foreach (split('\n', `prt-get printf "%p:%n:%v:%r:%i\n"`)) {
    my ($path, $name, $version, $release, $isinst) = split(':', $_, 5);
    my $i = ( ($options{"-i"}) and ($isinst eq "no") ) ? 0 : 1;

    if ( ($options{"-p"}) and ($i) ) {
        $wanted{$pkgdir}{"$name\#$version-$release.pkg.tar.$compress"} = 1;
    }
    if ( ($options{"-s"}) and ($i) ) {
        open SIGNATURES, "$path/$name/.signature" or next;
        while (<SIGNATURES>) {
            m/^SHA256\s\((.+)\)\s.+\n/;
            if ($1 && ($1 ne "Pkgfile") && ($1 ne ".footprint")) {
                $wanted{$srcdir}{$1} = 1;
            }
        }
        close SIGNATURES;
    }
    if ( ($options{"-l"}) and ($i) ) {
        my $logfile = $logtemplate;
        my %subs = ( $logdir => "", "%p" => $path, "%n" => $name, 
                 "%v" => $version, "%r" => $release );
        $logfile =~ s/($logdir|%p|%n|%v|%r)/$subs{$1}/g;
        $logfile =~ s/^\///;
        $wanted{$logdir}{$logfile} = 1;
    }
}

# Keep user-specified files
if ( -f "/etc/oldfiles.conf")   {
    my $keep;
    open KEEPME, "/etc/oldfiles.conf" or die "Could not open /etc/oldfiles.conf";
    while ($keep = <KEEPME>) {
        chomp($keep);
        $keepme{$keep} = 1;
    }
}
close KEEPME;

# Display unwanted files
foreach my $dir (keys %wanted) {
    opendir DIR, $dir;
    foreach (readdir DIR) {
        next if ($_ eq '.' or $_ eq '..');
        print "$dir/$_\n" unless ($wanted{$dir}{$_} or $keepme{"$dir/$_"});
    }
    closedir DIR;
}

######################## subroutines ########################

# Adapted  from Martin Opel's prtorphan script
sub getoptions {
    my @args = reverse @_;
    my %options = ("-p" => 0, "-s" => 0, "-l" => 0, "-i" =>0);
  
    while (my $argument = pop @args) {
        if ( $argument eq "-p" ) { $options{"-p"} = 1;
        } elsif ( $argument eq "-s" ) { $options{"-s"} = 1;
        } elsif ( $argument eq "-l" ) { $options{"-l"} = 1;
        } elsif ( $argument eq "-i" ) { $options{"-i"} = 1;
        } else {
            usage();
            exit 1;
        }
    } 
    return \%options;
}

# Show usage
sub usage {
    print "usage: oldfiles [-p|-s|-l|-i]\n";
}
