Remove.pm (5671B)
1 package SBO::App::Remove; 2 3 # vim: ts=2:et 4 # 5 # authors: Luke Williams <xocel@iquidus.org> 6 # Jacob Pipkin <j@dawnrazor.net> 7 # Andreas Guldstrand <andreas.guldstrand@gmail.com> 8 # maintainer: Slack Coder <slackcoder@server.ky> 9 10 use 5.16.0; 11 use strict; 12 use warnings FATAL => 'all'; 13 use SBO::Lib qw/ _ERR_USAGE get_inst_names get_installed_packages get_sbo_location get_build_queue merge_queues get_requires get_readme_contents prompt show_version in /; 14 use Getopt::Long qw(GetOptionsFromArray :config bundling); 15 16 use parent 'SBO::App'; 17 18 our $VERSION = '2.9.0'; 19 20 sub _parse_opts { 21 my $class = shift; 22 my @ARGS = @_; 23 24 my ($help, $vers, $non_int, $alwaysask); 25 26 my $res = GetOptionsFromArray( 27 \@ARGS, 28 'help|h' => \$help, 29 'version|v' => \$vers, 30 'nointeractive' => \$non_int, 31 'alwaysask|a' => \$alwaysask, 32 ); 33 34 return ({ help => $help, vers => $vers, non_int => $non_int, alwaysask => $alwaysask, args => \@ARGS, }, $res); 35 } 36 37 sub run { 38 my $self = shift; 39 40 if ($self->{help}) { $self->show_usage(); return 0; } 41 if ($self->{vers}) { $self->show_version(); return 0; } 42 if (!@{ $self->{args} }) { $self->show_usage(); return 1; } 43 44 unless ($< == 0) { 45 warn "This script requires root privileges.\n"; 46 exit _ERR_USAGE; 47 } 48 49 # current workflow: 50 # * get names of all installed SBo packages 51 # * compare commandline args to SBo packages as well as installed SBo packages 52 # * add reverse deps to list if they're not a dep of something else (which is not also already on the list) 53 # * confirm removal of each package on the list 54 # - while taking into account the options passed in such as $non_int, and $alwaysask 55 # - also offering to display README if %README% is passed 56 # * remove the confirmed packages 57 58 my @args = @{ $self->{args} }; 59 60 my @installed = @{ get_installed_packages('SBO') }; 61 my $installed = +{ map {; $_->{name}, $_->{pkg} } @installed }; 62 63 @args = grep { check_sbo($_, $installed) } @args; 64 exit 1 unless @args; 65 my %sbos = map { $_ => 1 } @args; 66 67 my @remove = get_full_queue($installed, @args); 68 69 my @confirmed; 70 71 if ($self->{non_int}) { 72 @confirmed = @remove; 73 } else { 74 my $required_by = get_reverse_reqs($installed); 75 for my $remove (@remove) { 76 # if $remove was on the commandline, mark it as not needed, 77 # otherwise check if it is needed by something else. 78 my @required_by = get_required_by($remove->{name}, [map { $_->{name} } @confirmed], $required_by); 79 my $needed = $sbos{$remove->{name}} ? 0 : @required_by; 80 81 next if $needed and not $self->{alwaysask}; 82 83 push @confirmed, $remove if confirm($remove, $needed ? @required_by : ()); 84 } 85 } 86 87 if (@confirmed) { 88 $self->remove(@confirmed); 89 } else { 90 say "Nothing to remove."; 91 } 92 93 return 0; 94 } 95 96 sub show_usage { 97 my $self = shift; 98 my $fname = $self->{fname}; 99 100 print <<"EOF"; 101 Usage: $fname [options] sbo 102 103 Options (defaults shown first where applicable): 104 -h|--help: 105 this screen. 106 -v|--version: 107 version information. 108 -a|--alwaysask: 109 always ask to remove, even if required by other packages on system. 110 111 Note: optional dependencies need to be removed separately. 112 113 EOF 114 return 1; 115 } 116 117 sub check_sbo { 118 my ($sbo, $installed) = @_; 119 120 if (not get_sbo_location($sbo)) { 121 say "Unable to locate $sbo in the SlackBuilds.org tree."; 122 return 0; 123 } 124 125 if (not exists $installed->{$sbo}) { 126 say "$sbo is not installed from SlackBuilds.org."; 127 return 0; 128 } 129 130 return 1; 131 } 132 133 sub get_full_queue { 134 my ($installed, @sbos) = @_; 135 136 my $remove_queue = []; 137 my %warnings; 138 for my $sbo (@sbos) { 139 my $queue = get_build_queue([$sbo], \%warnings); 140 @$queue = reverse @$queue; 141 $remove_queue = merge_queues($remove_queue, $queue); 142 } 143 144 return map {; +{ 145 name => $_, 146 pkg => $installed->{$_}, 147 defined $warnings{$_} ? (warning => $warnings{$_}) : () 148 } } 149 grep { exists $installed->{$_} } 150 @$remove_queue; 151 } 152 153 sub get_reverse_reqs { 154 my $installed = shift; 155 my %required_by; 156 157 for my $inst (keys %$installed) { 158 for my $req (@{ get_requires($inst) }) { 159 $required_by{$req}{$inst} = 1 if exists $installed->{$req}; 160 } 161 } 162 163 return \%required_by; 164 } 165 166 sub get_required_by { 167 my ($sbo, $confirmed, $required_by) = @_; 168 my @dep_of; 169 170 if ( $required_by->{$sbo} ) { 171 for my $req_by (keys %{$required_by->{$sbo}}) { 172 push @dep_of, $req_by unless in($req_by => @$confirmed); 173 } 174 } 175 return @dep_of; 176 } 177 178 sub confirm { 179 my ($remove, @required_by) = @_; 180 181 if (@required_by) { 182 say sprintf "%s : required by %s", $remove->{name}, join ' ', @required_by; 183 } else { 184 say $remove->{name}; 185 } 186 187 if ($remove->{warning}) { 188 say "It is recommended that you view the README before continuing."; 189 if (prompt("Display README now?", default => 'yes')) { 190 my $readme = get_readme_contents(get_sbo_location($remove->{name})); 191 if (not defined $readme) { 192 warn "Unable to open README for $remove->{name}.\n"; 193 } else { 194 print "\n" . $readme; 195 } 196 } 197 } 198 199 if (prompt("Remove $remove->{name}?", default => @required_by ? 'no' : 'yes')) { 200 say " * Added to remove queue\n"; 201 return 1; 202 } 203 say " * Ignoring\n"; 204 return 0; 205 } 206 207 sub remove { 208 my $self = shift; 209 my $non_int = $self->{non_int}; 210 my @confirmed = @_; 211 212 say sprintf "Removing %d package(s)", scalar @confirmed; 213 say join " ", map { $_->{name} } @confirmed; 214 215 if (!$non_int and !prompt("\nAre you sure you want to continue?", default => 'no')) { 216 return say 'Exiting.'; 217 } 218 219 system("/sbin/removepkg", $_->{pkg}) for @confirmed; 220 221 say "All operations have completed successfully."; 222 } 223 224 1;