File: //bin/dh_installalternatives
#!/usr/bin/perl
=head1 NAME
dh_installalternatives - install declarative alternative rules
=cut
use strict;
use warnings;
use constant LINE_PREFIX => ' ' . q{\\} . "\n ";
use Debian::Debhelper::Dh_Lib;
our $VERSION = DH_BUILTIN_VERSION;
=head1 SYNOPSIS
B<dh_installalternatives> [S<I<debhelper options>>]
=head1 DESCRIPTION
B<dh_installalternatives> is a debhelper program that is responsible for
parsing the declarative alternatives format and insert the relevant
maintscripts snippets to interface with L<update-alternatives(1)>
=head1 FILES
=over 4
=item debian/I<package>.alternatives
An example of the format:
Name: editor
Link: /usr/bin/editor
Alternative: /usr/bin/vim.basic
Dependents:
/usr/share/man/man1/editor.1.gz editor.1.gz /usr/share/man/man1/vim.1.gz
/usr/share/man/fr/man1/editor.1.gz editor.fr.1.gz /usr/share/man/fr/man1/vim.1.gz
/usr/share/man/it/man1/editor.1.gz editor.it.1.gz /usr/share/man/it/man1/vim.1.gz
/usr/share/man/pl/man1/editor.1.gz editor.pl.1.gz /usr/share/man/pl/man1/vim.1.gz
/usr/share/man/ru/man1/editor.1.gz editor.ru.1.gz /usr/share/man/ru/man1/vim.1.gz
Priority: 50
The fields B<Link>, B<Name>, B<Alternative>, and B<Priority> are mandatory and correspond
to the L<update-alternatives(1)> B<--install> parameters B<link>, B<name>, B<path>, and
B<priority> respectively.
The B<Dependents> field is optional and consists of one or more lines. Each non-empty
line must contain exactly 3 space separated values that match (in order) the values passed
to the B<--slave> parameter for L<update-alternatives(1)>.
=back
=head1 OPTIONS
=over 4
=item B<-n>, B<--no-scripts>
Do not modify F<postinst>/F<postrm>/F<prerm> scripts.
=back
=cut
init();
# Explicitly discard attempts to use --name; it does not make sense for
# this helper.
if ($dh{NAME}) {
warning('Ignoring unsupported --name option');
}
$dh{NAME} = undef;
# PROMISE: DH NOOP WITHOUT alternatives cli-options()
foreach my $package (@{$dh{DOPACKAGES}}) {
my $tmp = tmpdir($package);
my $alternatives = pkgfile($package, 'alternatives');
if (-f $alternatives) {
_parse_alternatives_file_and_generate_maintscripts($package, $tmp, $alternatives);
}
}
sub _parse_alternative_and_generate_maintscript {
my ($package, $tmpdir, $alternatives_file, $ctrl) = @_;
my $link_name = $ctrl->{'Name'} // error("Missing mandatory \"Name\" field in ${alternatives_file}");
my $link_path = $ctrl->{'Link'}
// error("Missing mandatory \"Link\" field for \"${link_name}\" in ${alternatives_file}");
my $impl_path = $ctrl->{'Alternative'}
// error("Missing mandatory \"Alternative\" field for \"${link_name}\" in ${alternatives_file}");
my $priority = $ctrl->{'Priority'}
// error("Missing mandatory \"Priority\" field for \"${link_name}\" in ${alternatives_file}");
my %maintscript_options;
if (index($link_name, '/') > -1) {
error(qq{Invalid link name "${link_name}" in "${alternatives_file}": Must not contain slash});
}
if ( ! -f "${tmpdir}/${impl_path}") {
error(qq{Alternative "${impl_path}" for "${link_name}" in ${alternatives_file} does not exist in ${tmpdir}});
}
$maintscript_options{'RM_OPTIONS'} = "--remove ${link_name} ${impl_path}";
$maintscript_options{'INSTALL_OPTIONS'} = "--install ${link_path} ${link_name} ${impl_path} ${priority}";
if (defined(my $slave_link_text = $ctrl->{'Dependents'})) {
my (%dlink_dup, @dependent_links);
for my $line (split(/\n/, $slave_link_text)) {
my ($dlink_name, $dlink_path, $dimpl_path, $trailing);
my $error_with_def = 0;
$line =~ s/^\s++//;
$line =~ s/\s++$//;
next if $line eq ''; # Ignore empty lines
($dlink_path, $dlink_name, $dimpl_path, $trailing) = split(' ', $line, 4);
if (not $dlink_name) {
warning(qq{Missing link name value (2nd item) for dependent link "${dlink_name}" for "${link_name}"}
. qq{ in "${alternatives_file}"});
$error_with_def = 1;
} elsif (index($dlink_name, '/') > -1) {
warning(qq{Invalid dependent link name "${dlink_name}" for "${link_name}"}
. qq{ in "${alternatives_file}": Must not contain slash});
$error_with_def = 1;
} elsif ($dlink_dup{$dlink_name}) {
warning(qq{Dependent link "${dlink_name}" is seen more than once for "${link_name}"}
. qq{ in ${alternatives_file}});
$error_with_def = 1;
}
if (not $dimpl_path) {
warning(qq{Missing path (alternative) value (3rd item) for dependent link "${dlink_name}"}
. qq{ for "${link_name}" in "${alternatives_file}"});
$error_with_def = 1;
}
if ($trailing) {
warning(qq{Trailing information for dependent link "${dlink_name}" for "${link_name}"}
. qq{ in "${alternatives_file}"}) if $trailing;
warning("Dependent links must consist of exactly 3 space-separated values");
$error_with_def = 1;
}
if ($error_with_def) {
my $link_id = $dlink_name // ('no ' . (scalar(@dependent_links) + 1));
error("Error parsing dependent link ${link_id} for \"${link_name}\" in ${alternatives_file}.");
}
push(@dependent_links, "--slave $dlink_path $dlink_name $dimpl_path");
}
error("Empty \"Dependents\" field for \"${link_name}\" in ${alternatives_file} (please remove it or add an entry)")
if not @dependent_links;
$maintscript_options{'INSTALL_OPTIONS'} .= LINE_PREFIX . join(LINE_PREFIX, @dependent_links);
}
for my $wrong_name (qw(Slave Slaves Slave-Links)) {
if ($ctrl->{$wrong_name}) {
error("Please use Dependents instead of ${wrong_name}");
}
}
autoscript($package, 'postinst', 'postinst-alternatives', \%maintscript_options);
autoscript($package, 'prerm', 'prerm-alternatives', \%maintscript_options);
return;
}
sub _parse_alternatives_file_and_generate_maintscripts {
my ($package, $tmpdir, $alternatives_file) = @_;
my ($ctrl, $fd);
require Dpkg::Control::HashCore;
open($fd, '<', $alternatives_file) or error("open $alternatives_file failed: $!");
while (defined($ctrl = Dpkg::Control::HashCore->new) and ($ctrl->parse($fd, $alternatives_file))) {
_parse_alternative_and_generate_maintscript($package, $tmpdir, $alternatives_file, $ctrl);
}
close($fd);
return;
}
=head1 SEE ALSO
L<debhelper(7)>
This program is a part of debhelper.
=cut