#!/usr/local/bin/perl -w

# This script is an attempt at speeding up vpkg-provides2.sh (which
# came with the RPM 3.0.4 distribution).  It packages all the shared
# libraries listed in /var/sadm/install/contents.

use strict;
use Getopt::Std;
use File::Basename;
use Bootstrap;

sub sun_version ($);
sub init_hash ();
sub hash ($); 

my %opt;
my %packages = ();
my $solaris_contents_ver = '$Id: solaris-contents.pl,v 1.11 2002/06/17 20:18:44 chrisjs Exp $';
my @hash_table;
my $hash_polynomial = 0x04c11db7;

getopts("vchp:ur:", \%opt);

if ($opt{h} || ($opt{u} && $opt{p})) {
    print <<EOF;
Usage: solaris-contents.pl [-c] [-v] [-h] [-u] [-r repository] [-p packages]

  -c: Do not use cached versions of packages
  -h: Print this usage message
  -p: Write specfile for individual Solaris packages
  -r: Specify the repository to use
  -u: Update all vpkgs (cannot be used with '-p')
  -v: Verbose output

Without the '-p' argument, solaris-contents.pl makes spec files for
all installed Solaris packages.

$solaris_contents_ver
$bootstrap_pm_ver
EOF
    exit 0;
}
if ($opt{p}) {
    @packages{@ARGV} = (1) x @ARGV;
    $packages{$opt{p}} = 1;
}
if ($opt{v}) {
    # Guarantee interactive-style output
    select(STDERR); $| = 1;
    select(STDOUT); $| = 1;
}

set_repository($opt{r} || guess_repository);

open_ftp unless ($opt{c});

# The contents file is a flat-text database of every Sun package
# installed on the system.  This code builds up a hash (%pkgdb) that
# associates packages with the files that they provide.

my %pkgdb;

open (CONTENTS, "< $sun_contents")
    or die "Cannot open $sun_contents: $!";

print "Reading $sun_contents..." if $opt{v};

my $line;

while ($line = <CONTENTS>) {
    next if ($line =~ m(^#));
    
    my @dbentry;
    
    chomp $line;

# The last element of the contents record is the name of the package;
# the first is a filename.  Symlinks appear with '=', so we strip them
# out.
   if ($line =~ /^([^= ]+).* +(\S+)$/ && (!$opt{p} || $packages{"$2"})) {
       # remember all files in the selected package(s)
       push @{ $pkgdb{ $2 } }, $1;
   }
} 

close CONTENTS;

print "done.\n" if $opt{v};

my %vtable;
if ($opt{u}) {
    open(RPM, "/usr/local/bin/rpm -qa |")
        or die "Cannot run /usr/local/bin/rpm: $!";

    while (my $pkg = <RPM>) {
	chomp $pkg;
        if ($pkg =~ /^vpkg-([A-Z].+)-([^-]+-[^-]+)/) {
            $vtable{$1} = $2;
            print "REMOVE:vpkg-$1-$2\n"
                unless (defined($pkgdb{$1}));
        }
    }
    
    close(RPM) or die "Pipe error: $!";
}

init_hash();

foreach my $pkg (keys %pkgdb) {
    my ($fn, $ver, $rel, @prov, @req);

    ($ver, $rel) = sun_version $pkg;
    if (defined($vtable{$pkg})) {
        next if ($vtable{$pkg} eq "$ver-$rel");
        print "REMOVE:vpkg-$pkg-$vtable{$pkg}\n";
    }
    if (!$opt{c} && ($fn = copy_cached_if_available($pkg, $ver, $rel))) {
        print "Using cached file $fn.\n" if $opt{v};
	print "INSTALL:$fn\n" if $opt{u};
    } else {
        print "Creating specfile for $pkg-$ver-$rel.\n" if $opt{v};
	print "BUILD:vpkg-$pkg-$ver-$rel.spec\n" if $opt{u};
	print "INSTALL:vpkg-$pkg-$ver-$rel\n" if $opt{u};
        print_spec { NAME => $pkg, 
                     VERSION => $ver, 
                     RELEASE => $rel,
                     PROVIDES => get_provides(@{ $pkgdb{$pkg}}, $pkg),
                     REQUIRES => get_requires(@{ $pkgdb{$pkg}}) };
    }
}

close_ftp unless ($opt{c});

# Get Sun's package version
sub sun_version ($) {
    my $pkg = shift;
    my ($ver, $rel) = (1,"UNPATCHED");

    open (PKG, "< $sun_pkg_dir/$pkg/pkginfo")
        or warn "Cannot open $sun_pkg_dir/$pkg/pkginfo: $!";

    while (defined ($line = <PKG>)) {
	chomp $line;
        if ($line =~ /^VERSION=(.*)$/) {
	    $ver = $1;
	    $ver =~ s/-/_/g;
        }
	if ($line =~ /^PATCHLIST=(.*\S.*)$/) {
	    $rel = hash($1);
	}
    }
    return ($ver, $rel);
}

# I made a mistake coding the following routine -- the table is filled
# slightly incorrectly for polynomial division.  However since the
# caches are already filled, and the hash functions appear to work,
# leave it:

sub init_hash () {
    for (my $i = 0; $i < 256; $i++) {
	$hash_table[$i] = 0;
	for (my $j = 0; $j < 8; $j ++) {
	    $hash_table[$i] ^= ($hash_polynomial << $j) if ($i & (1 << $j));
	}
    }
}

sub hash ($) {
    my @str = unpack("C*", shift);
    my $rem = 0;

    while (my $c = shift @str) {
        $rem = ((($rem << 8) & 0xffffffff) | $c) ^ $hash_table[$rem >> 24];
    }
    $rem = (($rem << 8) & 0xffffffff) ^ $hash_table[$rem >> 24];
    $rem = (($rem << 8) & 0xffffffff) ^ $hash_table[$rem >> 24];
    $rem = (($rem << 8) & 0xffffffff) ^ $hash_table[$rem >> 24];

    return sprintf("%x", $rem);
}
