#! /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.
#
# 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. 
#                         

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

my $version = '0.4';

my $cp = '/bin/cp';
my $rpm = '/bin/rpm';
my $cpio = '/usr/bin/cpio --quiet';
my $file = '/usr/bin/file';
my $djpeg = '/usr/bin/djpeg';
my $cjpeg = '/usr/bin/cjpeg';
my $ppmmake = '/usr/bin/ppmmake';
my $pnmtopng = '/usr/bin/pnmtopng --quiet';
my $rpminjectfile = 'rpminjectfile --quiet';	# not yet packaged?

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 %s)";
my $bgcolor = "#FFFFFF";

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{^-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{^-rel}) { $release_pat = shift; }
    elsif ($arg =~ m{^-ven}) { $vendor_pat = shift; }
    elsif ($arg =~ m{^-})   { usage("unknown option '$arg'"); }
    else { unshift @ARGV, $arg; last }
  }


# 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/crystalsuse/*/apps/suse.png
:kdebase3-SuSE:/opt/kde3/share/icons/crystalsuse/*/apps/SuSElogo1.png
:kdebase3-SuSE:/opt/kde3/share/icons/crystalsuse/*/apps/susehelpcenter.png
:kdebase3-SuSE:/opt/kde3/share/icons/crystalsuse/*/apps/kmenu.png
:kdebase3-SuSE:/opt/kde3/share/icons/crystalsuse/*/apps/SuSEmenu.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/susehelpcenter.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/SuSEmenu.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/SuSEconf.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/SuSElogo1.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/suse_doc.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/suse_link.png
:kdebase3-SuSE:/opt/kde3/share/icons/hicolor/*/apps/suse_sdb.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/icons/hicolor/*/apps/gnome-suse.png
o:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/opensuse.png
o:kdebase3-SuSE:/opt/kde3/share/apps/kdm/themes/SUSE/suse.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/novell.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/ksplash/Themes/ksplashx-suse/1600x1200/title.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

: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
o:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/Background.jpeg
o:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/opensuse.png
o:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/screenshot.png
o:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/suse.png
o:gnome2-SuSE:/opt/gnome/share/gdm/themes/GDM-SuSE/novell.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
);

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;
      }
  }

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


## no empty hashes, please.
for my $v (values %orig_list)
  {
    for my $k (keys %$v)
      {
        delete $v->{$k} unless keys %{$v->{$k}};
      }
  }

## okay, record what we did.
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: $!";
print "$md5list_file written.\n" if $verbose;

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

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

  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.
      push @files, @m;
    }

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

  printf "$pkg: %d files.\n", scalar(@files) if $verbose;

  my $root = sprintf "$output_dir", $name;
  mkdir $root, 0777;
  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: $!";
          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.
	}
    }
  
  my ($outname) = $pkg =~ m{([^/]+)$};
  print "$pkg -> $rpmout_dir/$outname\n";
  mkdir_pf("$rpmout_dir/$outname");
  my $tmp_pkg_out = "$rpmout_dir/$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)
    {
      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";
    }
  rename "$tmp_pkg_in", "$rpmout_dir/$outname" or die "rename($tmp_pkg_in) failed: $!\n";

}

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 };
	}
    }
  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'");
    }
  else
    {
      print "created blank $name\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'";
	}
      else
        {
	  die "cannot create unknown image type $img->{t}";
	}
      run_cmd($cmd);
      if ($substitute)
        {
	  mkdir_pf($substitute);
          run_cmd("$cp '$file' '$substitute'");	# expose the white image
	}
    }
}

sub run_cmd
{
  my ($cmd) = @_;
  print "$cmd\n" if $verbose > 1;
  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;


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

$0 [options] *.rpm

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

 -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.
 -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;
}
