File rpm-rembrand.pl of Package rembrand

#! /usr/bin/perl -w
#
# rpm-rembrand.pl -- Remove Branding. 
#
# (C) 2006 jw@suse.de
# Distribute under GPL 2.0
#
# A tool to find and remove openSUSE and Novell marks 
# from an opensuse distribution.
#
# Use of this tool shall be limited to the extent allowed and/or
# required by the openSUSE End User License Agreement.
#
# One tool to rule them all, one tool to find them, one tool
# to change them all and in the tempdir bind them.
#
# We run rpm packages (passed as parameters on the command line) against
# a builtin list of images, and check where we have to replace something.
# replacements are taken from the directory indicated by the environment variable
# $REMBRAND_INPUT_DIR or - if missing - are created on the fly as blank white images
# of the same size.
# rpm packages are modified inplace.
# A list of substituted files and their original md5sums are written into the file 
# named by $REMBRAND_ORIG_LIST or (orig_list.txt if missing.)
# This list can later be used to verify that a product was indeed debranded.
#
# rembrand.pl 
#
# 2006-11-19, V0.1,  jw -- unpacking, checking, replacing done. 
#                          cannot re-pack RPMs yet.
# 2006-11-21, V0.2,  jw -- added option parser and usage.
# 2006-11-22, V0.3,  jw -- added loop around rpminjectfile. Ready for testing.
# 2006-11-24, V0.4,  jw -- added more icons. optional flag added. 
#                          emtpy.svg, empty.eps, empty.mng added as defaults. 
#                          svg, svgz, bmp, gif, mng, eps not yet supported. 
# 2006-11-30, V1.0,  jw -- print a usage with suggested package list when 
#                          called without parameters. Small bugfixes.
#                          svg, svgz, bmp, gif, mng, eps added.
# 2006-11-30, V1.1,  jw -- eps support added.
# 2006-12-01, V1.1a, jw -- creating missing directories.
# 2006-12-01, V1.1b, jw -- added missing mng files in kdebase3
# 2006-12-04, V1.1c, jw -- added option -a, added titlebar_decor.png
#                          Picks RPMs from a directory, if given a directory 
# 2006-12-04, V1.1d, jw -- added some gnome splash and wallpapers.
#                          Typos in matching opensuse-updater kdebas3-SuSE.
# 2006-12-05, V1.2,  jw -- Some white PNG's were falsly flagged optional.
#                          Option -n added. One level of magic subdirectory
#                          expansion is now supported.
#
# TODO:
#------
# * kicker/ui/kmenubase.ui contains a hexadecimal representation of 'image0', 
#   (found in kdebase3/kickoff.diff).  I believe this is the 'openSUSE' button in the 
#   kickoff menue.
#   This graphics gets compiled into the binary. No chance to postprocess it.
#
# * what about initrd?
#
# * modifying buildrelease is a dangerous default. It break e.g. OOo dependencies.
#   Do we want to do that?
#


use Digest::MD5;
use Data::Dumper;
$Data::Dumper::Terse = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent = 1;

my $version = '1.2';

my $cp = '/bin/cp';
my $rpm = '/bin/rpm';
my $cpio = '/usr/bin/cpio --quiet';
my $file = '/usr/bin/file';
my $gzip = '/usr/bin/gzip -f';
my $djpeg = '/usr/bin/djpeg';
my $cjpeg = '/usr/bin/cjpeg';
my $ppmmake = '/usr/bin/ppmmake';
my $ppmtobmp = '/usr/bin/ppmtobmp 2> /dev/null';
my $ppmtogif = '/usr/bin/ppmtogif 2> /dev/null';
my $pnmtopng = '/usr/bin/pnmtopng --quiet';
my $rpminjectfile = 'rpminjectfile --quiet';	# not yet packaged?
my $defaults_dir = '/usr/share/rembrand/defaults';

my $verbose = 1;
my $input_dir = $ENV{REMBRAND_INPUT_DIR};
my $output_dir = $ENV{REMBRAND_OUTOUT_DIR} || "/tmp/rembrand-$$/%s";
my $md5list_file = $ENV{REMBRAND_ORIG_LIST} || 'orig_list.txt';
my $rpmout_dir   = $ENV{REMBRAND_RPMOUT_DIR} || 'rpm_out';
my $dump_only = 0;
my $inputlist_file;
my $release_pat = "%s.mod";
my $user = $ENV{LOGNAME} || 'unknown';
my $vendor_pat = "$user (originally from %s)";
my $bgcolor = "#777777";	# a medium gray.
my $skip_optional = 1;
my $noexec = 0;

while (defined(my $arg = shift))
  {
    if    ($arg =~ m{^-v})   { $verbose++; }
    elsif ($arg =~ m{^-q})   { $verbose = 0; }
    elsif ($arg =~ m{^--$})  { last; }
    elsif ($arg =~ m{^-i})   { $inputlist_file = shift; }
    elsif ($arg =~ m{^-d})   { $dump_only++; }
    elsif ($arg =~ m{^-a})   { $skip_optional = 0; }
    elsif ($arg =~ m{^-o})   { $md5list_file = shift; }
    elsif ($arg =~ m{^-I})   { $input_dir = shift; }
    elsif ($arg =~ m{^-O})   { $output_dir = shift; }
    elsif ($arg =~ m{^-R})   { $rpmout_dir = shift; }
    elsif ($arg =~ m{^-b})   { $bgcolor = shift; }
    elsif ($arg =~ m{^-n})   { $noexec++; }
    elsif ($arg =~ m{^-rel}) { $release_pat = shift; }
    elsif ($arg =~ m{^-ven}) { $vendor_pat = shift; }
    elsif ($arg =~ m{^-})   { usage("unknown option '$arg'"); }
    else { unshift @ARGV, $arg; last }
  }

$md5list_file = "$rpmout_dir/$md5list_file" unless $md5list_file =~ m{/};

# flags:package:path
# flags o=optional

my @imagelist = qw(
:opensuse-updater:/opt/kde3/share/icons/hicolor/*/apps/opensuse*updater.png
:opensuse-updater:/opt/kde3/share/apps/opensuseupdater/pics/suse_logo.png
o:opensuse-updater:/opt/kde3/share/apps/opensuseupdater/pics/suse_green.png
o:opensuse-updater:/opt/kde3/share/apps/opensuseupdater/pics/suse_yellow.png
o:opensuse-updater:/opt/kde3/share/apps/opensuseupdater/pics/suse_red.png
o:opensuse-updater:/opt/kde3/share/apps/opensuseupdater/pics/opensuseupdater.png

:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/suse.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/susehelpcenter.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/kmenu.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/SuSEmenu.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/SuSEconf.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/SuSElogo1.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/suse_doc.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/suse_link.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/suse_sdb.png
:kdebase3-SuSE:/opt/kde3/share/icons/*/*/apps/gnome-suse.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/bg-head-800x58.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/cr64-novell.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/cr64-suse.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/suse-logo.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/misc48.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/online_update.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/system_update.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/suse_doc.png
:kdebase3-SuSE:/opt/kde3/share/apps/SUSEgreeter/YaST.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/scalable/apps/SuSEmenu.svgz
:kdebase3-SuSE:/opt/kde3/share/apps/ksplash/Themes/ksplashx-suse/1600x1200/spinner.png
:kdebase3-SuSE:/opt/kde3/share/apps/ksplash/Themes/ksplashx-suse/Preview.png
:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/opensuse.png
:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/suse.png
:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/novell.png
:kdebase3-SuSE:/opt/kde3/share/apps/ksplash/Themes/ksplashx-suse/1600x1200/title.png
o:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/Background.jpeg
o:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/screenshot.jpg
o:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/screenshot.png
o:kdebase3-SuSE:/opt/kde3/share/apps/kicker/wallpapers/SuSE.png
o:kdebase3-SuSE:/etc/opt/kde3/share/doc/HTML/en/common/suse.png
o:kdebase3-SuSE:/opt/kde3/share/apps/ksplash/Themes/ksplashx-suse/1600x1200/background.png
o:kdebase3-SuSE:/opt/kde3/share/apps/kthememanager/themes/SUSE_Default/SUSE_Default.preview.png
o:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/suse_portal.png
o:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/suse_tour.png

:kdebase3:/opt/kde3/share/apps/kicker/pics/kmenu_basic.mng
:kdebase3:/opt/kde3/share/apps/kicker/pics/kmenu_flipped.mng
:kdebase3:/opt/kde3/share/apps/kicker/pics/kmenu_vertical.mng

:gnome2-SuSE:/opt/gnome/share/dist/icons/suse-yast.png
:gnome2-SuSE:/opt/gnome/share/dist/icons/susehelpcenter.png
:gnome2-SuSE:/opt/gnome/share/pixmaps/suse-menustripe.png
:gnome2-SuSE:/opt/gnome/share/dist/icons/SuSElogo1.png
:gnome2-SuSE:/opt/gnome/share/dist/icons/SuSEmenu.png
:gnome2-SuSE:/opt/gnome/share/dist/splash/dist-splash.png
:gnome2-SuSE:/opt/gnome/share/dist/wallpapers/dist-Desktop_*.jpg
:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/opensuse.png
:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/suse.png
:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/novell.png
o:gnome2-SuSE:/opt/gnome/share/dist/icons/assis.png
o:gnome2-SuSE:/opt/gnome/share/dist/icons/32x32.png
o:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/Background.jpeg
o:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/screenshot.png

o:gnome-icon-theme:/opt/gnome/share/icons/gnome/*/apps/susehelpcenter.png
o:tango-icon-theme:/opt/gnome/share/icons/Tango/*/apps/susehelpcenter.png

:gnome-desktop:/opt/gnome/share/pixmaps/gnome-suse.png

:compiz:/usr/share/compiz/opensuse.png

:susehelp:/usr/share/susehelp/img/suse_150.png
o:susehelp:/usr/share/susehelp/img/susehelpcenter.png

:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/control-center/title-bar-right.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/wizard/title-bar-right.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/desktops/gnome.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/desktops/kde.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/misc.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/misc2.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/SuSEconf.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/susehelpcenter.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/SuSEmenu.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/bootdisk_create.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-bootfloppy.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/suse_doc.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-desktop-select.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-online_update.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-software.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-sw_single.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-sw_source.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-sysconfig.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-update.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-you_server.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/YaST.png
:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/testpage/logo.eps
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/suse_link.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/suse_portal.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/suse_tour.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-backup.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-bootfloppy.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-cd-creator.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-cd_update.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-checkmedia.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-dirinstall.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-xen-dirinstall.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-instserver.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-release-notes.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-restore.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/icons/*/apps/yast-x11.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/control-center/title-bar-left.png
o:yast2-theme-openSUSE:/usr/share/YaST2/theme/openSUSE/wizard/title-bar-left.png

:bootsplash-theme-SuSE:/etc/bootsplash/themes/SuSE/animations/spinner.mng
:bootsplash-theme-SuSE:/etc/bootsplash/themes/SuSE/images/logo.mng
:bootsplash-theme-SuSE:/etc/bootsplash/themes/SuSE/images/logov.mng
o:bootsplash-theme-SuSE:/etc/bootsplash/themes/SuSE/images/bootsplash-*.jpg
o:bootsplash-theme-SuSE:/etc/bootsplash/themes/SuSE/images/silent-*.jpg

o:desktop-data-SuSE:/usr/share/wallpapers/suse*.jpg

:OpenOffice_org:/usr/lib/ooo-2.0/program/openabout_suse.bmp
:OpenOffice_org:/usr/lib/ooo-2.0/program/openintro_suse.bmp

:gnome-icon-theme:/opt/gnome/share/icons/*/scalable/apps/im-nov.svg
o:gnome-icon-theme:/opt/gnome/share/icons/*/scalable/apps/susehelpcenter.svg
o:gnome-icon-theme:/opt/gnome/share/icons/*/gnome/scalable/places/novell-button.svg

o:sax2-gui:/usr/share/xfine/figures/minisuse.gif
o:sax2-gui:/usr/share/xfine/figures/suse.gif

:kwin-decor-suse2:/opt/kde3/share/apps/kwin/pics/titlebar_decor.png

);

if ($dump_only)
  {
    print join("\n", @imagelist). "\n";
    exit 0;
  }

if ($inputlist_file)
  {
    open IN, "<$inputlist_file" or die "cannot read inputlist_file $inputlist_file: $!\n";
    chomp (@imagelist = <IN>);
    close IN;
  }

my %orig_list;
my %imagelist;
for my $i (@imagelist)
  {
    if ($i =~ m{^([^:]*):([^:]+):\s*(\S+)})
      {
        $orig_list{$2}{$3} .= $1;
      }
  }

for my $p (keys %orig_list)
  {
    for my $i (keys %{$orig_list{$p}})
      {
        push @{$imagelist{$p}}, $i;
	my $flags = $orig_list{$p}{$i};
        $orig_list{$p}{$i} = {missing => 1};
	$orig_list{$p}{$i}{flags} = $flags if $flags;
      }
  }

my $rpm_filter_pat = join '|', map { "\Q$_\E" } sort keys %orig_list;
$rpm_filter_pat = qr{^($rpm_filter_pat)(-\d.*)?\.rpm$};

my %subdir;

## look for directories, replace them by matching rpm files.
for my $i (reverse 0..$#ARGV)
  {
    next unless -d $ARGV[$i];
    opendir DIR, $ARGV[$i] or die "opendir($ARGV[$i]) failed: $!\n";
    my @rpms = grep { /$rpm_filter_pat/ } readdir DIR;
    rewinddir DIR;
    my @subdirs = grep { !/^\./ and -d "$ARGV[$i]/$_" } readdir DIR;
    closedir DIR;

    # Now here comes a trick:
    # we allow for one level of subdirecories before taking the RPM.
    # If that produces any RPMs, we record the single subdirectoy name
    # in %subdir, so that we can maintain such a subdriectory 
    # in $rpm_out directory.
    # 
    for my $d (@subdirs)
      {
        my $dir = "$ARGV[$i]/$d";

        opendir DIR, $dir or die "opendir($dir) failed: $!\n";
        my @sub_rpms = map { "$d/$_" } grep { /$rpm_filter_pat/ } readdir DIR;
        closedir DIR;

	$subdir{"$ARGV[$i]/$_"} = $d for @sub_rpms;
	push @rpms, @sub_rpms;
      }

    splice @ARGV, $i, 1, map { "$ARGV[$i]/$_" } @rpms;
  }

unless (scalar @ARGV)
  {
    my @pkg = sort keys %orig_list;
    usage("No RPM package names or no RPMs found in given directories.\nSuggested packages to process:\n\n\t*/" . join(".rpm */", @pkg) . ".rpm\n\n");
  }

my $new = 0;
for my $rpm_file (@ARGV)
  {
    my $name = rpm_header($rpm_file, 'name');
    if ($imagelist{$name})
      {
        $new += rembrand_rpm($rpm_file, $name, $imagelist{$name}, $input_dir ? (sprintf $input_dir, $name) : undef, $subdir{$rpm_file});
      }
    else
      {
        print "$rpm_file ($name) skipped\n" if $verbose;
      }
  }


## no empty hashes, please.
my $missing;
for my $p (keys %orig_list)
  {
    my $v = $orig_list{$p};
    for my $k (keys %$v)
      {
        $missing->{$p}++ if $v->{$k}{missing} and ($v->{$k}{flags}||'') !~ m{o};
        delete $v->{$k} unless keys %{$v->{$k}};
      }
  }

## okay, record what we did.
mkdir_pf($md5list_file);
open OUT, ">$md5list_file" or die "after all, cannot write $md5list_file: $!";
print OUT Dumper \%orig_list;
close OUT or die "could not write $md5list_file: $!";
printf "%d other RPMs need processing.\n", scalar(keys %$missing) if $verbose and $missing;
print "Original files stored in '$output_dir'\n" if $verbose;
print "$new files created in '$input_dir'\n" if $input_dir and $verbose;
print "$md5list_file written.\n" if $verbose;

exit 0;
#################################################################
sub rembrand_rpm
{
  my ($pkg, $name, $images, $path, $subdir) = @_;
  my @l = rpm_filelist($pkg);

  $name = $pkg unless $name;
  $name =~ s{[/\.]}{_}g;
  my $create_count = 0;

  my @files_o = ();
  my @files = ();
  for my $i (@$images)
    {
      my $re = "^$i\$";
      $re =~ s{\*}{.*}g;	# glob2re
      my @m = grep { /$re/ } @l;
      delete $orig_list{$name}{$i}{missing} if scalar @m;	# delete here, in case it is a glob.
      if (($orig_list{$name}{$i}{flags}||'') =~ m{o})
        {
          push @files_o, @m;
	}
      else
        {
          push @files, @m;
	}
    }

  push @files, @files_o unless $skip_optional;

  unless (scalar @files)
    {
      printf "no matching files in $pkg, expected at least %d\n", scalar @$images 
        unless scalar(@files_o) => scalar(@$images);
      return;
    }

  printf "$pkg: %d files.\n", scalar(@files) if $verbose;
  printf "(%d optional files skipped.)\n", scalar @files_o if $verbose and $skip_optional and scalar @files_o;

  my $root = sprintf "$output_dir", $name;	# where created files go.
  mkdir_pf("$root/.");
  unrpm($pkg, $root, \@files);

  for my $i (@files)
    {
      my $f = "$root/$i";
      if (-f $f)
        {
	  delete $orig_list{$name}{$i}{missing};	# may already be deleted
	  delete $orig_list{$name}{$i}{listed};		# may not yet exist...
          $orig_list{$name}{$i}{orig_size} = -s $f;
          $orig_list{$name}{$i}{orig_md5sum} = file_md5sum($f);
          my $geom = file_imgsize($f);
          unlink $f or die "unlink $f failed: $!";
          $create_count += file_substitute($f, $path, $i, $geom);
        }
      else
        {
	  $orig_list{$name}{$i} = {missing => 1, listed => $pkg } unless $orig_list{$name}{$i}{size};
	  # not packaged here, let us hope it is in some other rpm,
	  # if not, report it as missing although listed in the rpm package.
	}
    }
  
  $subdir = $subdir ? "/$subdir" : '';

  my ($outname) = $pkg =~ m{([^/]+)$};
  print "$pkg -> $rpmout_dir$subdir/$outname\n";
  mkdir_pf("$rpmout_dir$subdir/$outname");
  my $tmp_pkg_out = "$rpmout_dir$subdir/$outname-$$";
  my $tmp_pkg_in = "$pkg";
  my $rel = sprintf $release_pat, ($release_pat =~ m{%}) ? rpm_header($pkg, 'release') : undef;
  my $ven = sprintf $vendor_pat,  ($vendor_pat =~ m{%})  ? rpm_header($pkg, 'vendor') : undef;

  die "Can not use single-quotes (') in release or vendor string.\n" 
    if $rel =~ m{'} or $ven =~ m{'};

  my $opt = '';
  $opt .= "--release '$rel' " if length $rel;
  $opt .= "--vendor '$ven' "  if length $ven;

  for my $i (@files)
    {
      print STDERR "." if $verbose;
      run_cmd("$rpminjectfile $opt '$tmp_pkg_in' '$tmp_pkg_out' '$root/$i' '$i'");
      $tmp_pkg_in = "$tmp_pkg_out-in";
      $opt = '';
      rename "$tmp_pkg_out", "$tmp_pkg_in" or die "rename($tmp_pkg_out) failed: $!\n";
    }
  print STDERR "\n" if $verbose;
  rename "$tmp_pkg_in", "$rpmout_dir/$outname" or die "rename($tmp_pkg_in) failed: $!\n";
  
  return $create_count;
}

sub rpm_header
{
  my ($file, $name) = @_;
  my $cmd = "$rpm -qp --qf '%{$name}' '$file'";
  open RPM, "$cmd|" or die "cannot run $cmd: $!";
  my $r = join '', <RPM>;
  close RPM or die "failed to run $cmd: $!";
  die "$cmd: failed\n" unless length $r;
  return $r;
}

sub rpm_filelist
{
  my ($file, $name) = @_;
  my $cmd = "$rpm -qpl '$file'";
  open RPM, "$cmd|" or die "cannot run $cmd: $!";
  my @r = <RPM>;
  chomp @r;
  close RPM or die "failed to run $cmd: $!";
  die "$cmd: failed\n" unless scalar @r;
  return @r;
}

# extract an RPM file, optionally limited to certain files.
#
sub unrpm
{
  my ($pkg, $dir, $files) = @_;
  my $pattern = '';
  if ($files)
    {
      # we put leading dots in front of the file name list.
      # This is how rpm2cpio exports the names.
      # And we escape '?' and '*' because cpio reads shell globbing 
      # patterns, not files.

      $pattern = " '." . join("' '.", @$files) . "'" if $files;
      $pattern =~ s{([\*\?])}{\\$1}g;
    }
  run_cmd("rpm2cpio '$pkg' | (cd $dir && $cpio -uidm$pattern)");
}

sub file_md5sum
{
  my ($file) = @_;

  my $md5 = Digest::MD5->new;
  open IN, "<$file" or die "cannot read $file:$!";
  $md5->addfile(*IN);
  close IN;
  return $md5->hexdigest;
}

sub file_imgsize
{
  my ($img) = @_;
  my $magic = `$file -b '$img'`;

  if ($magic =~ m{PNG image data,\s+(\d+)\s*x\s*(\d+)})
    {
      return { t => 'png', w => $1, h => $2 };
    }

  if ($magic =~ m{^JPEG image data})
    {
      my $cmd = "$djpeg '$img' | head -2";
      my $r = `$cmd`;
      if ($r =~ m{^P\d\s+(\d+)\s+(\d+)}s)
        { 
          return { t => 'jpeg', w => $1, h => $2 };
	}
    }
  if ($magic =~ m{^PC bitmap data,.*\s(\d+)\s*x\s*(\d+)\s*x\s*(\d+)\s*$})
    {
      return { t => 'bmp', w => $1, h => $2 };
    }
  if ($magic =~ m{^MNG video data,\s+(\d+)\s*x\s*(\d+)})
    {
      return { t => 'mng', w => $1, h => $2 };
    }
  if ($magic =~ m{^GIF image data,.*\s+(\d+)\s*x\s*(\d+)})
    {
      return { t => 'gif', w => $1, h => $2 };
    }
  if ($magic =~ m{^XML} and $img =~ m{\.svg}i)
    {
      return { t => 'svg', w => 1, h => 1 };
    }
  if ($magic =~ m{^PostScript document text .* type EPS$})
    {
      return { t => 'eps', w => 1, h => 1 };
    }
  if ($magic =~ m{^gzip compressed} and $img =~ m{\.svgz}i)
    {
      return { t => 'svgz', w => 1, h => 1 };
    }
  return { t => $magic };
}

sub file_substitute
{
  my ($file, $dir, $name, $img) = @_;

  my $cmd;
  my $substitute = "$dir/$name" if $dir;
  if ($substitute && -f $substitute)
    {
      run_cmd("$cp '$substitute' '$file'");
      return 0;
    }

  print "creating blank $name $img->{w}x$img->{h}\n" if $verbose;
  $cmd = qq{$ppmmake "$bgcolor" $img->{w} $img->{h}};
  if ($img->{t} eq 'jpeg')
    {
      $cmd .= "|$cjpeg > '$file'";
    }
  elsif ($img->{t} eq 'png')
    {
      $cmd .= "|$pnmtopng > '$file'";
    }
  elsif ($img->{t} eq 'gif')
    {
      $cmd .= "|$ppmtogif > '$file'";
    }
  elsif ($img->{t} eq 'bmp')
    {
      $cmd .= "|$ppmtobmp > '$file'";
    }
  elsif ($img->{t} eq 'mng' || $img->{t} eq 'eps' || $img->{t} eq 'svg')
    {
      $cmd = "cp '$defaults_dir/empty.$img->{t}' '$file'";
    }
  elsif ($img->{t} eq 'svgz')
    {
      $cmd = "$gzip -c '$defaults_dir/empty.svg' > '$file'";
    }
  else 
    {
      die "cannot create unknown image type $img->{t},\nplease update file_imgsize()";
    }
  run_cmd($cmd);
  if ($substitute)
    {
      mkdir_pf($substitute);
      run_cmd("$cp '$file' '$substitute'");	# expose the white image
    }
  return 1;
}

sub run_cmd
{
  my ($cmd) = @_;
  print "\n$cmd\n" if $verbose > 1 or $noexec;
  return if $noexec;
  system "$cmd" and die "$cmd failed: $@ $!";
}

##
## this version of mkdir_pf handles relative and absolute paths.
##
sub mkdir_pf
{
  my ($path) = @_;

  my @dirs = split "/", $path;
  pop @dirs;                    # nuke trailing filename
  $path = ($dirs[0] eq '') ? '/' : '';
  for my $d (@dirs)
    { 
      $path .= "$d/";
      mkdir $path, 0777 or die "mkdir $path failed: $!" unless -d $path;
    }
  return 1;     # success, be mkdir compatible
}

## rm_rf -- recursive file tree delete
##
## fn is a predicate, that receives a file path name as parameter.
## If fn returns zero for a file object, it is excluded from removal.
## Directories are removed unconditionally, if we can empty them first. 
##
## Directories are traversed in reverse alphabetical order;
## thus dotfiles usually still exist while fn is called for other files.
## fn defaults to a true value.
## Adds write perm on the dir, if unlink/rmdir fails.
##
## Caution: No taint checks here.
##
## Does not follow symlinks for opendir; this could carry us out of the tree.
## Tries unlink after rmdir, just in case it is a weird directory symlik.
##
## rm_rf fails on very deep directory structures. It should 
## - chdir downward (remembering inode numbers), 
## - clear one level, 
## - cd(..), check if 
##   - inode matches, clear one level 
##   - or, if it does not match redo from start.
## - done.
##
sub rm_rf
{
  my ($path, $fn, $comment) = @_;

  $comment ||='';

  if (!-l $path and opendir DIR, $path)
    {
      my @e = grep { !/^(\.|\.\.)$/ } readdir DIR;
      closedir DIR;
      rm_rf("$path/$_") for reverse sort @e;
      $path = $1 if $path =~ m{^(.*)$};
      print "rmdir $path\n" if $verbose > 2;
      unless (unlink $path or rmdir $path)
        {
          my $dir; $dir = $1 if $path =~ m{^(.*/).};
          $dir = '.' unless defined $dir;
          chmod 0777, $dir;
          return if unlink $path or rmdir $path;

          warn "rm_rf: rmdir($path) failed: $!\n";
        }
    }
  else
    {
      if (!$fn or &$fn($path))
        {
          $path = $1 if $path =~ m{^(.*)$};     # UNTAINT. brute
          print "unlink $path\n" if $verbose > 2;
          unless (unlink $path)
            {
              my $dir; $dir = $1 if $path =~ m{^(.*/).};
              $dir = '.' unless defined $dir;
              chmod 0777, $dir;
              return if unlink $path;

              warn "rm_rf: unlink($path) failed: $!\n";
            }
        }
      else
        {
          print "rm_rf: skip $path, $comment\n" if $verbose > 2;
        }
    }
}


sub usage
{
  my ($msg) = @_;
  $msg .= "\n" if $msg and $msg !~ m{\n$};
  $msg = "Error: " . $msg if $msg;

  $input_dir ||= "<undefined>";


  print qq{
rembrand.pl version $version
$msg
Usage:

$0 [options] *.rpm [directory ...]

Valid options are:
 
 -v              Be more verbose. Default: $verbose
 -q              Be quiet.

 -a              Replace all files. Default: skip those marked as optional.
 -b bgcolor      Color used for blank images. Default: "$bgcolor".
 -i input_list   File with substitution list. Defaults to a built in list.
 -d              Dump built in substitution list and exit.
 -n              Do nothing. Just print the command that would execute.

 -o out_list     Write filenames substituted to listfile. 
                 Default $md5list_file or \$REMBRAND_ORIG_LIST

 -I input_dir    A root directory containing replacement files in their respective 
                 subdirectories. Default $input_dir or \$REMBRAND_INPUT_DIR
		 The name of the current RPM is substituted for '%s'.
 -O output_dir   A root directory containing the original versions of the replaced files.
                 Default $output_dir or \$REMBRAND_OUTPUT_DIR
	 	 The name of the current RPM is substituted for '%s'.
 -R rpmout_dir   Directory, where to write the modified RPM packages.
                 Default $rpmout_dir or \$REMBRAND_RPMOUT_DIR
 -rel release_nr Modify RPM header 'Release'. Default : $release_pat
	 	 The release of the current RPM is substituted for '%s'.
 -ven vendor_str Modify RPM header 'Vendor'. Default : $vendor_pat
	 	 The vendor of the current RPM is substituted for '%s'.
                
Blank files are used for missing files below input_dir. 
These are also placed into input_dir.
};

  exit 0;
}
openSUSE Build Service is sponsored by