#!/usr/bin/perl

#
# NEPD Consulting
# Paul Guglielmino <paulg@nepd.com>
#
# Please send improvements or comments to me!
#

#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#

use strict;
use warnings;
use Getopt::Long;

my($version) = "1";

my($xref, $debug, $usage, $v);
Getopt::Long::Configure( 'permute' );
GetOptions( 'xref=s' => \$xref, 'debug' => \$debug, 
	    'help' => \$usage, 'version' => \$v );

if ( $usage ) { print_usage(); exit 0; }
if ( $v ) { print_version(); exit 0; }

my($patch) = $ARGV[0];
if ( ! $patch ) {
  print_usage();
  exit 2;
}

# Holds all information on prerequisite patches. It is a hash of arrays.
my(%prereqs, %patchrevs) = ();

# Take cmd line options or supply reasonable defaults
$debug = $debug || 0;
my($patchdiag) = $xref || "patchdiag.xref";

# Load information from out Patchdiag file
read_patchdiag($patchdiag);

# Generate a list of all requires patches and remove the duplicates
my(@required_patches) = find_allreqs($patch);
@required_patches = do { my %seen; grep !$seen{$_}++, @required_patches };
print "| ", join(" ", reverse(@required_patches)), " |\n";

exit 0;


# Use already defined structures to recurse through needed patches
sub find_allreqs {
  my $i;
  my $p = $_[0];
  my(@patches_to_check, @patch_list);
  if ( defined @{$prereqs{$p}} ) {
    $debug && print "Recursing down from: $p\n";
    @patches_to_check = @{$prereqs{$p}};
    push(@patch_list, $p);
    foreach $i (@patches_to_check) {
      push(@patch_list, find_allreqs(patchid($i)));
    }
  } else {
    $debug && print "Leaf patch: $p\n";
    push(@patch_list, $p);
  }
  return @patch_list;
}

# Return just the major patch id 
sub patchid {
  return( (split(/-/, $_[0]))[0] );
}

# Return just the rev number
sub patchrev {
  return( (split(/-/, $_[0]))[1] );
}

# Read the entire file, look out for comments and others things not directly
# related to the patch information
sub read_patchdiag {
  my($patchdiag_file) = $_[0];
  my(@line, @prs, $x, $p);
  
  open(PD, "$patchdiag_file") || die "No $patchdiag found\n";
  while( <PD> ) {
    if ( /^\d/ ) {
      @line = split(/\|/);
      @prs = ($line[8] =~ /(\d{6}-\d{2};)+?/g);
      $patchrevs{patchid($line[0])} = patchrev($line[0]);
      #print "$line[0] - @prs\n";
      foreach $p (@prs) {
	push( @{$prereqs{$line[0]}}, patchid($p) );
      }
    }
  }
  close(PD);
}

sub print_usage {

print<<EOT;

Show all required prerequisites for a Solaris patch.

  Usage: $0 [OPTIONS]... Patch Number
\t --help              [This message]
\t --debug             [Show debugging output]
\t --xref=<filename>   [Match patches against give xref file]
\t --version           [Print version number]

EOT
}

sub print_version {
  print "$0: Version $version\n\n";
}

