+++ /dev/null
-#!/usr/bin/perl
-
-# RPM (and it's source code) is covered under two separate licenses.
-
-# The entire code base may be distributed under the terms of the GNU
-# General Public License (GPL), which appears immediately below.
-# Alternatively, all of the source code in the lib subdirectory of the
-# RPM source code distribution as well as any code derived from that
-# code may instead be distributed under the GNU Library General Public
-# License (LGPL), at the choice of the distributor. The complete text
-# of the LGPL appears at the bottom of this file.
-
-# This alternatively is allowed to enable applications to be linked
-# against the RPM library (commonly called librpm) without forcing
-# such applications to be distributed under the GPL.
-
-# Any questions regarding the licensing of RPM should be addressed to
-# Erik Troan <ewt@redhat.com>.
-
-
-# rpmdiff - a program for comparing two rpm files for differences.
-# Written by Ken Estes, Mail.com.
-
-use Getopt::Long;
-
-# much of this code comes from reading the book
-# "Maximum RPM" by Edward C. Bailey
-
-
-sub usage {
-
- my $args = "[--".join("] [--", @ARGS)."]";
- my @default_args = map $RPMTAG->{$_}->{'arg'}, @DEFAULT_CMP;
- my $default_args = "--".join(" --", @default_args)."";
-
- my $usage =<<EOF;
-
-$0 [--version] [--help] [--cmpmode] [--all]
- $args
- old_rpm_file new_rpm_file
-
-
-Arguments
-
-
---version Print version information for this program
-
---help Show this usage page
-
---cmpmode Do not send any information to stdout, instead
- exit with zero if the rpm files are identical and
- exit with 1 if there are differences.
-
---all Ensure that all possible comparisons will be performed.
- This argument should not be used with any CMP arguments.
-
-
-CMP Arguments
-
-
-Many of the options are designed to select which comparisons the user
-is interested in so that spurious differences can be ignored. If no
-CMP arguments are chosen then the default arguments are picked.
-
-The CMP arguments are:
- $args
-
-The default arguments are:
- $default_args
-
-There are two methods of picking which comparisions will be performed.
-Any differences between the two files which are not in the list of
-performed comparisons will be ignored.
-
-First arguments may be specified on the command line preceeded with a
-'--' as in '--md5' these arguments specify which comparisons will be
-performed. If a comparison is not mentioned on the command line it will
-not be performed.
-
-Second arguments may be specified on the command line preceeded with a
-'--no' as in '--nomd5' these arguments specify which comparisons will
-not be performed. If a comparison is not mentioned on the command line
-it will be prefomed.
-
-You can not mix the two types of arguments.
-
-
-Synopsis
-
-
-This script will compare old_rpm_file and new_rpm_file and print to
-standard out the differences between the two files. The output is
-designed to help isolate the changes which will occur when upgrading
-an installed package. The output looks like the output of 'rpm -V'.
-It is assumed that you have installed oldpackage and are thinking of
-upgrading to newpackage. The output is as if you ran 'rpm -Va' after
-installing the new package with cpio so that the rpm database was not
-up todate. Thus 'rpm -Va' will pick up the differences between the
-two pacakges.
-
-
-Additionally the RPM scripts (prein, postin, triggers, verify, etc)
-are compared and their results are displayed as if the scripts ended
-up as files in the filesystem.
-
-Exit Code
-
-
-The if not run in cmpmode the program exists with the number of files
-which are different between the two packages, the exitcode will get no
-bigger than $MAX_EXIT.
-
-If run in cmpmode then the program will exit with zero if the rpm
-files are identical and exit with 1 if there are differences.
-
-
-BUGS
-
-
-This program only parses the RPM file headers not the cpio payload
-inside the RPM file. In the rare case where an tag is defined in one
-RPM and not in another we charitably assume that the RPMs match on
-this tag. An example of this may be file uid/gid\'s. Currently rpm
-does not store this information in the header, it appears only in the
-cpio payload. If you were to compare two rpm files and one of them
-does not have the uid/gid\'s in the header then no difference in
-uid/gid will ever appear in the output regardless of what the RPMs
-actually contain.
-
-The program only checks differences between files and scripts, any
-changes to dependencies, prefixes, or spec file header information are
-not checked.
-
-There is no method provided to check changes in a files flags, this
-includes changes to the files documentation, MISSINGOK, NOREPLACE, or
-configuration status.
-
-
-Output Format
-
-
-The differences are sent to stdout. There is one line for each file
-which is different. The differences are encoded in a string using the
-following letters to represent the differerences and the following
-order to encode the information:
-
- S is the file size
-
- M is the file\'s mode
-
- 5 is the MD5 checksum of the file
-
- D is the files major and monor numbers
-
- L is the files symbolic link contents.
-
- U is the owner of the file
-
- G is the file\'s group
-
- T is the modification time of the file
-
- added indicates the file was added to the old version
-
- missing indicates the file was deleted from the old version
-
-Any attributes which match are denoted with a '.'.
-
-
-Output Example
-
-
-S.5..... PREIN
-.....UG. /bin/echo
-..5....T /usr/bin/grep
-S.5....T /etc/info-dir
-missing /usr/doc/dhcpcd-0.70/README
-.M...... /usr/lib/libpanel.so.4
-added /usr/lib/libmenu.so.4
-SM5....T /usr/info/dir
-
-
-Usage Example
-
-
-$0 --help
-$0 --version
-
-$0 java-jdk-1.1.7-2.rpm java-jdk-1.1.7-3.rpm
-$0 --md5 java-jdk-1.1.7-2.rpm java-jdk-1.1.7-3.rpm
-$0 --nomd5 java-jdk-1.1.7-2.rpm java-jdk-1.1.7-3.rpm
-$0 --md5 --link --mtime java-jdk-1.1.7-2.rpm java-jdk-1.1.7-3.rpm
-$0 --all java-jdk-1.1.7-2.rpm java-jdk-1.1.7-3.rpm
-$0 --cmpmode java-jdk-1.1.7-2.rpm java-jdk-1.1.7-3.rpm
-$0 --cmpmode --md5 java-jdk-1.1.7-2.rpm java-jdk-1.1.7-3.rpm
-
-
-EOF
-
- print $usage;
- exit 0;
-
-}
-
-
-
-sub set_static_vars {
-
-# This functions sets all the static variables which are often
-# configuration parameters. Since it only sets variables to static
-# quantites it can not fail at run time. Some of these variables are
-# adjusted by parse_args() but asside from that none of these
-# variables are ever written to. All global variables are defined here
-# so we have a list of them and a comment of what they are for.
-
-
-# The numerical indicies to the hash comes from ~rpm/lib/rpmlib.h
-# the index is the number of the tag as it appears in the rpmfile.
-
-# tag_name: the name of the tag as it appears in rpmlib.h.
-
-# all entries have a tag_name if an entry has one of the next three
-# fields it must have the rest. If a tag does not have the next three
-# fields we we will still gather data for it. This is for future
-# functionality as these fields look important, and for debugging.
-
-
-# diff_order: if the tag is used in the different output then this is
-# the order of the character differences.
-
-# diff_char: if the tag is used in the different output then this is
-# the character which will appear when there is a
-# difference.
-
-# arg: the name of the command line option to specify whether this tag
-# is used in the difference or not
-
-# The scripts contained in the rpm (preinstall, postinstall) do not
-# have the comparison information that files have. Some of the
-# comparisons (md5, size) can be performed on scripts, using regular
-# perl functions, others (uid, modes, link) can not. We use
-# script_cmp to link the perl comparison function to the comparison
-# arguments and to the diff_char.
-
-# script_cmp: the perl comparion function to perform if this
-# difference can be performed on the rpm scripts
-
-# is_script: if defined this indicates that the datatype is a script
-# and we can use script_cmp on it. The data is stored as an array
-# containing a single string.
-
-
-# note: that although we may need to denote differences in the flags
-# this table may not be appropriate for that task.
-
-
-# The data from the RPM files is stored in $RPM0, $RPM1 they are HoL.
-# The hash is indexed with the same index as appears in $RPMTAG, the
-# other data is what ever comes out of the file. Most of the data we
-# want is stored as arrays of values (occasionally it is a scalar
-# value). We keep a hash to allow us to find the index number to use
-# in all arrays given the filename. Some information like the package
-# name and the posinstall script are stored, in the hash table as an
-# array which contains a single string.
-
-
-
-
- $RPMTAG = {
- 1000 => {
- 'tag_name' => 'NAME',
- },
- 1001 => {
- 'tag_name' => 'VERSION',
- },
- 1002 => {
- 'tag_name' => 'RELEASE',
- },
- 1006 => {
- 'tag_name' => 'BUILDTIME',
- },
- 1027 => {
- 'tag_name' => 'OLDFILENAMES',
- },
- 1028 => {
- 'tag_name' => 'FILESIZES',
- 'diff_order' => 0,
- 'diff_char' => 'S',
- 'arg' => 'size',
- 'script_cmp' => sub { return (length($_[0]) ne
- length($_[1])); },
- },
- 1029 => {
- 'tag_name' => 'FILESTATES',
- },
- 1030 => {
- 'tag_name' => 'FILEMODES',
- 'diff_order' => 1,
- 'diff_char' => 'M',
- 'arg' => 'mode',
- },
- 1033 => {
- 'tag_name' => 'FILERDEVS',
- 'diff_order' => 3,
- 'diff_char' => 'D',
- 'arg' => 'dev',
- },
- 1034 => {
- 'tag_name' => 'FILEMTIMES',
- 'diff_order' => 7,
- 'diff_char' => 'T',
- 'arg' => 'mtime',
- },
- 1035 => {
- 'tag_name' => 'FILEMD5S',
- 'diff_order' => 2,
- 'diff_char' => '5',
- 'arg' => 'md5',
- 'script_cmp' => sub{ return ($_[0] ne
- $_[1]); },
- },
- 1036 => {
- 'tag_name' => 'FILELINKTOS',
- 'diff_order' => 4,
- 'diff_char' => 'L',
- 'arg' => 'link',
- },
- 1037 => {
- 'tag_name' => 'FILEFLAGS',
- },
- 1038 => {
- 'tag_name' => 'ROOT',
- },
- 1039 => {
- 'tag_name' => 'FILEUSERNAME',
- 'diff_order' => 5,
- 'diff_char' => 'U',
- 'arg' => 'user',
- },
- 1040 => {
- 'tag_name' => 'FILEGROUPNAME',
- 'diff_order' => 6,
- 'diff_char' => 'G',
- 'arg' => 'group',
- },
- 1098 => {
- 'tag_name' => 'PREFIXES',
- },
- 1099 => {
- 'tag_name' => 'INSTPREFIXES',
- },
-
- # support for differences of scripts
-
- 1023 => {
- 'tag_name' => 'PREIN',
- 'is_script' => 1,
- },
- 1024 => {
- 'tag_name' => 'POSTIN',
- 'is_script' => 1,
- },
- 1025 => {
- 'tag_name' => 'PREUN',
- 'is_script' => 1,
- },
- 1026 => {
- 'tag_name' => 'POSTUN',
- 'is_script' => 1,
- },
- 1079 => {
- 'tag_name' => 'VERIFYSCRIPT',
- 'is_script' => 1,
- },
- 1065 => {
- 'tag_name' => 'TRIGGERSCRIPTS',
- 'is_script' => 1,
- },
- 1091 => {
- 'tag_name' => 'VERIFYSCRIPTPROG',
- 'is_script' => 1,
- },
- 1092 => {
- 'tag_name' => 'TRIGGERSCRIPTPROG',
- 'is_script' => 1,
- },
-
- };
-
- # by default check these options, which are the "contents" of the
- # files.
-
- @DEFAULT_CMP = ( 1028, 1035, 1036, );
-
-
- $RPM_FILE_MAGIC = chr(0xed).chr(0xab).chr(0xee).chr(0xdb);
- $RPM_HEADER_MAGIC = chr(0x8e).chr(0xad).chr(0xe8);
-
- # we want the second header block, as the first header is the
- # signature block.
-
- $HEADER_BLOCK_NUM = 2;
-
- # number of bytes in the file to skip when looking for the first
- # header. Actually I think the lead is bigger then this like 96, but
- # I am sure this minimum value is correct.
-
- $LEAD_LENGTH = 66;
-
- $HEADER_RECORD_SIZE = 16;
-
- # largest exit code we allow.
-
- $MAX_EXIT = 250;
-
- $NUM_DIFFERENCES = 0;
-
- $RCS_REVISION = ' $Revision: 1.8 $ ';
-
- # set a known path.
-
- $ENV{'PATH'}= (
- '/opt/gnu/bin'.
- ':/usr/local/bin'.
- ':/usr/bin'.
- ':/bin'.
- '');
-
- # taint perl requires we clean up these bad environmental variables.
-
- delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
-
-
- $VERSION = 'NONE';
- if ( $RCS_REVISION =~ m/([.0-9]+)/ ) {
- $VERSION = $1;
- }
-
- return ;
-}
-
-
-sub parse_args{
-
- my $arg_include = '';
- my $arg_exclude = '';
- my %arg_tags= ();
-
- my @args_with_bang = ();
- my %arg2tag = ();
-
- # find out what arguments are availible and build some
- # data structures to work with them.
-
- foreach $tag (keys %$RPMTAG ) {
- my $arg = $RPMTAG->{$tag}->{'arg'};
- ($arg) || next;
- push @ARGS, $arg;
- push @ALL_CMP_TAGS, $tag;
- push @args_with_bang, "$arg!";
- $arg2tag{$arg}=$tag;
- }
-
- # sort the tags to determine the proper comparison order.
- # use the order stored in the RPMTAG table.
- # If this code is too confusing, look up
- # 'Schwartzian Transform' in perlfaq4 or an advanced perl book.
-
- @ALL_CMP_TAGS = map { $_->[0] }
- sort{ $a->[1] <=> $b->[1] }
- map { [ $_, $RPMTAG->{$_}->{'diff_order'} ] }
- @ALL_CMP_TAGS;
-
- $FILES_EQ_STRING = '.' x scalar(@ALL_CMP_TAGS);
-
- if( !GetOptions("version", "help", "all", "cmpmode!", @args_with_bang) ) {
- print("Illegal options in \@ARGV: '@ARGV'\n");
- usage() ;
- exit 1 ;
- }
-
- if($opt_version) {
- print "$0: Version: $VERSION\n";
- exit 0;
- }
-
- if ($opt_help) {
- usage();
- }
-
- if ($opt_all) {
- # all is just an exclude with nothing to exclude
- $arg_exclude = 1;
- }
-
- # process each of the arguments derived from the $RPMTAG hash
-
- foreach $arg (@ARGS) {
- my $arg_var = "opt_$arg";
- if (defined($$arg_var)) {
- $arg_tags{$arg2tag{$arg}} = 1;
- if ($$arg_var) {
- $arg_include = 1;
- } else {
- $arg_exclude = 1;
- }
- }
- }
-
- ($arg_include) && ($arg_exclude) &&
- die("$0: Can not mix both include and exclude arguements ".
- "on the command line.\n");
-
- if ($arg_include) {
- # check only the options listed
- foreach $tag (keys %arg_tags) {
- $CMP_TAGS{$tag} = 1;
- }
- } elsif ($arg_exclude) {
- # check everything but the options listed
- foreach $tag (@ALL_CMP_TAGS) {
- $CMP_TAGS{$tag} = 1;
- }
- foreach $tag (keys %arg_tags) {
- delete $CMP_TAGS{$tag};
- }
- } else {
- # check the default options
- foreach $tag (@DEFAULT_CMP) {
- $CMP_TAGS{$tag} = 1;
- }
- }
-
- ($#ARGV == 1) ||
- die("$0: Argument list must include two file names\n");
-
- $RPMFILE0 = $ARGV[0];
- $RPMFILE1 = $ARGV[1];
-
- ( !(-f $RPMFILE0) || !(-r $RPMFILE0) ) &&
- die("$0: '$RPMFILE0' is not a readable file\n");
-
- ( !(-f $RPMFILE1) || !(-r $RPMFILE1) ) &&
- die("$0: '$RPMFILE1' is not a readable file\n");
-
- $CMP_MODE = ($opt_cmpmode == 1);
-
- return ;
-}
-
-# read the rpmfile and extract the header information.
-
-sub parse_rpm_headers {
- my ($filename) = @_;
-
- my $file = '';
- my $out = {};
-
- # read whole file into memory
- {
- open (RPMFILE, "<$filename")||
- die("$0: Could not open: $filename for reading. $!\n");
-
- # not needed on unix but lets be very clear
- binmode (RPMFILE);
-
- # slurp whole file
- my $old_irs = $/;
- undef $/;
-
- $file = <RPMFILE>;
-
- $/ = $old_irs;
-
- close(RPMFILE)||
- die("$0: Could not close: $filename. $!\n");
-
- $file =~ m/^$RPM_FILE_MAGIC/ ||
- die("$0: file: $filename is not an RPM file. ".
- "No magic number found.\n");
- }
-
- # we want the second header block, as the first header is the
- # signature block.
-
- my ($header_start, $store_start) = ($LEAD_LENGTH,0);
- my ($_version, $_reserved, $num_header_entries, $num_store_bytes) = ();
-
- foreach $i (1 .. $HEADER_BLOCK_NUM) {
-
- # find beginning of header,
- $header_start = index($file, $RPM_HEADER_MAGIC, $header_start);
- ($header_start < 0) &&
- die("$0: file: $filename is not an RPM file. ".
- "No: $i, header found.\n");
-
- $header_start += length($RPM_HEADER_MAGIC);
-
- ($_version, $_reserved, $num_header_entries, $num_store_bytes) =
- unpack("CNNN", substr($file, $header_start, 1+(4*3)));
- $header_start += 1+(4*3);
-
- # find beginning of store
- $store_start = $header_start +
- ($num_header_entries * $HEADER_RECORD_SIZE);
-
- ( ($store_start + $num_store_bytes) < length($file) ) ||
- die("$0: File Parse Error, file: $filename, ".
- "is not long enough to hold store.\n");
- }
-
- # the header is just a list of information about data.
- # the data is stored in the store futher down the file.
- my $header_position = $header_start;
- foreach $i (0 .. $num_header_entries-1) {
-
- my ($tag, $data_type, $offset, $data_count) =
- unpack("N4", substr($file, $header_position, $HEADER_RECORD_SIZE));
- $header_position += $HEADER_RECORD_SIZE;
-
- (
- ( ($tag < 60) || ($tag > 1200) ) ||
- ( ($data_type < 0) || ($data_type > 10) ) ||
- ($offset < 0)
- ) && die("$0: Error parsing header in rpm file: $filename, ".
- "record number: $i.\n");
-
- # we are only interested in the tags which are defined
- $RPMTAG->{$tag} || next;
-
- foreach $j (0 .. $data_count-1) {
- my $value ='';
- if (0) {
- # dummy for aliging the code like a case statement
- } elsif ($data_type == 0) {
- # null
- $value = '';
- } elsif ($data_type == 1) {
- # char
- $value = substr($file, $store_start+$offset, 1);
- $offset += 1;
- } elsif ($data_type == 2) {
- # int8
- $value = ord(substr($file, $store_start+$offset, 1));
- $offset += 1;
- } elsif ($data_type == 3) {
- # int16
- $value = unpack("n", substr($file, $store_start+$offset, 2));
- $offset += 2;
- } elsif ($data_type == 4) {
- # int32
- $value = unpack("N", substr($file, $store_start+$offset, 4));
- $offset += 4;
- } elsif ($data_type == 5) {
- # int64
- # ---- These aren't supported by RPM (yet) */
- die("$0: int64 type found in rpm file: $filename, ".
- "record number: $i.\n");
- } elsif ($data_type == 6) {
- # string
- my $null_position = index ($file, "\0", $store_start+$offset);
- my $length = $null_position - ($store_start+$offset);
- $value = substr($file, $store_start+$offset, $length);
- $offset += $length;
- } elsif ($data_type == 7) {
- # bin
- # to properly support this I need to move it outside the $j
- # loop. However I do not need it.
- die("$0: Bin type found in rpm file: $filename, ".
- "record number: $i.\n");
- } elsif ($data_type == 8) {
- # string_array
- my $null_position = index ($file, "\0", $store_start+$offset);
- my $length = $null_position - ($store_start+$offset);
- $value = substr($file, $store_start+$offset, $length);
- $offset += $length+1
- } elsif ($data_type == 9) {
- # this is listed as both RPM_I18NSTRING_TYPE and RPM_MAX_TYPE
- # in file ~rpm/lib/header.h but I ignore it
- die("$0: I18NSTRING type found in rpm file: $filename, ".
- "record number: $i.\n");
- }
-
- push @{$out->{$tag}}, $value;
- if ($RPMTAG->{$tag}->{"tag_name"} eq 'OLDFILENAMES') {
- $out->{'name2index'}->{$value} = $j;
- }
- } # foreach $j
-
- } # foreach $i
-
- return $out;
-}
-
-
-# traverse the datastructures to create a text representation of the
-# critical differences between rpmscripts. If we are running in
-# cmpmode and a difference is found exit early.
-
-
-sub format_script_differences {
- my ($rpm0, $rpm1) = @_;
-
- my $out = '';;
- my %seen = ();
-
- foreach $script ( sort (keys %$RPMTAG) ) {
-
- ($RPMTAG->{$script}->{'is_script'}) || next;
-
- ($rpm0->{$script} || $rpm1->{$script}) || next;
-
- my $prefix='';
-
- if ( ($rpm0->{$script}) && (!($rpm1->{$script})) ) {
- $prefix = 'missing ';
- } elsif ( (!($rpm0->{$script})) && ($rpm1->{$script}) ) {
- $prefix = 'added ';
- } else {
- my $diff_str = '';
- foreach $cmp_tag (@ALL_CMP_TAGS) {
- if ( !($CMP_TAGS{$cmp_tag}) ||
- !($RPMTAG->{$cmp_tag}->{'script_cmp'}) ){
- $diff_str .= '.';
- next;
- }
-
- # In the rare case where an tag is defined in one RPM and not
- # in another we charitably assume that the RPMs match on this
- # tag. There is a warning in the stderr anyway.
-
- if (
- ($rpm0->{$cmp_tag}) &&
- ($rpm1->{$cmp_tag}) &&
-
- # use the anonymous comparison function (stored in the
- # table) to compare the two scripts
-
- (&{$RPMTAG->{$cmp_tag}->{'script_cmp'}}
- ($rpm0->{$script}->[0], $rpm1->{$script}->[0]))
- ) {
- $diff_str .= $RPMTAG->{$cmp_tag}->{'diff_char'};
- } else {
- $diff_str .= '.';
- }
-
- } # foreach $tag
- if ($diff_str ne $FILES_EQ_STRING) {
- $prefix = $diff_str;
- }
- }
-
- ($prefix) || next;
-
- if ($CMP_MODE) {
- exit 1;
- }
-
- ($NUM_DIFFERENCES < $MAX_EXIT) &&
- $NUM_DIFFERENCES++;
-
- $out .= "$prefix $RPMTAG->{$script}->{'tag_name'}\n";
-
- } # foreach $filename
-
- return $out;
-}
-
-
-
-# traverse the datastructures to create a text representation of the
-# critical differences between file stored in the pacakge. If we are
-# running in cmpmode and a difference is found exit early.
-
-
-
-sub format_file_differences {
- my ($rpm0, $rpm1) = @_;
-
- my $out = '';;
- my %seen = ();
-
- foreach $filename ( sort (
- (keys %{$rpm0->{'name2index'}}),
- (keys %{$rpm1->{'name2index'}})
- ) ) {
-
- $seen{$filename} && next;
- $seen{$filename} = 1;
- $index0 = $rpm0->{'name2index'}->{$filename};
- $index1 = $rpm1->{'name2index'}->{$filename};
-
- my $prefix='';
-
- if ( ($index0) && (!($index1)) ) {
- $prefix = 'missing ';
- } elsif ( (!($index0)) && ($index1) ) {
- $prefix = 'added ';
- } else {
- my $diff_str = '';
- foreach $cmp_tag (@ALL_CMP_TAGS) {
- if (!($CMP_TAGS{$cmp_tag})){
- $diff_str .= '.';
- next;
- }
-
- # In the rare case where an tag is defined in one RPM and not
- # in another we charitably assume that the RPMs match on this
- # tag. There is a warning in the stderr anyway.
-
- if (
- ($rpm0->{$cmp_tag}->[$index0]) &&
- ($rpm1->{$cmp_tag}->[$index1]) &&
- ($rpm0->{$cmp_tag}->[$index0] ne
- $rpm1->{$cmp_tag}->[$index1])
- ) {
- $diff_str .= $RPMTAG->{$cmp_tag}->{'diff_char'};
- } else {
- $diff_str .= '.';
- }
-
- } # foreach $tag
- if ($diff_str ne $FILES_EQ_STRING) {
- $prefix = $diff_str;
- }
- }
-
- ($prefix) || next;
-
- if ($CMP_MODE) {
- die 1;
- }
-
- ($NUM_DIFFERENCES < $MAX_EXIT) &&
- $NUM_DIFFERENCES++;
-
- # this set of blanks would contain information from the flags, if
- # only I was not so lazy
-
- $out .= "$prefix $filename\n";
-
- } # foreach $filename
-
- return $out;
-}
-
-# warn user of a cmp that was requested can not be carried out due to
-# lack of data in the header of atleast one file.
-
-sub data_missing_warnings {
- my ($rpm0, $rpm1) = @_;
-
- my $out = '';;
-
- foreach $cmp_tag (@ALL_CMP_TAGS) {
- if (!($CMP_TAGS{$cmp_tag})) {
- next;
- }
-
- if ( ($CMP_TAGS{$cmp_tag}) &&
- (!$rpm0->{$cmp_tag})
- ){
- $out .= ("Comparison: '$RPMTAG->{$cmp_tag}->{'arg'}' ".
- "specified, but data is not availible in ".
- "rpm: $RPMFILE0.\n");
- }
- if ( ($CMP_TAGS{$cmp_tag}) &&
- (!$rpm1->{$cmp_tag})
- ){
- $out .= ("Comparison: '$RPMTAG->{$cmp_tag}->{'arg'}' ".
- "specified, but data is not availible in ".
- "rpm: $RPMFILE1.\n");
- }
- }
- return $out;
-}
-
-
-
-
-# -------------- main --------------
-{
- set_static_vars();
- parse_args();
- $RPM0 = parse_rpm_headers($RPMFILE0);
- $RPM1 = parse_rpm_headers($RPMFILE1);
-
- my $warnings = data_missing_warnings($RPM0, $RPM1);
-
- # we must print warnings before running diff as we may exit early.
-
- ($warnings) &&
- warn($warnings);
-
- my $header = "oldpkg $RPMFILE0\n"."newpkg $RPMFILE1\n"."\n\n";
- my $script_diffs = format_script_differences($RPM0, $RPM1);
- my $file_diffs = format_file_differences($RPM0, $RPM1);
-
- ($script_diffs || $file_diffs) &&
- print $header, $script_diffs, $file_diffs;
-
- exit $NUM_DIFFERENCES;
-}
+++ /dev/null
-#!/usr/bin/perl
-
-# a web interface to 'cvs rdiff'. This script makes it easy to query
-# the tags which are created by the build script.
-
-
-use CGI ':standard';
-use File::Basename;
-use File::stat;
-use Data::Dumper;
-
-# the big datastructures are:
-
-# $RPM_FILE_BY_FQN{$fqn} is the full path rpm wich is discribed by the fqn
-
-# keys %SORTED_RECENT_FQN is the set of all package names
-
-# $SORTED_RECENT_FQN{$name} is an ordered list of the most recent
-# versions of this package
-
-# for a short time there are these datastrutures but they are large
-# and expensive to save to disk.
-
-
-# An rpm_package is a hash of:
-# $package{'fqn'}="perl-5.00502-3"
-# $package{'rpm_file'}="$RPMS_DIR/".
-# "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
-# $package{'srpm_file'}="$SRPMS_DIR/".
-# "./perl-5.00502-3.src.rpm"
-# $package{'name'}="perl"
-# $package{'version'}="5.00502"
-# $package{'release'}="3"
-
-# fqn is "fully qualified name"
-
-# while the $pkg structure exists we find the pkg we want by looking
-# it up in this structure. This will hold many more packages then the
-# web page ever knows about.
-# $BY_NAME{$name}{$version}{$release};
-
-
-sub usage {
-
- # If they are asking for help then they are clueless so reset all
- # their parameters for them, in case they are in a bad state.
-
- param(-name=>'Defaults', -value=>'on');
- my $rpmdiff_version = `rpmdiff --version`;
-
- $usage =<<EOF;
-
- $0 version: $VERSION
- $rpmdiff_version
-
-This is a web interface into the rpmdiff command.
-
-The user is requested to enter two different packages to diff after
-any one of the multiple submit buttons is pressed the difference will
-be the next webpage loaded. For convenience each package name is
-listed once (in alphabetical order) and below it is checkbox of the
-most recent $MAX_PICK_LIST versions of this package. Any pick list
-which is not actively picked by the user contains the string '(none)'.
-
-The user should pick one package in the first column (this represents
-the "old package") and one package in the second column (this
-represents the "new package"). When the user wants to run the
-difference any 'submit' button can be pressed. The multiple submit
-buttons are listed only for convenience to reduce hunting for a button
-on the page.
-
-Error reporting is very minimal and if an incorrect number of packages
-is picked then the main page is displayed again. It is suggested that
-the user hit the default button if any problems are encountered using
-the program.
-
-Most users are only interested in differences in the contents of files
-and the contents of soft links. The defaults for the program reflect
-this interest. However sometimes users are also interested in changes
-in permissions or ownership. Alternatively it may happen that a user
-is only interested in the set of files whose size changes and changes
-to files which keep the same size should be ignored. To acomidate all
-possible uses we gave the user great flexibility in determining what
-set of changes are significant. There is a pick list at the top of
-the main screen which displays the current criterion for a difference
-to be displayed. A file which has changes made to properties which
-are not picked will not be considered different and will not be
-displayed. Of special note the options:
-
-help will display the help screen for rpmdiff which contains an
- explanation of how to read the diff format.
-
-all will require that all differences are considered important.
- This is the same as checking all the boxes of differences
-
-version will display the version of rpmdiff that is being used by
- this webpage.
-
-The organization of the pick list page keeps the total number of
-packages hidden from the user. The pick list page takes a long time
-to load because the number of choices is very large. To save time the
-set of package pick lists is not regenerated each time the page is
-loaded. There may have been new packages added to the package
-repository since the page was generated and these packages will not be
-displayed until the page is regenerated again. The page will never be
-more then one day old. If you need to use the latest contents of the
-package repository check the box at the bottom of the page marked
-"Flush Cache" this will increase the loading time of the page but
-ensure the freshness of the data.
-
-EOF
- print pre($usage);
-
- return ;
-}
-
-
-sub set_static_vars {
-
-# This functions sets all the static variables which are often
-# configuration parameters. Since it only sets variables to static
-# quantites it can not fail at run time. Some of these variables are
-# adjusted by parse_args() but asside from that none of these
-# variables are ever written to. All global variables are defined here
-# so we have a list of them and a comment of what they are for.
-
-
- $ARCHIVE = "/devel/java_repository";
- $RCS_REVISION = ' $Revision: 1.1 $ ';
-
- @ORIG_ARGV= @ARGV;
-
- # The pattern for parsing fqn into ($name, $version, $release).
- # This is difficult to parse since some hyphens are significant and
- # others are not, some packages have alphabetic characters in the
- # version number.
-
- $PACKAGE_PAT ='(.*)-([^-]+)-([^-]+).solaris2.6-\w*.rpm';
-
- # packages which will end up in the picklists match this pattern
-
- $PICKLIST_PAT = '/((htdocs)|(djava)|(devel))';
-
- # only show the most recent packages
-
- $MAX_PICK_LIST = 20;
-
- # the list of allowable arguments to rpmdiff
-
- @RPMDIFF_ARGS= qw(
- version help all
- size mode md5 dev link user group mtime
- );
-
- @RPMDIFF_ARGS_DEFAULT = qw(size md5 link);
-
- # the list of directories where rpms are stored
- @RPM_ARCHIVES = ('/net/master-mm/export/rpms/redhat',);
-
- $CACHE_DIR = "/tmp/webtools";
-
- # In an effort to make the cache update atomic we write to one file
- # name and only move it into the gobally known name when the whole
- # file is ready.
-
- $TMP_CACHE_FILE= "$CACHE_DIR/rpmfiles.cache.$UID";
- $CACHE_FILE= "$CACHE_DIR/rpmfiles.cache";
-
- # set a known path.
-
- # the correct path has not been finalized yet, but this is close.
-
- $ENV{'PATH'}= (
- '/usr/local/bin'.
- ':/usr/bin'.
- ':/bin'.
- ':/usr/apache/cgibins/cgi-forms'.
- ':/tmp'.
- '');
-
- # taint perl requires we clean up these bad environmental
- # variables.
-
- delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
-
- return 1;
-} #set_static_vars
-
-
-
-
-sub get_env {
-
-# this function sets variables similar to set_static variables. This
-# function may fail only if the OS is in a very strange state. after
-# we leave this function we should be all set up to give good error
-# handling, should things fail.
-
- $| = 1;
- $PID = $$;
- $PROGRAM = basename($0);
- $TIME = time();
- $LOCALTIME = localtime($main::TIME);
- $START_TIME = $TIME;
-
- {
- my ($sec,$min,$hour,$mday,$mon,
- $year,$wday,$yday,$isdst) =
- localtime(time());
-
- # convert confusing perl time vars to what users expect
-
- $year += 1900;
- $mon++;
-
- $CVS_DATE_STR = sprintf("%02u/%02u/%02u", $mday, $mon, $year, );
- $TAG_DATE_STR = sprintf("%02u%02u%02u", $year, $mon, $mday, );
- $TIME_STR = sprintf("%02u%02u", $hour, $min);
- }
- # a unique id for cache file generation
- $UID = "$TAG_DATE_STR.$TIME_STR.$PID";
- $VERSION = 'NONE';
- if ( $RCS_REVISION =~ m/([.0-9]+)/ ) {
- $VERSION = $1;
- }
-
- (-d $CACHE_DIR) ||
- mkdir($CACHE_DIR, 0664) ||
- die("Could not mkdir: $CACHE_DIR: $!\n");
-
- return 1;
-} # get_env
-
-
-
-sub parse_fqn {
-
- # This is difficult to parse since some hyphens are significant and
- # others are not, some packages have alphabetic characters in the
- # version number.
-
- # Also remember that the format of the file is dependent on how RPM
- # is configured so this may not be portable to all RPM users.
-
- (!("@_" =~ m/^$PACKAGE_PAT$/)) &&
- die("rpm_package_name: '$@_' is not in a valid format");
-
- return ($1, $2, $3);
-}
-
-
-sub new_rpm_package {
-
-# An rpm_package is a hash of:
-# $package{'fqn'}="perl-5.00502-3"
-# $package{'rpm_file'}="$RPMS_DIR/".
-# "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
-# $package{'srpm_file'}="$SRPMS_DIR/".
-# "./perl-5.00502-3.src.rpm"
-# $package{'name'}="perl"
-# $package{'version'}="5.00502"
-# $package{'release'}="3"
-
- my ($rpm_file) = @_;
- my $error = '';
- my($name, $version, $release) = main::parse_fqn(basename($rpm_file));
-
- my ($package) = ();
-
- $package->{'fqn'}="$name-$version-$release";
- $package->{'name'}=$name;
- $package->{'version'}=$version;
- $package->{'release'}=$release;
- $package->{'rpm_file'}=$rpm_file;
-
- # these are needed to do proper sorting of major/minor numbers in
- # the version of the package
-
- $package->{'version_cmp'}=[split(/\./, $version)];
- $package->{'release_cmp'}=[split(/\./, $release)];
-
- return $package;
-}
-
-
-sub get_recent_fqn {
- my ($name) =(@_);
-
- my @out = ();
-
- foreach $version ( keys %{ $BY_NAME{$name} }) {
- foreach $release ( keys %{ $BY_NAME{$name}{$version} }) {
-
- push @out, $BY_NAME{$name}{$version}{$release};
-
- }
- }
-
- # the $BY_NAME datastructure is fairly good but the list can not be
- # sorted right. Sort again using the Schwartzian Transform as
- # discribed in perlfaq4
-
- my @sorted = sort {
-
- # compare the versions but make no assumptions
- # about how many elements there are
-
- my $i=0;
- my @a_version = @{ $a->{'version_cmp'} };
- my @b_version = @{ $b->{'version_cmp'} };
- while (
- ($#a_version > $i) &&
- ($#b_version > $i) &&
- ($a_version[$i] == $b_version[$i])
- ) {
- $i++;
- }
-
- my $j = 0;
- my @a_release = @{ $a->{'release_cmp'} };
- my @b_release = @{ $b->{'release_cmp'} };
- while (
- ($#a_release > $j) &&
- ($#b_release > $j) &&
- ($a_release[$j] == $b_release[$j])
- ) {
- $j++;
- }
-
- return (
- ($b_version[$i] <=> $a_version[$i])
- ||
- ($b_release[$j] <=> $a_release[$j])
- );
- }
- @out;
-
- ($#sorted > $MAX_PICK_LIST) &&
- (@sorted = @sorted[0 .. $MAX_PICK_LIST]);
-
- # dumping data to disk is expensive so we only save the data we
- # need. Limit RPM_FILE_BY_FQN to only those packages which appear
- # in the picklist and this explains why we do not store the whole
- # pkg in a BY_FQN hash.
-
- foreach $pkg (@sorted) {
- $RPM_FILE_BY_FQN{$pkg->{'fqn'}}=$pkg->{'rpm_file'}
- }
-
- my @fqns = map { $_->{'fqn'} } @sorted;
-
- return @fqns;
-}
-
-
-
-sub parse_package_names {
-
- $flush_cache = param("Flush Cache");
- if ( (!($flush_cache)) && (-e $CACHE_FILE) && ( -M $CACHE_FILE < 1 ) ) {
- my $st = stat($CACHE_FILE) ||
- die ("Could not stat: $CACHE_FILE: $!");
- $CACHE_LOCALTIME=localtime($st->mtime);
- require $CACHE_FILE;
- return ;
- }
-
- $CACHE_LOCALTIME=$LOCALTIME;
-
- foreach $archive (@RPM_ARCHIVES) {
-
- open(FILES, "-|") ||
- exec("find", $archive, "-print") ||
- die("Could not run find. $!\n");
-
- while ($filename = <FILES>) {
-
- # we want only the binary rpm files of interest
-
- ($filename =~ m/\.rpm$/) || next;
- ($filename =~ m/\.src\.rpm$/) && next;
- ($filename =~ m/$PICKLIST_PAT/) || next;
- chomp $filename;
-
- $pkg = new_rpm_package($filename);
- $BY_NAME{$pkg->{'name'}}{$pkg->{'version'}}{$pkg->{'release'}} = $pkg;
-
- }
-
- close(FILES) ||
- die("Could not close find. $!\n");
-
- }
-
- foreach $group (keys %BY_NAME) {
- $SORTED_RECENT_FQN{$group} = [get_recent_fqn($group)];
-
- }
-
- open(FILE, ">$TMP_CACHE_FILE") ||
- die("Could not open filename: '$TMP_CACHE_FILE': $!\n");
-
- print FILE "# cache file created by $0\n";
- print FILE "# at $LOCALTIME\n\n";
-
- print FILE Data::Dumper->Dump( [\%RPM_FILE_BY_FQN, \%SORTED_RECENT_FQN],
- ["SAVED_FQN", "SAVED_SORTED",], );
-
- print FILE "\n\n";
- print FILE '%RPM_FILE_BY_FQN = %{ $SAVED_FQN };'."\n";
- print FILE '%SORTED_RECENT_FQN = %{ $SAVED_SORTED };'."\n";
- print FILE "1;\n";
-
- close(FILE) ||
- die("Could not close filename: '$TMP_CACHE_FILE': $!\n");
-
- # In an effort to make the cache update atomic we write to one file
- # name and only move it into the gobally known name when the whole
- # file is ready.
-
- (!(-e $CACHE_FILE)) ||
- unlink($CACHE_FILE) ||
- die("Could not unlink $CACHE_FILE: $!\n");
-
- rename($TMP_CACHE_FILE, $CACHE_FILE) ||
- die("Could not rename ($TMP_CACHE_FILE, $CACHE_FILE): $!\n");
-
- return ;
-}
-
-
-
-
-
-sub print_pkg_picklists {
-
- print start_form;
- # create a set of picklists for the packages based on the package names.
-
- print h3("Choose the criterion for a difference"),
- checkbox_group(
- -name=>"rpmdiff arguments",
- -value=>[ @RPMDIFF_ARGS ],
- -default=>[ @RPMDIFF_ARGS_DEFAULT ],
- ),p();
-
- print h3("Choose one package in each column then hit any submit"),p();
-
- my @rows = ();
-
- foreach $name (sort keys %SORTED_RECENT_FQN) {
-
- push @rows,
- # column A
- td(
- strong("$name "),
- p(),
- popup_menu(
- -name=>"old$name",
- -value=>[
- '(none)',
- @{ $SORTED_RECENT_FQN{$name} },
- ],
- -default=>'(none)',
- ),
- ).
- # column B
- td(
- strong("$name "),
- p(),
- popup_menu(
- -name=>"new$name",
- -value=>[
- '(none)',
- @{ $SORTED_RECENT_FQN{$name} },
- ],
- -default=>'(none)',
- ),
- ).
- td(
- defaults(-name=>'Defaults'),
- submit(-name=>'Submit'),
- ).
- '';
- }
-
- print table(Tr(\@rows));
-
- my $footer_info=<<EOF;
-
-Try 'rpmdiff --help' for information about what constitues a
-difference. The output of rpmdiff is exactly the same as the output
-of rpm verify, 'rpm -V'. The --help option documents the format of
-rpm verify and the format of rpmdiff and is a handy reference for this
-terse table. rpmdiff is included in the devel-build-tools package.
-
-
-This web interface is for taking differences in the binary code. To
-take differences of the binaries use <a href="cvs_tag_diff.cgi">'cvs tag diff'</a>.
-
-EOF
-
- print pre($footer_info);
-
- print "This page generated with data cached at: $CACHE_LOCALTIME\n",p(),
- "The time is now: $LOCALTIME\n",p(),
- submit(-name=>"Flush Cache"),p(),
- submit(-name=>"Help Screen"),p();
-
- print end_form;
-
- return ;
-}
-
-
-
-sub print_diff {
- my($oldpkg_file, $newpkg_file, @args) = @_;
-
- my $cmd = "rpmdiff @args $oldpkg_file $newpkg_file 2>&1";
-
- my $result = "\n".qx{$cmd}."\n";
- print pre($result);
-
- return ;
-}
-
-
-# Main
-{
-
- set_static_vars();
- get_env();
-
- parse_package_names();
-
- my @picked_rpmdiff_args = param("rpmdiff arguments");
- @picked_rpmdiff_args = split(/\s+/,
- '--'.(join(" --", @picked_rpmdiff_args)));
- push @picked_rpmdiff_args, '--';
-
- foreach $name (sort keys %SORTED_RECENT_FQN) {
-
- if ( (param("old$name")) && (param("old$name") ne "(none)") ) {
- push @picked_oldpkg, param("old$name");
- }
-
- if ( (param("new$name")) && (param("new$name") ne "(none)") ) {
- push @picked_newpkg, param("new$name");
- }
-
- }
-
- print (header.
- start_html(-title=>'rpmdiff'),
- h2("rpmdiff"));
-
- if (param("Help Screen")) {
-
- usage();
-
- } elsif ( grep {/^(\-\-)((help)|(version))$/} @picked_rpmdiff_args ) {
-
- print_diff(
- '/dev/null',
- '/dev/null',
- @picked_rpmdiff_args,
- );
-
- } elsif (
- ($#picked_oldpkg == 0) &&
- ($#picked_newpkg == 0)
- ) {
-
- print_diff(
- $RPM_FILE_BY_FQN{$picked_oldpkg[0]},
- $RPM_FILE_BY_FQN{$picked_newpkg[0]},
- @picked_rpmdiff_args,
- );
-
- } else {
-
- print_pkg_picklists();
-
- print end_html;
- print "\n\n\n";
- }
-
-}
-