#!/usr/bin/perl #- Copyright (C) 2000 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. #- this program is based upon urpmi. use strict; use urpm; #- default options. my $query = { use_provides => 1, }; my @files; my @names; my @src_names; sub usage { print STDERR N("urpmq version %s Copyright (C) 2000, 2001, 2002 MandrakeSoft. This is free software and may be redistributed under the terms of the GNU GPL. usage: ", $urpm::VERSION) . N(" --help - print this help message. ") . N(" --update - use only update media. ") . N(" --media - use only the given media, separated by comma. ") . N(" --excludemedia - do not use the given media, separated by comma. ") . N(" --sortmedia - sort media according to substrings separated by comma. ") . N(" --synthesis - use the synthesis given instead of urpmi db. ") . N(" --auto-select - automatically select packages to upgrade the system. ") . N(" --fuzzy - impose fuzzy search (same as -y). ") . N(" --list - list available packages. ") . N(" --list-media - list available media. ") . N(" --list-nodes - list available nodes when using --parallel. ") . N(" --list-aliases - list available parallel aliases. ") . N(" --src - next package is a source package (same as -s). ") . N(" --headers - extract headers for package listed from urpmi db to stdout (root only). ") . N(" --sources - give all source packages before downloading (root only). ") . N(" --force - force invocation even if some packages do not exist. ") . N(" --parallel - distributed urpmi accross machines of alias. ") . N(" --wget - use wget to retrieve distant files. ") . N(" --curl - use curl to retrieve distant files. ") . N(" --proxy - use specified HTTP proxy, the port number is assumed to be 1080 by default (format is ). ") . N(" --proxy-user - specify user and password to use for proxy authentication (format is ). ") . N(" --env - use specific environment (typically a bug report). ") . N(" -v - verbose mode. ") . N(" -d - extend query to package dependencies. ") . N(" -u - remove package if a more recent version is already installed. ") . N(" -c - complete output with package to be removed. ") . N(" -P - do not search in provides to find package. ") . N(" -R - reverse search to what requires package. ") . N(" -y - impose fuzzy search (same as --fuzzy). ") . N(" -s - next package is a source package (same as --src). ") . N(" -i - print usefull information in human readeable form. ") . N(" -g - print groups with name also. ") . N(" -r - print version and release with name also. ") . N(" -f - print version, release and arch with name. ") . "\n" . N(" names or rpm files given on command line are queried. "); exit(0); } #- params contains informations to parse installed system. my $urpm = new urpm; #- parse arguments list. my @nextargv; @ARGV or usage; while (defined($_ = shift @ARGV)) { /^--help$/ and do { usage }; /^--update$/ and do { $query->{update} = 1; next }; /^--media$/ and do { push @nextargv, \$query->{media}; next }; /^--exclude-?media$/ and do { push @nextargv, \$query->{excludemedia}; next }; /^--sort-?media$/ and do { push @nextargv, \$query->{sortmedia}; next }; /^--mediums$/ and do { push @nextargv, \$query->{media}; next }; /^--synthesis$/ and do { push @nextargv, \$query->{synthesis}; next }; /^--auto-select$/ and do { $query->{deps} = $query->{upgrade} = $query->{auto_select} = 1; next }; /^--fuzzy$/ and do { $query->{fuzzy} = 1; next }; /^--list$/ and do { $query->{list} = 1; next }; /^--list-media$/ and do { $query->{list_media} = 1; next }; /^--list-nodes$/ and do { $query->{list_nodes} = 1; next }; /^--list-aliases$/ and do { $query->{list_aliases} = 1; next }; /^--src$/ and do { $query->{src} = 1; next }; /^--headers$/ and do { $query->{headers} = 1; next }; /^--sources$/ and do { $query->{sources} = 1; next }; /^--force$/ and do { $query->{force} = 1; next }; /^--skip$/ and do { push @nextargv, \$query->{skip}; next }; /^--root$/ and do { push @nextargv, \$query->{root}; next }; /^--parallel$/ and do { push @nextargv, \$query->{parallel}; next }; /^--wget$/ and do { $urpm->{sync} = sub { my $options = shift @_; if (ref($options)) { $options->{prefer} = 'wget' } else { $options = { dir => $options, prefer => 'wget' } } urpm::sync_webfetch($options, @_) }; next }; /^--curl$/ and do { $urpm->{sync} = \&urpm::sync_webfetch; next }; /^--proxy$/ and do { my ($proxy, $port) = ($_ = shift @ARGV) =~ m,^(?:http://)?([^:]+(:\d+)?)/*$, or die N("bad proxy declaration on command line\n"); $proxy .= ":1080" unless $port; $urpm->{proxy}{http_proxy} = "http://$proxy"; next; }; /^--proxy-user$/ and do { ($_ = shift @ARGV) =~ /(.+):(.+)/, or die N("bad proxy declaration on command line\n"); $urpm->{proxy}{user} = $1; $urpm->{proxy}{pwd} = $2; next; }; /^--env$/ and do { push @nextargv, \$query->{env}; next }; /^-(.*)$/ and do { foreach (split //, $1) { /[\?h]/ and do { usage; next }; /d/ and do { $query->{deps} = 1; next }; /u/ and do { $query->{upgrade} = 1; next }; /m/ and do { $query->{deps} = $query->{upgrade} = 1; next }; /M/ and do { $query->{deps} = $query->{upgrade} = 1; next }; /c/ and do { $query->{complete} = 1; next }; /g/ and do { $query->{group} = 1; next }; /p/ and do { $query->{use_provides} = 1; next }; /P/ and do { $query->{use_provides} = 0; next }; /R/ and do { $query->{what_requires} = 1; next }; /y/ and do { $query->{fuzzy} = 1; next }; /s/ and do { $query->{src} = 1; next }; /v/ and do { $query->{verbose} = 1; next }; /i/ and do { $query->{info} = 1; next }; /r/ and do { $query->{version} = $query->{release} = 1; next }; /f/ and do { $query->{version} = $query->{release} = $query->{arch} = 1; next }; print STDERR N("urpmq: unknown option \"-%s\", check usage with --help\n", $1); exit(1) } next }; @nextargv and do { my $r = shift @nextargv; $r and $$r = $_; next }; /\.rpm$/ and do { if (-r $_) { push @files, $_ } else { print STDERR N("urpmq: cannot read rpm file \"%s\"\n", $_) } next }; if ($query->{src}) { push @src_names, $_; } else { push @names, $_; } $query->{src} = 0; #- reset switch for next package. } #- remove verbose if not asked. $query->{verbose} or $urpm->{log} = sub {}; #- improve speed if using any list_... options. $query->{nodepslist} = $query->{list_aliases} || $query->{list_nodes} || $query->{list_media}; if ($query->{env}) { print STDERR N("using specific environment on %s\n", $query->{env}); #- setting new environment. $urpm->{config} = "$query->{env}/urpmi.cfg"; $urpm->{skiplist} = "$query->{env}/skip.list"; $urpm->{instlist} = "$query->{env}/inst.list"; $urpm->{statedir} = $query->{env}; } $urpm->configure(nocheck_access => 1, noskipping => $query->{nodepslist}, noinstalling => $query->{nodepslist}, nodepslist => $query->{nodepslist}, media => $query->{media}, excludemedia => $query->{excludemedia}, sortmedia => $query->{sortmedia}, synthesis => $query->{synthesis}, update => $query->{update}, skip => $query->{skip}, root => $query->{root}, parallel => $query->{parallel}, ); my $state = {}; my %requested; if ($query->{list_aliases}) { local ($_, *PARALLEL); open PARALLEL, "/etc/urpmi/parallel.cfg"; while () { chomp; s/#.*$//; s/^\s*//; s/\s*$//; /\s*([^:]*):/ or next; print "$1\n"; } close PARALLEL; } elsif ($query->{list_nodes}) { $query->{parallel} or $urpm->{fatal}(1, N("--list-nodes can only be used with --parallel")); foreach (keys %{$urpm->{parallel_handler}{nodes} || {}}) { print "$_\n"; } exit 0; } elsif ($query->{list_media}) { foreach (@{$urpm->{media}}) { print "$_->{name}\n"; } exit 0; } elsif ($query->{list}) { #- if list has been activated, select all... @{$state->{selected}}{0 .. $#{$urpm->{depslist}}} = (); } else { %requested = $urpm->register_rpms(@files); #- search the packages according the selection given by the user, #- basesystem is added to the list so if it need to be upgraded, all its dependency #- will be updated too. if (@names) { $urpm->search_packages(\%requested, [ @names ], use_provides => $query->{use_provides}, fuzzy => $query->{fuzzy}) or $query->{force} or exit 1; } if (@src_names) { $urpm->search_packages(\%requested, [ @src_names ], use_provides => $query->{use_provides}, fuzzy => $query->{fuzzy}, src => 1) or $query->{force} or exit 1; } #- keep track of choices, do no propagate but mark them in selected. my $stop_on_choices = sub { my ($urpm, $db, $state, $choices) = @_; $state->{selected}{join '|', sort { $a <=> $b } map { $_ ? $_->id : () } @$choices} = 0; }; #- open/close of database should be moved here, in order to allow testing #- some bogus case and check for integrity. (note auto_select implies upgrade). if ($query->{what_requires}) { #- search for packages that requires on of the package proposed. my (@properties, %requires, %properties, $dep); #- keep in mind the requested id (if given) in order to prefer these packages #- on choices instead of anything other one. @properties = keys %requested; if (@properties) { require URPM::Resolve; #- build a requires to packages id hash. foreach my $pkg (@{$urpm->{depslist}}) { foreach ($pkg->requires_nosense) { $requires{$_}{$pkg->id} = undef; } } #- for each dep property evaluated, examine which package will be obsoleted on $db, #- then examine provides that will be removed (which need to be satisfied by another #- package present or by a new package to upgrade), then requires not satisfied and #- finally conflicts that will force a new upgrade or a remove. while (defined ($dep = shift @properties)) { #- take the best package for each choices of same name. my $packages = $urpm->find_candidate_packages($dep); foreach (values %$packages) { my ($best_requested, $best); foreach (@$_) { if ($best_requested || exists $requested{$_->id}) { if ($best_requested && $best_requested != $_) { $_->compare_pkg($best_requested) > 0 and $best_requested = $_; } else { $best_requested = $_; } } elsif ($best && $best != $_) { $_->compare_pkg($best) > 0 and $best = $_; } else { $best = $_; } } #- examine all choice proposed. my $pkg = $best_requested || $best or next; exists $state->{selected}{$pkg->id} and next; $state->{selected}{$pkg->id} = undef; #- for all provides of package, look of what is requiring them. foreach ($pkg->provides) { if (my ($n, $s) = /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/) { foreach (map { $urpm->{depslist}[$_] } grep { ! exists $state->{selected}{$_} && ! exists $properties{$_} } keys %{$requires{$n} || {}}) { if (grep { URPM::ranges_overlap("$n $s", $_) } $_->requires) { push @properties, $_->id; $properties{$_->id} = undef; } } } } } } } } elsif ($query->{auto_select} || $query->{upgrade}) { $urpm->resolve_dependencies($state, \%requested, rpmdb => $query->{env} && "$query->{env}/rpmdb.cz", auto_select => $query->{auto_select}, callback_choices => $stop_on_choices); $query->{deps} or delete @{$state->{selected}}{grep { exists $state->{selected}{$_} && ! defined $state->{selected}{$_} } keys %{$state->{selected}}}; } elsif ($query->{deps}) { #- only deps required. my $empty_db = new URPM; #- URPM has same methods as URPM::DB and empty URPM will be seen as empty URPM::DB. require URPM::Resolve; $urpm->resolve_requested($empty_db, $state, \%requested, callback_choices => $stop_on_choices, nodeps => 1); } else { $state->{selected} = \%requested; } if ($query->{headers} || $query->{sources} || $query->{info}) { my ($local_sources, $list) = $urpm->get_source_packages($state->{selected}); unless ($local_sources || $list) { $urpm->{fatal}(1, N("unable to get source packages, aborting")); } if ($query->{headers}) { #- now examine source package to build headers list to extract. values %$local_sources and system 'rpm2header', values %$local_sources; foreach (0..$#{$urpm->{media} || []}) { my @headers = (grep { my $file = "$urpm->{cachedir}/headers/$_"; -s $file and system 'cat', $file; ! -s $file } map { /(.*)\/([^\/]*)-([^-]*)-([^-]*)\.([^\.]*)\.rpm/ and "$2-$3-$4.$5" } values %{$list->[$_]}); @headers > 0 or next; if (-s "$urpm->{statedir}/$urpm->{media}[$_]{hdlist}") { require packdrake; my $packer = new packdrake("$urpm->{statedir}/$urpm->{media}[$_]{hdlist}"); $packer->extract_archive(undef, @headers); } else { #- fallback to retrieve rpm package before, so that --headers will be ok. my %downloads = $urpm->download_source_packages({}, $list, force_local => 1); system "rpm2header", values %downloads; } } } elsif ($query->{info}) { my %downloads; unless ($>) { foreach (0..$#{$urpm->{media} || []}) { if (my @headers = (grep { my $file = "$urpm->{cachedir}/headers/$_"; ! -s $file } map { my $pkg = $urpm->{depslist}[$_]; $pkg && $pkg->header_filename } keys %{$list->[$_]})) { #map { /(.*)\/([^\/]*)-([^-]*)-([^-]*)\.([^\.]*)\.rpm/ and "$2-$3-$4.$5" } values %{$list->[$_]})) { if ($< == 0 && -s "$urpm->{statedir}/$urpm->{media}[$_]{hdlist}") { require packdrake; my $packer = new packdrake("$urpm->{statedir}/$urpm->{media}[$_]{hdlist}"); $packer->extract_archive("$urpm->{cachedir}/headers", @headers); } elsif (!%downloads) { #- fallback to retrieve rpm package before, so that --headers will be ok. %downloads = $urpm->download_source_packages({}, $list, force_local => 1); } } } } foreach (keys %{$state->{selected}}) { foreach (split /\|/, $_) { my $pkg = $urpm->{depslist}[$_] or next; my $file = $local_sources->{$_} || $downloads{$_} || "$urpm->{cachedir}/headers/".$pkg->header_filename; $pkg->update_header($file, keep_all_tags => 1); printf "%-12s: %s\n", "Name", $pkg->name; printf "%-12s: %s\n", "Version", $pkg->version; printf "%-12s: %s\n", "Release", $pkg->release; printf "%-12s: %s\n", "Group", $pkg->group; printf "%-12s: %-28s %12s: %s\n", "Size", $pkg->size, "Architecture", $pkg->arch; if ($pkg->sourcerpm || $pkg->buildhost) { if ($pkg->sourcerpm && $pkg->buildhost) { printf "%-12s: %-28s %12s: %s\n", "Source RPM", $pkg->sourcerpm, "Build Host", $pkg->buildhost; } elsif ($pkg->sourcerpm) { $pkg->sourcerpm and printf "%-12s: %s\n", "Source RPM", $pkg->sourcerpm; } else { $pkg->sourcerpm and printf "%-12s: %s\n", "Build Host", $pkg->buildhost; } } $pkg->packager and printf "%-12s: %s\n", "Packager", $pkg->packager; $pkg->url and printf "%-12s: %s\n", "URL", $pkg->url; $pkg->summary and printf "%-12s: %s\n", "Summary", $pkg->summary; $pkg->description and printf "%-12s:\n%s\n", "Description", $pkg->description; } } } elsif ($query->{sources}) { print join "\n", values %$local_sources; values %$local_sources and print "\n"; foreach (0..$#{$urpm->{media} || []}) { print join "\n", values %{$list->[$_]}; values %{$list->[$_]} and print "\n"; } } exit 0; } } #- print sub for query. my $query_sub = sub { my ($id) = @_; my $pkg = $urpm->{depslist}[$id]; my $str = ''; $query->{group} and $str .= $pkg->group . '/'; $str .= $pkg->name; $query->{version} and $str .= '-' . $pkg->version; $query->{release} and $str .= '-' . $pkg->release; $query->{arch} and $str .= '.' . $pkg->arch; $str; }; my %hack_only_one; if ($query->{complete}) { foreach my $removal (grep { $state->{rejected}{$_}{removed} && !$state->{rejected}{$_}{obsoleted} } keys %{$state->{rejected} || {}}) { print '@removing@' . $removal . "\n"; } foreach my $selected (values %{$state->{selected} || {}}) { if (ref($selected) eq 'HASH' && ref($selected->{unsatisfied}) eq 'ARRAY') { foreach (@{$selected->{unsatisfied}}) { exists $hack_only_one{$_} and next; print '@unsatisfied@' . $_ . "\n"; $hack_only_one{$_} = undef; } } } } foreach my $id ($state->{selected} ? keys %{$state->{selected}} : keys %requested) { my $class = $state->{selected}{$id} || $requested{$id}; if (ref($class) eq 'ARRAY') { foreach my $choices (@{$class || []}) { exists $hack_only_one{$choices} and next; print join('|', map { $query_sub->($_) } @$choices), "\n"; $hack_only_one{$choices} = undef; } } else { print join('|', map { $query_sub->($_) } split '\|', $id), "\n"; } }