#!/usr/bin/perl #- Synchronize mulitple RPMS/SRPMS directories. #- Copyright (C) 1999 MandrakeSoft (fpons@mandrakesoft.com) #- #- 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, or (at your option) #- 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. #- #- You should have received a copy of the GNU General Public License #- along with this program; if not, write to the Free Software #- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. use strict qw(subs vars refs); #- get basename for a file. sub basename { $_[0] =~ /([^\/]*)$/ ? $1 : $_[0]; } #- compare a version string. sub version_compare { my ($a, $b) = @_; local $_; while ($a && $b) { my ($sb, $sa) = map { $1 if $a =~ /^\W*\d/ ? s/^\W*0*(\d+)// : s/^\W*(\D+)// } ($b, $a); $_ = length($sa) cmp length($sb) || $sa cmp $sb and return $_; } } #- system functions. sub cp { my $pid; if ($pid = fork()) { waitpid($pid, 0); } else { exec '/bin/cp', @_; } } sub rm { my $pid; if ($pid = fork()) { waitpid($pid, 0); } else { exec '/bin/rm', '-f', @_; } } #- get a hash on name of srpms/rpms in a directory. sub get_rpms { my ($dir, $rpms, $flag) = @_; opendir D, $dir or die "cannot open directory $dir\n"; map { if (/([^\/]*?)-([^-]*)-([^-]*)\.([^-]*)(?:\.src)?\.rpm$/) { if ($rpms->{$1}) { if (version_compare($rpms->{$1}{version}, $2) > 0 || version_compare($rpms->{$1}{version}, $2) == 0 && version_compare($rpms->{$1}{release}, $3) > 0) { if ($flag->{clean}) { print "removing obseleted $rpms->{$1}{file} by $_ in $rpms->{$1}{dir}\n" if $flag->{verbose}; rm("$rpms->{$1}{dir}/$rpms->{$1}{file}"); } $rpms->{$1} = { name => $1, version => $2, release => $3, arch => $4, dir => $dir, file => $_, }; } else { if ($flag->{clean}) { print "removing older or equal $_ by $rpms->{$1}{file} in $dir\n" if $flag->{verbose}; rm("$dir/$_"); } } } else { $rpms->{$1} = { name => $1, version => $2, release => $3, arch => $4, dir => $dir, file => $_, }; } } else { print STDERR "unable to parse filename $_\n"; } } grep { /\.rpm$/ } readdir D; closedir D; } #- sync two hashes of rpms, update rpms and printer newer version that are not taken into account. sub sync_rpms { my ($source, $target, $flag) = @_; #- search in source part. foreach (keys %$source) { unless ($target->{$_}) { if ($flag->{verbose}) { print "adding $source->{$_}{file}" . (-d $flag->{add} ? " to $flag->{add}\n" : " is neccessary!\n"); } if (-d $flag->{add}) { cp("$source->{$_}{dir}/$source->{$_}{file}", $flag->{add}); } } } #- search in both part. foreach (keys %$source) { if ($target->{$_}) { if (version_compare($source->{$_}{version}, $target->{$_}{version}) > 0 || version_compare($source->{$_}{version}, $target->{$_}{version}) == 0 && version_compare($source->{$_}{release}, $target->{$_}{release}) > 0) { if ($flag->{verbose}) { print "updating $target->{$_}{dir}/$target->{$_}{file} with newer version $source->{$_}{file}\n"; } if ($flag->{update}) { cp("$source->{$_}{dir}/$source->{$_}{file}", $target->{$_}{dir}); unless (-e "$target->{$_}{dir}/$source->{$_}{file}") { die "unable to copy $source->{$_}{file} from $source->{$_}{dir} into $target->{$_}{dir}\n"; } rm("$target->{$_}{dir}/$target->{$_}{file}"); } } elsif (version_compare($source->{$_}{version}, $target->{$_}{version}) != 0 || version_compare($source->{$_}{release}, $target->{$_}{release}) != 0) { if ($flag->{verbose}) { print STDERR "keeping more up-to-date version $target->{$_}{dir}/$source->{$_}{file} against $source->{$_}{dir}/$source->{$_}{file}, check your repository !\n"; } } #- say nothing if source is equal to target. } } #- search in target part. foreach (keys %$target) { unless ($source->{$_}) { if ($flag->{verbose}) { print "removing $target->{$_}{file}" . ($flag->{remove} ? " from $target->{$_}{dir}\n" : " is neccessary!\n") } if ($flag->{remove}) { rm("$target->{$_}{dir}/$target->{$_}{file}"); } } } } #- main program. sub main { my @from_rpms; my @to_rpms; my $target_rpms; my %flag; my %source; my %target; foreach (@_) { if (/^--(\w*)$/) { if ($1 eq 'verbose' || $1 eq 'update' || $1 eq 'remove' || $1 eq 'clean') { $flag{$1} = 1; } elsif ($1 eq 'add') { $flag{add} = undef; } elsif ($1 eq 'from') { $target_rpms = \@from_rpms; } elsif ($1 eq 'to') { $target_rpms = \@to_rpms; } else { die "unknown option: $1\n"; } } else { if (exists $flag{add} && ! $flag{add}) { $flag{add} = $_; die "cannot add to non-directory: $_\n" unless -d $flag{add}; } else { die "unknown parameter: $_\n" unless $target_rpms; push @$target_rpms, $_; } } } die "usage: syncrpms [--update] [--remove] [--clean] [--add <dir>] --from <dir_sources> --to <dir_target>\n" unless scalar(@from_rpms) > 0 && scalar(@to_rpms) > 0; #- parse directory structures. get_rpms($_, \%source, \%flag) foreach @from_rpms; get_rpms($_, \%target, \%flag) foreach @to_rpms; print STDERR "reading " . scalar(keys %source) . " packages as source rpms from\n"; print STDERR " $_\n" foreach @from_rpms; print STDERR "reading " . scalar(keys %target) . " packages as target rpms from\n"; print STDERR " $_\n" foreach @to_rpms; sync_rpms(\%source, \%target, \%flag); } main(@ARGV);