#!/usr/bin/perl # # vim: ts=2:et # # sboremove # script to remove an installed SlackBuild # # authors: Luke Williams # Jacob Pipkin # Andreas Guldstrand # license: WTFPL use 5.16.0; use strict; use warnings FATAL => 'all'; use SBO::Lib qw/ get_inst_names get_installed_packages get_sbo_location get_build_queue merge_queues get_requires get_readme_contents prompt show_version in /; use Getopt::Long qw(:config bundling); use File::Basename; my $self = basename ($0); sub show_usage { print <<"EOF"; Usage: $self [options] sbo Options (defaults shown first where applicable): -h|--help: this screen. -v|--version: version information. -a|--alwaysask: always ask to remove, even if required by other packages on system. Note: optional dependencies need to be removed separately. EOF return 1; } my ($help, $vers, $non_int, $alwaysask); GetOptions( 'help|h' => \$help, 'version|v' => \$vers, 'nointeractive' => \$non_int, 'alwaysask|a' => \$alwaysask, ); if ($help) { show_usage(); exit 0 } if ($vers) { show_version(); exit 0 } if (!@ARGV) { show_usage(); exit 1 } # current workflow: # * get names of all installed SBo packages # * compare commandline args to SBo packages as well as installed SBo packages # * add reverse deps to list if they're not a dep of something else (which is not also already on the list) # * confirm removal of each package on the list # - while taking into account the options passed in such as $non_int, and $alwaysask # - also offering to display README if %README% is passed # * remove the confirmed packages sub main { my ($non_int, $alwaysask, @args) = @_; my @installed = @{ get_installed_packages('SBO') }; my $installed = +{ map {; $_->{name}, $_->{pkg} } @installed }; @args = grep { check_sbo($_, $installed) } @args; exit 1 unless @args; my %sbos = map { $_ => 1 } @args; my @remove = get_full_queue($installed, @args); my @confirmed; if ($non_int) { @confirmed = @remove; } else { my $required_by = get_reverse_reqs($installed); for my $remove (@remove) { # if $remove was on the commandline, mark it as not needed, # otherwise check if it is needed by something else. my @required_by = get_required_by($remove->{name}, [map { $_->{name} } @confirmed], $required_by); my $needed = $sbos{$remove->{name}} ? 0 : @required_by; next if $needed and not $alwaysask; push @confirmed, $remove if confirm($remove, $needed ? @required_by : ()); } } return say "Nothing to remove." unless @confirmed; remove($non_int, @confirmed); } sub check_sbo { my ($sbo, $installed) = @_; if (not get_sbo_location($sbo)) { say "Unable to locate $sbo in the SlackBuilds.org tree."; return 0; } if (not exists $installed->{$sbo}) { say "$sbo is not installed from SlackBuilds.org."; return 0; } return 1; } sub get_full_queue { my ($installed, @sbos) = @_; my $remove_queue = []; my %warnings; for my $sbo (@sbos) { my $queue = get_build_queue([$sbo], \%warnings); @$queue = reverse @$queue; $remove_queue = merge_queues($remove_queue, $queue); } return map {; +{ name => $_, pkg => $installed->{$_}, defined $warnings{$_} ? (warning => $warnings{$_}) : () } } grep { exists $installed->{$_} } @$remove_queue; } sub get_reverse_reqs { my $installed = shift; my %required_by; for my $inst (keys %$installed) { for my $req (@{ get_requires($inst) }) { $required_by{$req}{$inst} = 1 if exists $installed->{$req}; } } return \%required_by; } sub get_required_by { my ($sbo, $confirmed, $required_by) = @_; my @dep_of; if ( $required_by->{$sbo} ) { for my $req_by (keys %{$required_by->{$sbo}}) { push @dep_of, $req_by unless in($req_by => @$confirmed); } } return @dep_of; } sub confirm { my ($remove, @required_by) = @_; if (@required_by) { say sprintf "%s : required by %s", $remove->{name}, join ' ', @required_by; } else { say $remove->{name}; } if ($remove->{warning}) { say "It is recommended that you view the README before continuing."; if (prompt("Display README now?", default => 'yes')) { my $readme = get_readme_contents(get_sbo_location($remove->{name})); if (not defined $readme) { warn "Unable to open README for $remove->{name}.\n"; } else { print "\n" . $readme; } } } if (prompt("Remove $remove->{name}?", default => @required_by ? 'no' : 'yes')) { say " * Added to remove queue\n"; return 1; } say " * Ignoring\n"; return 0; } sub remove { my ($non_int, @confirmed) = @_; say sprintf "Removing %d package(s)", scalar @confirmed; say join " ", map { $_->{name} } @confirmed; if (!$non_int and !prompt("\nAre you sure you want to continue?", default => 'no')) { return say 'Exiting.'; } system("/sbin/removepkg", $_->{pkg}) for @confirmed; say "All operations have completed successfully."; } main($non_int, $alwaysask, @ARGV);