summaryrefslogtreecommitdiffstats
path: root/dh_install
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xdh_install387
-rwxr-xr-xdh_installalternatives193
-rwxr-xr-xdh_installcatalogs138
-rwxr-xr-xdh_installchangelogs375
-rwxr-xr-xdh_installcron90
-rwxr-xr-xdh_installdeb429
-rwxr-xr-xdh_installdebconf243
-rwxr-xr-xdh_installdirs141
-rwxr-xr-xdh_installdocs447
-rwxr-xr-xdh_installemacsen149
-rwxr-xr-xdh_installexamples192
-rwxr-xr-xdh_installgsettings106
-rwxr-xr-xdh_installifupdown82
-rwxr-xr-xdh_installinfo133
-rwxr-xr-xdh_installinit427
-rwxr-xr-xdh_installinitramfs103
-rwxr-xr-xdh_installlogcheck91
-rwxr-xr-xdh_installlogrotate63
-rwxr-xr-xdh_installman430
-rwxr-xr-xdh_installmanpages208
-rwxr-xr-xdh_installmenu100
-rwxr-xr-xdh_installmime73
-rwxr-xr-xdh_installmodules119
-rwxr-xr-xdh_installpam81
-rwxr-xr-xdh_installppp78
-rwxr-xr-xdh_installsystemd453
-rwxr-xr-xdh_installsystemduser288
-rwxr-xr-xdh_installsysusers115
-rwxr-xr-xdh_installtmpfiles128
-rwxr-xr-xdh_installudev112
-rwxr-xr-xdh_installwm140
-rwxr-xr-xdh_installxfonts100
32 files changed, 6214 insertions, 0 deletions
diff --git a/dh_install b/dh_install
new file mode 100755
index 0000000..da92715
--- /dev/null
+++ b/dh_install
@@ -0,0 +1,387 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_install - install files into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_install> [B<-X>I<item>] [B<--autodest>] [B<--sourcedir=>I<dir>] [S<I<debhelper options>>] [S<I<file|dir> ... I<destdir>>]
+
+=head1 DESCRIPTION
+
+B<dh_install> is a debhelper program that handles installing files into package
+build directories. There are many B<dh_install>I<*> commands that handle installing
+specific types of files such as documentation, examples, man pages, and so on,
+and they should be used when possible as they often have extra intelligence for
+those particular tasks. B<dh_install>, then, is useful for installing everything
+else, for which no particular intelligence is needed. It is a replacement for
+the old B<dh_movefiles> command.
+
+This program may be used in one of two ways. If you just have a file or two
+that the upstream Makefile does not install for you, you can run B<dh_install>
+on them to move them into place. On the other hand, maybe you have a large
+package that builds multiple binary packages. You can use the upstream
+F<Makefile> to install it all into F<debian/tmp>, and then use B<dh_install> to copy
+directories and files from there into the proper package build directories.
+
+From debhelper compatibility level 7 on, B<dh_install> will fall back to
+looking in F<debian/tmp> for files, if it does not find them in the current
+directory (or wherever you've told it to look using B<--sourcedir>).
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.install
+
+List the files to install into each package and the directory they should be
+installed to. The format is a set of lines, where each line lists a file or
+files to install, and at the end of the line tells the directory it should be
+installed in. The name of the files (or directories) to install should be given
+relative to the current directory, while the installation directory is given
+relative to the package build directory. You may use wildcards in the names of
+the files to install.
+
+Note that if you list exactly one filename or wildcard-pattern on a line by
+itself, with no explicit destination, then B<dh_install>
+will automatically guess the destination to use, the same as if the
+--autodest option were used.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=item debian/not-installed
+
+Used with the deprecated B<--list-missing> and B<--fail-missing> options.
+Please refer to L<dh_missing(1)> for the documentation of this file.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--list-missing>
+
+B<Deprecated>: Please use B<dh_missing --list-missing> instead. If you use
+this option, B<dh_install> will call B<dh_missing> with that option after it has
+processed all the files. Please see L<dh_missing(1)> for the documentation of
+this option.
+
+This option is removed in compat 12.
+
+=item B<--fail-missing>
+
+B<Deprecated>: Please use B<dh_missing --fail-missing> instead. If you use
+this option, B<dh_install> will call B<dh_missing> with that option after it has
+processed all the files. Please see L<dh_missing(1)> for the documentation of
+this option.
+
+This option is removed in compat 12.
+
+=item B<--sourcedir=>I<dir>
+
+Look in the specified directory for files to be installed.
+
+Note that this is not the same as the B<--sourcedirectory> option used
+by the B<dh_auto_>I<*> commands. You rarely need to use this option, since
+B<dh_install> automatically looks for files in F<debian/tmp> in debhelper
+compatibility level 7 and above.
+
+=item B<--autodest>
+
+Guess as the destination directory to install things to. If this is
+specified, you should not list destination directories in
+F<debian/package.install> files or on the command line. Instead, B<dh_install>
+will guess as follows:
+
+Strip off F<debian/tmp> (or the sourcedir if one is given) from the front of
+the filename, if it is present, and install into the dirname of the
+filename. So if the filename is F<debian/tmp/usr/bin>, then that directory
+will be copied to F<debian/package/usr/>. If the filename is
+F<debian/tmp/etc/passwd>, it will be copied to F<debian/package/etc/>.
+
+=item I<file|dir> ... I<destdir>
+
+Lists files (or directories) to install and where to install them to.
+The files will be installed into the first package F<dh_install> acts on.
+
+=back
+
+=cut
+
+init(options => {
+ "autodest" => \$dh{AUTODEST},
+ "list-missing" => \$dh{LIST_MISSING},
+ "fail-missing" => \$dh{FAIL_MISSING},
+ "sourcedir=s" => \$dh{SOURCEDIR},
+});
+
+my $srcdir = '.';
+if (defined($dh{SOURCEDIR})) {
+ $srcdir = $dh{SOURCEDIR};
+ $srcdir =~ s{/+$}{};
+ error("Invalid --sourcedir - must not be empty nor /") if not $srcdir;
+}
+
+my $missing_files = 0;
+
+if ($dh{LIST_MISSING} || $dh{FAIL_MISSING}) {
+ deprecated_functionality('Please use dh_missing --list-missing/--fail-missing instead', 12);
+}
+
+# Support for -X flag.
+my $exclude = '';
+if ($dh{EXCLUDE_FIND}) {
+ $exclude = '! \( '.$dh{EXCLUDE_FIND}.' \)';
+}
+
+# PROMISE: DH NOOP WITHOUT pkgfile-logged(install) cli-options()
+
+foreach my $package (getpackages()) {
+ my (@installed, %dest2sources);
+ my $default_source_dir = default_sourcedir($package);
+ my @search_dirs = ($srcdir);
+ push(@search_dirs, $default_source_dir) if not compat(6);
+
+ # Look at the install files for all packages to handle
+ # list-missing/fail-missing, but skip really installing for
+ # packages that are not being acted on.
+ my $skip_install = process_pkg($package) ? 0 : 1;
+
+ my $tmp=tmpdir($package);
+ my $file=pkgfile($package,"install");
+
+ my @install;
+ if ($file) {
+ @install=filedoublearray($file); # no globbing here; done below
+ }
+
+
+ # With autodest, we can just pretend every pattern was on its own line
+ @install = map { [$_] } map { @$_ } @install if $dh{AUTODEST};
+
+ if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) {
+ if ($dh{AUTODEST}) {
+ # Same as above, with autodest, we can just isolate each entry
+ # - the split is for bug-backwards compatibility (#867866).
+ push(@install, map { [$_] } map { split } @ARGV);
+ } else {
+ # Bug backwards compatibility (#867866). The new "glob_expand"
+ # interface is smart enough to not split on spaces, but dh_install
+ # used to do that... *except* for the "DEST" since it was never
+ # passed to the glob function.
+ my @a = @ARGV;
+ my $dest = pop(@a) if @a > 1;
+ my @srcs = map { split } @a;
+ push(@srcs, $dest) if defined($dest);
+ push(@install, \@srcs);
+ }
+ }
+
+
+ my $glob_error_handler = sub {
+ # Do not require a match for packages that not acted on
+ # (directly). After all, the files might not have been
+ # generated/compiled.
+ return if $skip_install;
+ ++$missing_files;
+ goto \&glob_expand_error_handler_warn_and_discard;
+ };
+
+ foreach my $set (@install) {
+ my ($dest, @filelist, @patterns);
+
+ if (@$set > 1) {
+ $dest=pop @$set;
+ }
+ # Skip excluded patterns. We will need two exclude checks per pattern;
+ # 1) exclude the entire pattern as people expect this to work (#814856)
+ # 2) exclude files matched by the pattern as people could have just
+ # excluded a single file of a "dir/*"-pattern.
+ # This line below filters entire patterns
+ @patterns = grep { not excludefile($_) } @{$set};
+ next if not @patterns;
+ foreach my $glob (@patterns) {
+ my @found = glob_expand(\@search_dirs, $glob_error_handler, $glob);
+ push(@filelist, map { tr{/}{/}s; $_ } @found);
+ }
+
+ if (! @filelist && ! $skip_install) {
+ warning("$package missing files: @$set");
+ ++$missing_files;
+ next;
+ }
+
+ # Do a quick bulk handling of excluded files and update @installed.
+ # - this is for filtering files matched by the pattern
+ @filelist = grep { not excludefile($_) } @filelist if $exclude;
+ push(@installed, @filelist);
+
+ # ... because then we can short-curcit here.
+ next if $skip_install or $missing_files;
+
+ if (not $exclude) {
+ my @unoptimized;
+ for my $src (@filelist) {
+ my $d = $dest // compute_dest($default_source_dir, $src);
+ my $basename = basename($src);
+ if (exists($dest2sources{$d}{$basename})) {
+ # If there is a clash, silently undo the optimizations.
+ # See #866405 and #868169.
+ my $replaced = delete($dest2sources{$d}{$basename});
+ # Associate the $replaced the destination
+ # directory. We cannot be sure that compute_dest will
+ # get it right nor can we blindly set $dest.
+ #
+ # It is technically unnecessary for $src, but we
+ # might as well do it to possibly save a
+ # compute_dest call.
+ push(@unoptimized, [$replaced, $d], [$src, $d]);
+ next;
+ }
+ $dest2sources{$d}{$basename} = $src;
+ }
+ next if not @unoptimized;
+ @filelist = @unoptimized;
+ }
+
+ foreach my $src (@filelist) {
+
+ my $target_dest;
+
+ if (ref($src)) {
+ # On a failed optimization, we will have the
+ # destination directory.
+ ($src, $target_dest) = @{$src};
+ } else {
+ $target_dest = $dest;
+ if (! defined $target_dest) {
+ # Guess at destination directory.
+ $target_dest = compute_dest($default_source_dir, $src);
+ }
+ }
+
+ # Make sure the destination directory exists.
+ install_dir("$tmp/$target_dest");
+
+ if (-d $src && $exclude) {
+ my $basename = basename($src);
+ my $dir = ($basename eq '.') ? $src : "$src/..";
+ my $pwd=`pwd`;
+ chomp $pwd;
+ complex_doit("cd '$dir' && " .
+ "find '$basename' $exclude \\( -type f -or -type l \\) -print0 | LC_ALL=C sort -z | " .
+ "xargs -0 -I {} cp --reflink=auto --parents -dp {} $pwd/$tmp/$target_dest/");
+ # cp is annoying so I need a separate pass
+ # just for empty directories
+ complex_doit("cd '$dir' && " .
+ "find '$basename' $exclude \\( -type d -and -empty \\) -print0 | LC_ALL=C sort -z | " .
+ "xargs -0 -I {} cp --reflink=auto --parents -a {} $pwd/$tmp/$target_dest/");
+ }
+ else {
+ doit("cp", '--reflink=auto', "-a", $src, "$tmp/$target_dest/");
+ }
+ }
+ }
+
+ for my $dest (sort(keys(%dest2sources))) {
+ my @srcs = sort(values(%{$dest2sources{$dest}}));
+ # Make sure the destination directory exists.
+ install_dir("$tmp/$dest");
+ xargs(\@srcs, "cp", '--reflink=auto', "-a", XARGS_INSERT_PARAMS_HERE, "$tmp/$dest/");
+ }
+ log_installed_files($package, @installed);
+}
+
+if ($missing_files) {
+ # There were files we could not install (e.g. patterns that matched nothing)
+ error("missing files, aborting");
+}
+
+if ($dh{LIST_MISSING} || $dh{FAIL_MISSING}) {
+ my @options;
+ foreach (@{$dh{EXCLUDE}}) {
+ push(@options, '--exclude', $_);
+ }
+ push(@options, '--sourcedir', $dh{SOURCEDIR}) if defined($dh{SOURCEDIR});
+ push @options, "--list-missing" if $dh{LIST_MISSING};
+ push @options, "--fail-missing" if $dh{FAIL_MISSING};
+ doit("dh_missing", @options);
+}
+
+sub compute_dest {
+ my ($source_dir, $dest) = @_;
+
+ $dest =~ s/^(.*\/)?\Q$srcdir\E\///;
+ $dest =~ s/^(.*\/)?\Q$source_dir\E\///;
+ $dest = dirname("/".$dest);
+
+ return $dest;
+}
+
+=head1 EXAMPLES
+
+Here are some small examples of configuration files for dh_install.
+
+ # Install my-prog into usr/bin (as "usr/bin/my-prog")
+ my-prog usr/bin
+
+ # Install a plugins directory into usr/share/my-prog
+ # (as "usr/share/my-prog/plugins/")
+ plugins usr/share/my-prog
+
+ # Install a file with spaces in into usr/share/my-prog/data
+ # (as "usr/share/my-prog/data/my datafile with spaces.txt")
+ # ASSUMES COMPAT 13, where substitution patterns are available
+ my${Space}datafile${Space}with${Space}spaces.txt usr/share/my-prog/data
+
+ # Install a library into the multi-arch lib directory
+ # ASSUMES COMPAT 13, where substitution patterns are available
+ build/output/libfrop*.so.* usr/lib/${DEB_HOST_MULTIARCH}
+
+=head1 LIMITATIONS
+
+B<dh_install> cannot rename files or directories, it can only install them
+with the names they already have into wherever you want in the package
+build tree.
+
+However, renaming can be achieved by using B<dh-exec> with compatibility level 9 or
+later. An example debian/I<package>.install file using B<dh-exec>
+could look like:
+
+ #!/usr/bin/dh-exec
+ debian/default.conf => /etc/my-package/start.conf
+
+Please remember the following three things:
+
+=over 4
+
+=item * The package must be using compatibility level 9 or later (see L<debhelper(7)>)
+
+=item * The package will need a build-dependency on dh-exec.
+
+=item * The install file must be marked as executable.
+
+=back
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installalternatives b/dh_installalternatives
new file mode 100755
index 0000000..7bfbae5
--- /dev/null
+++ b/dh_installalternatives
@@ -0,0 +1,193 @@
+#!/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});
+ }
+ my $actual_impl_path = "${tmpdir}/${impl_path}";
+ if ( ! -l $actual_impl_path && ! -e _) {
+ error(qq{Alternative "${impl_path}" for "${link_name}" in ${alternatives_file} does not exist in ${tmpdir}});
+ }
+ if ( -d $actual_impl_path) {
+ error(qq{Alternative "${impl_path}" for "${link_name}" in ${alternatives_file} is a directory});
+ }
+ if ($link_name eq $impl_path) {
+ error(qq{The link name cannot be the same as the implementation path "${link_name}" (in "${alternatives_file}")});
+ }
+
+ $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 ($dlink_name eq $dimpl_path) {
+ warning(qq{The link name cannot be the same as the implementation path for "${dlink_name}"}
+ . qq{ 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}"});
+ 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
diff --git a/dh_installcatalogs b/dh_installcatalogs
new file mode 100755
index 0000000..aa5f45d
--- /dev/null
+++ b/dh_installcatalogs
@@ -0,0 +1,138 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installcatalogs - install and register SGML Catalogs
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+my $sgmlbasever = "1.28";
+
+=head1 SYNOPSIS
+
+B<dh_installcatalogs> [S<I<debhelper options>>] [B<-n>]
+
+=head1 DESCRIPTION
+
+B<dh_installcatalogs> is a debhelper program that installs and
+registers SGML catalogs. It complies with the Debian XML/SGML policy.
+
+Catalogs will be registered in a supercatalog, in
+F</etc/sgml/I<package>.cat>.
+
+This command automatically adds maintainer script snippets for
+registering and unregistering the catalogs and supercatalogs (unless
+B<-n> is used). These snippets are inserted into the maintainer
+scripts and the B<triggers> file by B<dh_installdeb>; see
+L<dh_installdeb(1)> for an explanation of Debhelper maintainer script
+snippets.
+
+A dependency on B<sgml-base> will be added to B<${misc:Depends}>, so be
+sure your package uses that variable in F<debian/control>.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.sgmlcatalogs
+
+Lists the catalogs to be installed per package. Each line in that file
+should be of the form C<I<source> I<dest>>, where I<source> indicates where the
+catalog resides in the source tree, and I<dest> indicates the destination
+location for the catalog under the package build area. I<dest> should
+start with F</usr/share/sgml/>.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-n>, B<--no-scripts>
+
+Do not modify F<postinst>/F<postrm>/F<prerm> scripts nor add an
+activation trigger.
+
+=back
+
+=head1 NOTES
+
+Note that this command is not idempotent. L<dh_prep(1)> should be
+called between invocations of this command. Otherwise, it may cause
+multiple instances of the same text to be added to maintainer scripts.
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT sgmlcatalogs cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+ my $sgmlcatlistfile = pkgfile($package, "sgmlcatalogs");
+ my @sgmlinstalled; # catalogs we've installed
+ if ($#ARGV >= 0) {
+ error("extra command-line arguments");
+ }
+ if ($sgmlcatlistfile) {
+ foreach my $line (filedoublearray($sgmlcatlistfile)) {
+ my $source = $line->[0];
+ my $dest = $line->[1];
+ my $fulldest = "$tmpdir/$dest";
+ $fulldest =~ s|//|/|g; # beautification
+
+ if (! -d dirname($fulldest)) {
+ # Ensure the parent exist
+ install_dir($tmpdir."/".dirname($dest));
+ }
+
+ install_file($source,$fulldest);
+
+ push(@sgmlinstalled,$dest);
+ }
+ }
+ if (@sgmlinstalled) {
+ addsubstvar($package, "misc:Depends", "sgml-base", ">= $sgmlbasever");
+
+ install_dir("$tmpdir/etc/sgml");
+
+ my $centralcat = "/etc/sgml/$package.cat";
+
+ open(my $fd, ">", "$tmpdir$centralcat") || error("failed to write to $tmpdir$centralcat");
+ foreach my $sgmldest (@sgmlinstalled) {
+ print {$fd} "CATALOG " . $sgmldest . "\n";
+ }
+ close($fd) or error("close $tmpdir$centralcat: $!");
+
+ if (! $dh{NOSCRIPTS}) {
+ autotrigger($package, "activate-await", "update-sgmlcatalog");
+ autoscript($package, "postrm", "postrm-sgmlcatalog",
+ { 'CENTRALCAT' => $centralcat });
+ }
+ }
+ else {
+ # remove the dependency
+ addsubstvar($package, "misc:Depends", "sgml-base", ">= $sgmlbasever", 1);
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+F</usr/share/doc/sgml-base-doc/>
+
+=head1 AUTHOR
+
+Adam Di Carlo <aph@debian.org>
+
+=cut
diff --git a/dh_installchangelogs b/dh_installchangelogs
new file mode 100755
index 0000000..49c3821
--- /dev/null
+++ b/dh_installchangelogs
@@ -0,0 +1,375 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installchangelogs - install changelogs into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+use Time::Piece;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installchangelogs> [S<I<debhelper options>>] [B<-k>] [B<-X>I<item>] [B<--no-trim>] [I<upstream>]
+
+=head1 DESCRIPTION
+
+B<dh_installchangelogs> is a debhelper program that is responsible for
+installing changelogs into package build directories.
+
+An upstream F<changelog> file may be specified as an option. If none
+is specified, B<dh_installchangelogs> may look for files with names
+that seem likely to be changelogs as described in the next paragraphs.
+
+In non-native packages, B<dh_installchangelogs> will first look for
+changelog files installed by the upstream build system into F<<
+usr/share/doc/I<package> >> (of the package build directory) and
+rename the most likely candidate (if any) to F<<
+usr/share/doc/I<package>/changelog >>. Note that
+B<dh_installchangelogs> does I<not> look into any source directory
+(such as F<debian/tmp>). Otherwise, B<dh_installchangelogs> (at
+compatibility level 7 or any later) will look for changelog files in
+the source directory (e.g. the root or the F<docs> subdirectory). It
+will look for F<changelog>, F<changes> and F<history> optionally with
+common extensions (such as F<.txt>, F<.md> and F<.rst>).
+
+If a changelog file is specified and is an F<html> file (determined by file
+extension), it will be installed as F<usr/share/doc/package/changelog.html>
+instead. If the html changelog is converted to plain text, that variant
+can be specified as a second parameter. When no plain text variant is
+specified, a short F<usr/share/doc/package/changelog> is generated,
+pointing readers at the html changelog file.
+
+The B<debchange>-style Debian changelogs are trimmed to include only
+entries more recent than the release date of I<oldstable>.
+No trimming will be performed if the B<--no-trim> option is passed or
+if the B<DEB_BUILD_OPTIONS> environment variable contains B<notrimdch>.
+
+=head1 FILES
+
+=over 4
+
+=item F<debian/changelog>
+
+=item F<debian/NEWS>
+
+=item debian/I<package>.changelog
+
+=item debian/I<package>.NEWS
+
+Automatically installed into usr/share/doc/I<package>/
+in the package build directory.
+
+Use the package specific name if I<package> needs a different
+F<NEWS> or F<changelog> file.
+
+The F<changelog> file is installed with a name of changelog
+for native packages, and F<changelog.Debian> for non-native packages.
+The F<NEWS> file is always installed with a name of F<NEWS.Debian>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-k>, B<--keep>
+
+Keep the original name of the upstream changelog. This will be accomplished
+by installing the upstream changelog as F<changelog>, and making a symlink from
+that to the original name of the F<changelog> file. This can be useful if the
+upstream changelog has an unusual name, or if other documentation in the
+package refers to the F<changelog> file.
+
+=item B<-X>I<item>, B<--exclude=>I<item>
+
+Exclude upstream F<changelog> files that contain I<item> anywhere in their
+filename from being installed.
+
+Note that directory name of the changelog is also part of the match.
+
+=item B<--no-trim>
+
+Install the full changelog, not its trimmed version that includes only
+recent entries.
+
+=item I<upstream>
+
+Install this file as the upstream changelog.
+
+=back
+
+=cut
+
+init(options => {
+ 'keep|k' => \$dh{K_FLAG},
+ 'no-trim' => \$dh{NO_TRIM},
+});
+
+my $news_name="NEWS.Debian";
+my $changelog_name="changelog.Debian";
+
+use constant CUTOFF_DATE_STR => "2019-07-06"; # oldstable = Debian 10 Buster
+use constant CUTOFF_DATE => Time::Piece->strptime(CUTOFF_DATE_STR, "%Y-%m-%d");
+use constant MIN_NUM_ENTRIES => 4;
+
+my $explicit_changelog = @ARGV ? 1 : 0;
+my $default_upstream = $ARGV[0];
+my $default_upstream_text=$default_upstream;
+my $default_upstream_html;
+if (! defined($default_upstream)) {
+ if (! isnative($dh{MAINPACKAGE}) && !compat(6)) {
+ foreach my $dir (qw{. doc docs}) {
+ my $changelog = find_changelog($dir);
+ if ($changelog) {
+ $default_upstream = $changelog;
+ $default_upstream_text = $default_upstream;
+ last;
+ }
+ }
+ }
+ if (isnative($dh{MAINPACKAGE})) {
+ $changelog_name='changelog';
+ }
+}
+elsif ($default_upstream=~m/\.html?$/i) {
+ $default_upstream_html=$default_upstream;
+ $default_upstream_text=$ARGV[1];
+}
+
+sub find_changelog {
+ my ($dir) = @_;
+ my @files=sort glob("$dir/*");
+ foreach my $suffix ('', qw(.txt .md .rst)) {
+ foreach my $name (qw{changelog changes history}) {
+ my @matches=grep {
+ lc basename($_) eq "$name$suffix" && -f $_ && -s _ && ! excludefile($_)
+ } @files;
+ if (@matches) {
+ return shift(@matches);
+ }
+ }
+ }
+ return;
+}
+
+sub install_debian_changelog {
+ my ($changelog, $package, $arch, $tmp) = @_;
+
+ if ($dh{NO_TRIM} || get_buildoption("notrimdch")) {
+ # Install the whole changelog.
+ install_file($changelog, "$tmp/usr/share/doc/$package/$changelog_name");
+ return;
+ }
+
+ local $ENV{LC_ALL} = "C.UTF-8";
+
+ my $changelog_trimmed = generated_file($package, "dh_installchangelogs.dch.trimmed");
+ my $changelog_binnmu = generated_file($package, "dh_installchangelogs.dch.binnmu");
+
+ open(my $log1, "<", $changelog) or error("Cannot open($changelog): $!");
+ open(my $log2, ">", $changelog_trimmed) or error("Cannot open($changelog_trimmed): $!");
+
+ my $error_in_changelog = 0;
+ my $is_binnmu = 0;
+ my $entry = "";
+ my $entry_num = 0;
+ while (my $line=<$log1>) {
+ $entry .= $line;
+
+ # Identify binNUM packages by binary-only=yes in the first line of the changelog.
+ if (($. == 1) && ($line =~ /\A\S.*;.*\bbinary-only=yes/)) {
+ $is_binnmu = 1;
+ }
+
+ # Get out of binNMU mode once we are in the second entry (and throw away one empty line).
+ if ($is_binnmu && ($entry_num eq 1)) {
+ $is_binnmu = 0;
+ $entry_num = 0;
+ $entry = "";
+ next;
+ }
+
+ if ($line =~ /^\s*--\s+.*?\s+<[^>]*>\s+(?<timestamp>.*)$/) {
+ if ($is_binnmu && ($entry_num eq 0)) {
+ # For binNMUs the first changelog entry is written into an extra file to
+ # keep the packages coinstallable.
+ open(my $log_binnum, ">", $changelog_binnmu) or error("Cannot open($changelog_binnmu): $!");
+ print($log_binnum $entry) or error("Cannot write($changelog_binnmu): $!");
+ close($log_binnum) or error("Cannot close($changelog_binnmu): $!");
+
+ # Continue processing the rest of the changelog.
+ $entry = "";
+ $entry_num++;
+ next;
+ }
+
+ my $timestamp = $+{timestamp};
+ $timestamp =~ s/^[A-Za-z]+, +//;
+
+ my $entry_time;
+ eval { $entry_time = Time::Piece->strptime($timestamp, '%d %b %Y %T %z') };
+ if (! defined $entry_time) {
+ $error_in_changelog = 1;
+ warning("Could not parse timestamp '$timestamp'. $changelog will not be trimmed.");
+ truncate($log2, 0) or error("Cannot truncate($changelog_trimmed): $!");
+ last;
+ }
+
+ # Stop processing the changelog if we reached the cut-off date and
+ # at least MIN_NUM_ENTRIES entries have been added.
+ if (($entry_time < CUTOFF_DATE) && ($entry_num >= MIN_NUM_ENTRIES)) {
+ last;
+ }
+
+ # Append entry to trimmed changelog.
+ print($log2 $entry) or error("Cannot write($changelog_trimmed): $!");
+ $entry = "";
+ $entry_num++;
+ }
+ }
+ # If the whole changelog has not been read, then it has been trimmed.
+ my $has_been_trimmed = !eof($log1);
+
+ close($log1) or error("Cannot close($changelog): $!");
+ close($log2) or error("Cannot close($changelog_trimmed): $!");
+
+ if ($error_in_changelog) {
+ # If the changelog could not be trimmed, fall back to the full changelog.
+ warning("$changelog could not be trimmed. The full changelog will be installed.");
+ $changelog_trimmed = $changelog;
+ } elsif ($has_been_trimmed) {
+ # Otherwise add a comment stating that this changelog has been trimmed.
+ my $note = "\n";
+ $note .= "# Older entries have been removed from this changelog.\n";
+ $note .= "# To read the complete changelog use `apt changelog $package`.\n";
+ open(my $log2, ">>", $changelog_trimmed) or error("Cannot open($changelog_trimmed): $!");
+ print($log2 $note) or error("Cannot write($changelog_trimmed): $!");
+ close($log2) or error("Cannot close($changelog_trimmed): $!");
+ }
+
+ install_file($changelog_trimmed, "$tmp/usr/share/doc/$package/$changelog_name");
+ if (-s $changelog_binnmu) {
+ install_file($changelog_binnmu, "$tmp/usr/share/doc/$package/$changelog_name.$arch");
+ }
+}
+
+on_pkgs_in_parallel {
+ foreach my $package (@_) {
+ next if is_udeb($package);
+
+ my $tmp=tmpdir($package);
+ my $changelog=pkgfile($package,"changelog");
+ my $news=pkgfile($package,"NEWS");
+ my $upstream_changelog;
+ my ($upstream_changelog_text, $upstream_changelog_html);
+ my $changelog_from_tmp_dir = 0;
+
+ if ($explicit_changelog) {
+ $upstream_changelog = $default_upstream;
+ $upstream_changelog_text = $default_upstream_text;
+ $upstream_changelog_html = $default_upstream_html;
+ } else {
+ # Check if the upstream build system provided a
+ # changelog
+ $upstream_changelog = find_changelog("${tmp}/usr/share/doc/${package}");
+ if ($upstream_changelog) {
+ $upstream_changelog_text = $upstream_changelog;
+ $changelog_from_tmp_dir = 1;
+ } else {
+ $upstream_changelog = $default_upstream;
+ $upstream_changelog_text = $upstream_changelog;
+ }
+ }
+
+ if (!$changelog) {
+ $changelog="debian/changelog";
+ }
+ if (!$news) {
+ $news="debian/NEWS";
+ }
+
+ if (! -e $changelog) {
+ error("could not find changelog $changelog");
+ }
+
+ # If it is a symlink to a documentation directory from the same
+ # source package, then don't do anything. Think multi-binary
+ # packages that depend on each other and want to link doc dirs.
+ if (-l "$tmp/usr/share/doc/$package") {
+ my $linkval=readlink("$tmp/usr/share/doc/$package");
+ my %allpackages=map { $_ => 1 } getpackages();
+ if ($allpackages{basename($linkval)}) {
+ next;
+ }
+ # Even if the target doesn't seem to be a doc dir from the
+ # same source package, don't do anything if it's a dangling
+ # symlink.
+ next unless -d "$tmp/usr/share/doc/$package";
+ }
+
+ install_dir("$tmp/usr/share/doc/$package");
+
+ if (! $dh{NO_ACT}) {
+ my $arch = package_binary_arch($package);
+ install_debian_changelog($changelog, $package, $arch, $tmp);
+ }
+
+ if (-e $news) {
+ install_file($news, "$tmp/usr/share/doc/$package/$news_name");
+ }
+
+ if (defined($upstream_changelog)) {
+ my $link_to;
+ my $base="$tmp/usr/share/doc/$package";
+ if (defined($upstream_changelog_text)) {
+ if ($changelog_from_tmp_dir and not $dh{K_FLAG}) {
+ # mv (unless if it is the same file)
+ rename_path($upstream_changelog_text, "$base/changelog")
+ if basename($upstream_changelog_text) ne 'changelog';
+ reset_perm_and_owner(0644, "$base/changelog");
+ } else {
+ install_file($upstream_changelog_text, "$base/changelog");
+ }
+ $link_to='changelog';
+ }
+ if (defined($upstream_changelog_html)) {
+ if ($changelog_from_tmp_dir and not $dh{K_FLAG}) {
+ # mv (unless if it is the same file)
+ rename_path($upstream_changelog_html, "$base/changelog.html")
+ if basename($upstream_changelog_text) ne 'changelog.html';
+ reset_perm_and_owner(0644, "$base/changelog.html");
+ } else {
+ install_file($upstream_changelog_html,"$base/changelog.html");
+ }
+ $link_to='changelog.html';
+ if (! defined($upstream_changelog_text)) {
+ complex_doit("echo 'See changelog.html.gz' > $base/changelog");
+ reset_perm_and_owner(0644,"$base/changelog");
+ }
+ }
+ if ($dh{K_FLAG}) {
+ # Install symlink to original name of the upstream changelog file.
+ # Use basename in case original file was in a subdirectory or something.
+ doit('ln', '-sf', $link_to, "$tmp/usr/share/doc/$package/".basename($upstream_changelog));
+ }
+ }
+ }
+};
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installcron b/dh_installcron
new file mode 100755
index 0000000..9325b0e
--- /dev/null
+++ b/dh_installcron
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installcron - install cron scripts into etc/cron.*
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installcron> [S<B<debhelper options>>] [B<--name=>I<name>]
+
+=head1 DESCRIPTION
+
+B<dh_installcron> is a debhelper program that is responsible for installing
+cron scripts.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.cron.daily
+
+=item debian/I<package>.cron.weekly
+
+=item debian/I<package>.cron.monthly
+
+=item debian/I<package>.cron.yearly
+
+=item debian/I<package>.cron.hourly
+
+=item debian/I<package>.cron.d
+
+Installed into the appropriate F<etc/cron.*/> directory in the package
+build directory.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+Look for files named F<debian/package.name.cron.*> and install them as
+F<etc/cron.*/name>, instead of using the usual files and installing them
+as the package name.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT cron.hourly cron.daily cron.weekly cron.monthly cron.yearly cron.d cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ foreach my $type (qw{hourly daily weekly monthly yearly}) {
+ my $cron=pkgfile($package,"cron.$type");
+ if ($cron) {
+ install_dir("$tmp/etc/cron.$type");
+ install_prog($cron,"$tmp/etc/cron.$type/".pkgfilename($package));
+ }
+ }
+ # Separate because this needs to be mode 644.
+ my $cron=pkgfile($package,"cron.d");
+ if ($cron) {
+ install_dir("$tmp/etc/cron.d");
+ install_file($cron,"$tmp/etc/cron.d/".pkgfilename($package));
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installdeb b/dh_installdeb
new file mode 100755
index 0000000..b9fd72b
--- /dev/null
+++ b/dh_installdeb
@@ -0,0 +1,429 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installdeb - install files into the DEBIAN directory
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installdeb> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+B<dh_installdeb> is a debhelper program that is responsible for installing
+files into the F<DEBIAN> directories in package build directories with the
+correct permissions.
+
+=head1 FILES
+
+=over 4
+
+=item I<package>.postinst
+
+=item I<package>.preinst
+
+=item I<package>.postrm
+
+=item I<package>.prerm
+
+These maintainer scripts are installed into the F<DEBIAN> directory.
+
+B<dh_installdeb> will perform substitution of known tokens of
+the pattern B<#TOKEN#>. In generally, scripts will want to
+include the B<#DEBHELPER#> to benefit from the shell scripts
+generated by debhelper commands (including those from
+B<dh_installdeb> when it processes I<package>.maintscript files).
+
+The B<#DEBHELPER#> token should be placed on its own line as it is
+often replaced by a multi-line shell script.
+
+=item I<package>.triggers
+
+=item I<package>.shlibs
+
+These control files are installed into the F<DEBIAN> directory.
+
+Note that I<package>.shlibs is only installed in compat level 9 and
+earlier. In compat 10, please use L<dh_makeshlibs(1)>.
+
+=item I<package>.conffiles
+
+This file will be installed into the F<DEBIAN> directory. The
+provided file will be enriched by debhelper to include all the
+B<conffiles> auto-detected by debhelper (the maintainer should
+not list there as debhelper assumes it should handle that part).
+
+This file is primarily useful for using "special" entries such as
+the B<< remove-on-upgrade >> feature from dpkg.
+
+=item I<package>.maintscript
+
+Lines in this file correspond to L<dpkg-maintscript-helper(1)>
+commands and parameters. However, the "maint-script-parameters"
+should I<not> be included as debhelper will add those automatically.
+
+Example:
+
+ # Correct
+ rm_conffile /etc/obsolete.conf 0.2~ foo
+ # INCORRECT
+ rm_conffile /etc/obsolete.conf 0.2~ foo -- "$@"
+
+In compat 10 or later, any shell metacharacters will be escaped, so
+arbitrary shell code cannot be inserted here. For example, a line
+such as C<mv_conffile /etc/oldconffile /etc/newconffile> will insert
+maintainer script snippets into all maintainer scripts sufficient to
+move that conffile.
+
+It was also the intention to escape shell metacharacters in previous
+compat levels. However, it did not work properly and as such it was
+possible to embed arbitrary shell code in earlier compat levels.
+
+The B<dh_installdeb> tool will do some basic validation of some of
+the commands listed in this file to catch common mistakes. The
+validation is enabled as a warning since compat 10 and as a hard
+error in compat 12.
+
+Where possible, B<dh_installdeb> may choose to rewrite some or all
+of the entries into equivalent features supported in dpkg without
+relying on maintainer scripts at its sole discretion (examples
+include rewriting B<rm_conffile> into dpkg's B<remove-on-upgrade>).
+The minimum requirement for activating this feature is that debhelper
+runs in compat 10 or later.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-D>I<TOKEN=VALUE>, B<--define> I<TOKEN=VALUE>
+
+Define tokens to be replaced inside the maintainer scripts when
+it is generated. Please note that the limitations described in
+L</Limitations in token names> also applies to tokens defined
+on the command line. Invalid token names will trigger an error.
+
+In the simple case, this parameter will cause B<< #I<TOKEN># >>
+to be replaced by I<VALUE>. If I<VALUE> starts with a literal
+I<@>-sign, then I<VALUE> is expected to point to a file
+containing the actual value to insert.
+
+An explicit declared token with this parameter will replace built-in
+tokens.
+
+Test examples to aid with the understanding:
+
+ cat >> debian/postinst <<EOF
+ #SIMPLE#
+ #FILEBASED#
+ EOF
+ echo -n "Complex value" > some-file
+ dh_installdeb --define SIMPLE=direct --define FILEBASED=@some-file
+
+In this example, B<#SIMPLE#> will expand to B<direct> and B<#FILEBASED#>
+will expand to B<Complex value>.
+
+It is also possible to set package-specific values for a given
+token. This is useful when B<dh_installdeb> is acting on multiple
+packages that need different values for the same token. This is
+done by prefixing the token name with B<< pkg.I<package-name>. >>.
+
+This can be used as in the following example:
+
+ cat >> debian/foo.postinst <<EOF
+ # Script for #PACKAGE#
+ #TOKEN#
+ EOF
+ cat >> debian/bar.postinst <<EOF
+ # Script for #PACKAGE#
+ #TOKEN#
+ EOF
+ cat >> debian/baz.postinst <<EOF
+ # Script for #PACKAGE#
+ #TOKEN#
+ EOF
+ dh_installdeb -pfoo -pbar -pbaz --define TOKEN=default --define pkg.bar.TOKEN=unique-bar-value \
+ --define pkg.baz.TOKEN=unique-baz-value
+
+In this example, B<#TOKEN#> will expand to B<default> in F<debian/foo.postinst>,
+to B<unique-bar-value> in F<debian/bar.postinst> and to B<unique-baz-value>
+in F<debian/baz.postinst>.
+
+Note that the B<#pkg.*#> tokens will be visible in all scripts acted on. E.g.
+you can refer to B<#pkg.bar.TOKEN#> inside F<debian/foo.postinst> and it will
+be replaced by B<unique-bar-value>.
+
+=back
+
+=head1 SUBSTITUTION IN MAINTAINER SCRIPTS
+
+The B<dh_installdeb> will automatically replace the following tokens
+inside a provided maintainer script (if not replaced via B<-D>/B<--define>):
+
+=over 4
+
+=item #DEBHELPER#
+
+This token is by default replaced with generated shell snippets debhelper
+commands. This includes the snippets generated by
+B<dh_installdeb> from I<package>.maintscript file (if present).
+
+=item #DEB_HOST_I<NAME>#, #DEB_BUILD_I<NAME>#, #DEB_TARGET_I<NAME>#
+
+These tokens are replaced with the respective variable from
+L<dpkg-architecture(1)>. In almost all cases, you will want
+use the B<< #DEB_HOST_I<NAME> >> variant in a script to ensure
+you get the right value when cross-building.
+
+On a best effort, tokens of this pattern that do not match
+a variable in L<dpkg-architecture(1)> will be left as-is.
+
+=item #ENV.I<NAME>#
+
+These tokens of this form will be replaced with value of the
+corresponding environment variable. If the environment
+variable is unset, the token is replaced with the empty
+string.
+
+Note that there are limits on which names can be used (see
+L</Limitations in token names>).
+
+=item #PACKAGE#
+
+This token is by default replaced by the package name, which will contain
+the concrete script.
+
+=back
+
+=head2 Limitations in token names
+
+All tokens intended to be substituted must match the regex: #[A-Za-z0-9_.+]+#
+
+Tokens that do not match that regex will be silently ignored if found in the
+script template. Invalid token names passed to B<-D> or B<--define> will
+cause B<dh_installdeb> to reject the command with an error in most cases.
+
+=cut
+
+my %PROVIDED_SUBST;
+
+init(options => {
+ 'define|D=s%' => \%PROVIDED_SUBST,
+});
+
+# dpkg-maintscript-helper commands with their associated dpkg pre-dependency
+# versions.
+my %maintscript_predeps = (
+ "rm_conffile" => "",
+ "mv_conffile" => "",
+ "symlink_to_dir" => "",
+ "dir_to_symlink" => "",
+);
+my %maintscript_validator = (
+ "rm_conffile" => \&_validate_conffile_args,
+ "mv_conffile" => \&_validate_conffile_args,
+);
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ install_dir("$tmp/DEBIAN");
+
+ if (is_udeb($package)) {
+ # For udebs, only do the postinst, and no #DEBHELPER#.
+ # Udebs also support menutest and isinstallable scripts.
+ foreach my $script (qw{postinst menutest isinstallable}) {
+ my $f=pkgfile($package,$script);
+ if ($f) {
+ install_prog($f, "$tmp/DEBIAN/$script");
+ }
+ }
+
+ # stop here for udebs
+ next;
+ }
+
+ my $maintscriptfile=pkgfile($package, "maintscript");
+ my @special_conffiles_entries;
+ if ($maintscriptfile) {
+ if (compat(9)) {
+ foreach my $line (filedoublearray($maintscriptfile)) {
+ my $cmd=$line->[0];
+ error("unknown dpkg-maintscript-helper command: $cmd")
+ unless exists $maintscript_predeps{$cmd};
+ addsubstvar($package, "misc:Pre-Depends", "dpkg",
+ ">= $maintscript_predeps{$cmd}")
+ if length $maintscript_predeps{$cmd};
+ my $params=escape_shell(@$line);
+ foreach my $script (qw{postinst preinst prerm postrm}) {
+ autoscript($package, $script, "maintscript-helper",
+ "s!#PARAMS#!$params!g");
+ }
+ }
+ } else {
+ my @maintscripts = filedoublearray($maintscriptfile);
+ my @params;
+ foreach my $line (@maintscripts) {
+ my $cmd=$line->[0];
+ error("unknown dpkg-maintscript-helper command: $cmd")
+ unless exists $maintscript_predeps{$cmd};
+ addsubstvar($package, "misc:Pre-Depends", "dpkg",
+ ">= $maintscript_predeps{$cmd}")
+ if length $maintscript_predeps{$cmd};
+ if (my $validator = $maintscript_validator{$cmd}) {
+ $validator->($package, @{$line});
+ }
+ if (0) { # Disabled for now: #994919 + #994903
+ my $current_conffile = $line->[1];
+ push(@special_conffiles_entries, "remove-on-upgrade ${current_conffile}");
+ addsubstvar($package, "misc:Pre-Depends", "dpkg (>= 1.20.6~)");
+ next;
+ }
+ push(@params, escape_shell(@{$line}) );
+ }
+ if (@params) {
+ foreach my $script (qw{postinst preinst prerm postrm}) {
+ my $subst = sub {
+ my @res;
+ chomp;
+ for my $param (@params) {
+ my $line = $_;
+ $line =~ s{#PARAMS#}{$param}g;
+ push(@res, $line);
+ }
+ $_ = join("\n", @res) . "\n";
+ };
+ autoscript($package, $script, "maintscript-helper", $subst);
+ }
+ }
+ }
+ }
+
+ # Install debian scripts.
+ my $package_subst = debhelper_script_per_package_subst($package, \%PROVIDED_SUBST);
+ foreach my $script (qw{postinst preinst prerm postrm}) {
+ debhelper_script_subst($package, $script, $package_subst);
+ }
+
+ # Install non-executable files
+ my @non_exec_files;
+ # Removed in compat 12.
+ push(@non_exec_files, 'conffiles');
+ # In compat 10, we let dh_makeshlibs handle "shlibs".
+ push(@non_exec_files, 'shlibs') if compat(9);
+ foreach my $file (@non_exec_files) {
+ my $f=pkgfile($package,$file);
+ if ($f) {
+ install_file($f, "$tmp/DEBIAN/$file");
+ }
+ }
+
+ install_triggers($package, $tmp);
+ if (@special_conffiles_entries) {
+ open(my $fd, '>>', "$tmp/DEBIAN/conffiles");
+ for my $line (@special_conffiles_entries) {
+ print {$fd} "${line}\n";
+ }
+ close($fd);
+ }
+
+ # Automatic conffiles registration: If it is in /etc, it is a
+ # conffile.
+ if ( -d "$tmp/etc") {
+ complex_doit("find $tmp/etc -type f -printf '/etc/%P\n' | LC_ALL=C sort >> $tmp/DEBIAN/conffiles");
+ # Anything found?
+ if (-z "$tmp/DEBIAN/conffiles") {
+ rm_files("$tmp/DEBIAN/conffiles");
+ }
+ }
+
+ if ( -f "$tmp/DEBIAN/conffiles") {
+ reset_perm_and_owner(0644, "$tmp/DEBIAN/conffiles");
+ }
+}
+
+sub install_triggers {
+ my ($package, $tmp) = @_;
+ my $generated = generated_file($package, 'triggers', 0);
+ my @sources = grep { -f $_ } (
+ pkgfile($package, 'triggers'),
+ $generated,
+ );
+ my $target = "$tmp/DEBIAN/triggers";
+ return if not @sources;
+ if (@sources > 1) {
+ my $merged = "${generated}.merged";
+ open(my $ofd, '>', $merged)
+ or error("open ${target} failed: $!");
+ for my $src (@sources) {
+ open(my $ifd, '<', $src)
+ or error("open ${src} failed: $!");
+ print {$ofd} $_ while <$ifd>;
+ close($ifd);
+ }
+ close($ofd) or error("close ${merged} failed: $!");
+ @sources = ($merged);
+ }
+ install_file($sources[0], $target);
+}
+
+sub _validate_conffile_args {
+ my ($package, $cmd, @args) = @_;
+ my ($current_conffile, $new_conffile, $prior_version, $owning_package, $other);
+ for my $arg (@args) {
+ if ($arg eq '--') {
+ _maybe_error("The maintscripts file for $package includes a \"--\" for one of the ${cmd} commands, but it should not");
+ }
+ }
+ if ($cmd eq 'rm_conffile') {
+ ($current_conffile, $prior_version, $owning_package, $other) = @args;
+ } else {
+ ($current_conffile, $new_conffile, $prior_version, $owning_package, $other) = @args;
+ }
+ $current_conffile //= '';
+ _maybe_error("The current conffile path for ${cmd} must be present and absolute, got ${current_conffile}")
+ if not $current_conffile or substr($current_conffile, 0, 1) ne '/';
+ _maybe_error("The new conffile path for ${cmd} must be present and absolute, got ${new_conffile}")
+ if $cmd eq 'mv_conffile' and (not $new_conffile or substr($new_conffile, 0, 1) ne '/');
+
+ _maybe_error("The version for ${cmd} ${current_conffile} is not valid, got ${prior_version}")
+ if $prior_version and $prior_version !~ m{^${Debian::Debhelper::Dh_Lib::PKGVERSION_REGEX}$}o;
+ _maybe_error("The owning package for ${cmd} ${current_conffile} is not valid, got ${owning_package}")
+ if $owning_package and $owning_package !~ m{^${Debian::Debhelper::Dh_Lib::PKGNAME_REGEX}$}o;
+ if (defined($other)) {
+ warning("Too many arguments for ${cmd} ${current_conffile}");
+ }
+}
+
+sub _maybe_error {
+ my ($msg) = @_;
+ if (compat(11)) {
+ warning($msg);
+ } else {
+ error($msg);
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installdebconf b/dh_installdebconf
new file mode 100755
index 0000000..79d50d2
--- /dev/null
+++ b/dh_installdebconf
@@ -0,0 +1,243 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installdebconf - install files used by debconf in package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installdebconf> [S<I<debhelper options>>] [B<-n>] [S<B<--> I<params>>]
+
+=head1 DESCRIPTION
+
+B<dh_installdebconf> is a debhelper program that is responsible for installing
+files used by debconf into package build directories.
+
+It also automatically generates the F<postrm> commands needed to interface
+with debconf. The commands are added to the maintainer scripts by
+B<dh_installdeb>. See L<dh_installdeb(1)> for an explanation of how that
+works.
+
+Note that if you use debconf, your package probably needs to depend on it
+(it will be added to B<${misc:Depends}> by this program).
+
+Note that for your config script to be called by B<dpkg>, your F<postinst>
+needs to source debconf's confmodule. B<dh_installdebconf> does not
+install this statement into the F<postinst> automatically as it is too
+hard to do it right.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.config
+
+This is the debconf F<config> script, and is installed into the F<DEBIAN>
+directory in the package build directory.
+
+Inside the script, the token B<#DEBHELPER#> is replaced with
+shell script snippets generated by other debhelper commands.
+
+=item debian/I<package>.templates
+
+This is the debconf F<templates> file, and is installed into the F<DEBIAN>
+directory in the package build directory.
+
+=item F<debian/po/>
+
+If this directory is present, this program will automatically use
+L<po2debconf(1)> to generate merged templates
+files that include the translations from there.
+
+For this to work, your package should build-depend on F<po-debconf>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-n>, B<--no-scripts>
+
+Do not modify F<postrm> script.
+
+=item B<--> I<params>
+
+Pass the params to B<po2debconf>.
+
+=item B<-D>I<TOKEN=VALUE>, B<--define> I<TOKEN=VALUE>
+
+Define tokens to be replaced inside the maintainer scripts when
+it is generated. Please note that the limitations described in
+L</Limitations in token names> also applies to tokens defined
+on the command line. Invalid token names will trigger an error.
+
+In the simple case, this parameter will cause B<< #I<TOKEN># >>
+to be replaced by I<VALUE>. If I<VALUE> starts with a literal
+I<@>-sign, then I<VALUE> is expected to point to a file
+containing the actual value to insert.
+
+An explicit declared token with this parameter will replace built-in
+tokens.
+
+Test examples to aid with the understanding:
+
+ cat >> debian/config <<EOF
+ #SIMPLE#
+ #FILEBASED#
+ EOF
+ echo -n "Complex value" > some-file
+ dh_installdeb --define SIMPLE=direct --define FILEBASED=@some-file
+
+In this example, B<#SIMPLE#> will expand to B<direct> and B<#FILEBASED#>
+will expand to B<Complex value>.
+
+It is also possible to set package-specific values for a given
+token. This is useful when B<dh_installdebconf> is acting on multiple
+packages that need different values for the same token. This is
+done by prefixing the token name with B<< pkg.I<package-name>. >>.
+
+This can be used as in the following example:
+
+ cat >> debian/foo.config <<EOF
+ # Script for #PACKAGE#
+ #TOKEN#
+ EOF
+ cat >> debian/bar.config <<EOF
+ # Script for #PACKAGE#
+ #TOKEN#
+ EOF
+ cat >> debian/baz.config <<EOF
+ # Script for #PACKAGE#
+ #TOKEN#
+ EOF
+ dh_installdebconf -pfoo -pbar -pbaz --define TOKEN=default --define pkg.bar.TOKEN=unique-bar-value \
+ --define pkg.baz.TOKEN=unique-baz-value
+
+In this example, B<#TOKEN#> will expand to B<default> in F<debian/foo.config>,
+to B<unique-bar-value> in F<debian/bar.config> and to B<unique-baz-value>
+in F<debian/baz.config>.
+
+Note that the B<#pkg.*#> tokens will be visible in all scripts acted on. E.g.
+you can refer to B<#pkg.bar.TOKEN#> inside F<debian/foo.config> and it will
+be replaced by B<unique-bar-value>.
+
+=back
+
+=head1 SUBSTITUTION IN MAINTAINER SCRIPTS
+
+The B<dh_installdebconf> will automatically replace the following tokens
+inside a provided maintainer script (if not replaced via B<-D>/B<--define>):
+
+=over 4
+
+=item #DEB_HOST_I<NAME>#, #DEB_BUILD_I<NAME>#, #DEB_TARGET_I<NAME>#
+
+These tokens are replaced with the respective variable from
+L<dpkg-architecture(1)>. In almost all cases, you will want
+use the B<< #DEB_HOST_I<NAME> >> variant in a script to ensure
+you get the right value when cross-building.
+
+On a best effort, tokens of this pattern that do not match
+a variable in L<dpkg-architecture(1)> will be left as-is.
+
+=item #ENV.I<NAME>#
+
+These tokens of this form will be replaced with value of the
+corresponding environment variable. If the environment
+variable is unset, the token is replaced with the empty
+string.
+
+Note that there are limits on which names can be used (see
+L</Limitations in token names>).
+
+=item #PACKAGE#
+
+This token is by default replaced by the package name, which will contain
+the concrete script.
+
+=back
+
+=head2 Limitations in token names
+
+All tokens intended to be substituted must match the regex: #[A-Za-z0-9_.+]+#
+
+Tokens that do not match that regex will be silently ignored if found in the
+script template. Invalid token names passed to B<-D> or B<--define> will
+cause B<dh_installdebconf> to reject the command with an error in most cases.
+
+=cut
+
+my %PROVIDED_SUBST;
+
+init(options => {
+ 'define|D=s%' => \%PROVIDED_SUBST,
+});
+
+my @extraparams;
+if (defined($dh{U_PARAMS})) {
+ @extraparams=@{$dh{U_PARAMS}};
+}
+
+# PROMISE: DH NOOP WITHOUT config templates cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $config=pkgfile($package,"config");
+ my $templates=pkgfile($package,"templates");
+
+ install_dir("$tmp/DEBIAN");
+
+ if (! is_udeb($package)) {
+ # Install debian scripts.
+ my $package_subst = debhelper_script_per_package_subst($package, \%PROVIDED_SUBST);
+ debhelper_script_subst($package, "config", $package_subst);
+ }
+
+ if ($templates ne '') {
+ # Are there old-style translated templates?
+ if (glob("$templates.??"), glob("$templates.??_??")) {
+ warning "Ignoring debian/templates.ll files. Switch to po-debconf!";
+ }
+
+ umask(0022); # since I do a redirect below
+
+ if (-d "debian/po") {
+ complex_doit("po2debconf @extraparams $templates > $tmp/DEBIAN/templates");
+ }
+ else {
+ install_file($templates,"$tmp/DEBIAN/templates");
+ }
+ }
+
+ # I'm going with debconf 0.5 because it was the first
+ # "modern" one. udebs just need cdebconf.
+ my $debconfdep=is_udeb($package) ? "cdebconf-udeb" : "debconf (>= 0.5) | debconf-2.0";
+ if ($config ne '' || $templates ne '') {
+ addsubstvar($package, "misc:Depends", $debconfdep);
+ }
+
+ if (($config ne '' || $templates ne '') && ! $dh{NOSCRIPTS}) {
+ autoscript($package,"postrm","postrm-debconf");
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installdirs b/dh_installdirs
new file mode 100755
index 0000000..8d33b10
--- /dev/null
+++ b/dh_installdirs
@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installdirs - create subdirectories in package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installdirs> [S<I<debhelper options>>] [B<-A>] [B<--sourcedir=>I<dir>] [B<--create-in-sourcedir>] [S<I<dir> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installdirs> is a debhelper program that is responsible for creating
+subdirectories in package build directories.
+
+Many packages can get away with omitting the call to B<dh_installdirs>
+completely. Notably, other B<dh_*> commands are expected to create
+directories as needed.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.dirs
+
+Lists directories to be created in I<package>.
+
+Generally, there is no need to list directories created by the
+upstream build system or directories needed by other B<debhelper>
+commands.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-A>, B<--all>
+
+Create any directories specified by command line parameters in ALL packages
+acted on, not just the first.
+
+=item B<--create-in-sourcedir>, B<--no-create-in-sourcedir>
+
+Whether to create the specified directories in the source directory
+(usually F<debian/tmp>) I<in addition to> in the package build directory
+(usually F<< debian/I<package> >>).
+
+The default is B<--no-create-in-sourcedir>.
+
+=item B<--sourcedir=>I<dir>
+
+Consider I<dir> the source directory for the packages acted on instead
+of the default (which is usually F<debian/tmp>).
+
+Please note that this option is dependent on the
+B<--create-in-sourcedir> option (when B<--no-create-in-sourcedir> is
+in effect, this option does nothing in B<dh_installdirs>).
+
+=item I<dir> ...
+
+Create these directories in the package build directory of the first
+package acted on. (Or in all packages if B<-A> is specified.)
+
+=back
+
+=cut
+
+my $create_in_sourcedir = 0;
+
+init(options => {
+ 'sourcedir=s' => \$dh{SOURCEDIR},
+ 'create-in-sourcedir!' => \$create_in_sourcedir,
+});
+
+# PROMISE: DH NOOP WITHOUT dirs cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $file=pkgfile($package,"dirs");
+ my $srcdir = $dh{SOURCEDIR} // default_sourcedir($package);
+
+ install_dir($tmp) if compat(10);
+
+ my @dirs;
+
+ if ($file) {
+ @dirs=filearray($file)
+ }
+
+ if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) {
+ push @dirs, @ARGV;
+ }
+
+ if (@dirs) {
+ # Stick the $tmp onto the front of all the dirs.
+ # This is necessary, for 2 reasons, one to make them
+ # be in the right directory, but more importantly, it
+ # protects against the danger of absolute dirs being
+ # specified.
+ my @make_dirs;
+ push(@make_dirs, map {
+ my $dir = "$tmp/$_";
+ $dir =~ tr:/:/:s; # just beautification.
+ $dir;
+ } @dirs);
+ if ($create_in_sourcedir) {
+ push(@make_dirs, map {
+ my $dir = "${srcdir}/$_";
+ $dir =~ tr:/:/:s; # just beautification.
+ $dir;
+ } @dirs);
+ }
+
+ # Create dirs.
+ install_dir(@make_dirs);
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installdocs b/dh_installdocs
new file mode 100755
index 0000000..2e4529d
--- /dev/null
+++ b/dh_installdocs
@@ -0,0 +1,447 @@
+#!/usr/bin/perl
+
+=encoding UTF-8
+
+=head1 NAME
+
+dh_installdocs - install documentation into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installdocs> [S<I<debhelper options>>] [B<-A>] [B<-X>I<item>] [S<I<file> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installdocs> is a debhelper program that is responsible for installing
+documentation into F<usr/share/doc/package> in package build directories.
+
+In compat 10 and earlier, L<dh_install(1)> may be a better tool for handling
+the upstream documentation, when upstream's own build system installs all the desired documentation
+correctly. In this case, B<dh_installdocs> is still useful for installing
+packaging related documentation (e.g. the F<debian/copyright> file).
+
+From debhelper compatibility level 11 on, B<dh_install> will fall back to
+looking in F<debian/tmp> for files, if it does not find them in the current
+directory (or wherever you've told it to look using B<--sourcedir>).
+
+In compat 11 and later, B<dh_installdocs> offers many of the features that
+L<dh_install(1)> also has. Furthermore, B<dh_installdocs> also supports
+the B<nodoc> build profile to exclude documentation (regardless of compat
+level).
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.docs
+
+List documentation files to be installed into I<package>.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=item F<debian/copyright>
+
+The copyright file is installed into all packages, unless a more
+specific copyright file is available.
+
+=item debian/I<package>.copyright
+
+=item debian/I<package>.README.Debian
+
+=item debian/I<package>.TODO
+
+Each of these files is automatically installed if present for a
+I<package>.
+
+=item F<debian/README.Debian>
+
+=item F<debian/TODO>
+
+These files are installed into the first binary package listed in
+debian/control.
+
+Note that F<README.debian> files are also installed as F<README.Debian>,
+and F<TODO> files will be installed as F<TODO.Debian> in non-native packages.
+
+=item debian/I<package>.doc-base
+
+Installed as doc-base control files. Note that the doc-id will be
+determined from the B<Document:> entry in the doc-base control file in
+question. In the event that multiple doc-base files in a single source
+package share the same doc-id, they will be installed to
+usr/share/doc-base/package instead of usr/share/doc-base/doc-id.
+
+=item debian/I<package>.doc-base.*
+
+If your package needs to register more than one document, you need
+multiple doc-base files, and can name them like this. In the event
+that multiple doc-base files of this style in a single source package
+share the same doc-id, they will be installed to
+usr/share/doc-base/package-* instead of usr/share/doc-base/doc-id.
+
+Please be aware that this deduplication is currently done in memory
+only, so for now it requires B<dh_installdocs> to be called no more
+than once during the package build. Calling B<dh_installdocs
+-p>I<package> in combination with using
+F<debian/>I<package>F<.doc-base.*> files can lead to uninstallable
+packages. See L<https://bugs.debian.org/980903> for details.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-A>, B<--all>
+
+Install all files specified by command line parameters in ALL packages
+acted on.
+
+=item B<-X>I<item>, B<--exclude=>I<item>
+
+Exclude files that contain I<item> anywhere in their filename from
+being installed. Note that this includes doc-base files.
+
+=item B<--sourcedir=>I<dir>
+
+Look in the specified directory for files to be installed. This option
+requires compat 11 or later (it is silently ignored in compat 10 or earlier).
+
+Note that this is not the same as the B<--sourcedirectory> option used
+by the B<dh_auto_>I<*> commands. You rarely need to use this option, since
+B<dh_installman> automatically looks for files in F<debian/tmp> in debhelper
+compatibility level 11 and above.
+
+=item B<--doc-main-package=>I<main-package>
+
+Set the main package for a documentation package. This is used to
+install the documentation of the documentation package in F<<
+/usr/share/doc/I<main-package> >> as recommended by the Debian policy
+manual 3.9.7 in §12.3.
+
+In compat 11 (or later), this option is only useful if debhelper's
+auto-detection of the main package is wrong. The option can also be
+used to silence a warning from debhelper when the auto-detection fails
+but the default happens to be correct.
+
+This option cannot be used when B<dh_installdocs> is instructed to act
+on multiple packages. If you need this option, you will generally
+need to combine it with B<-p> to ensure exactly one package is acted
+on.
+
+Please keep in mind that some documentation (the copyright file,
+README.Debian, etc.) will be unaffected by this option.
+
+=item B<--link-doc=>I<package>
+
+Make the documentation directory of all packages acted on be a symlink to
+the documentation directory of I<package>. This has no effect when acting on
+I<package> itself, or if the documentation directory to be created already
+exists when B<dh_installdocs> is run. To comply with policy, I<package> must
+be a binary package that comes from the same source package.
+
+debhelper will try to avoid installing files into linked documentation
+directories that would cause conflicts with the linked package. The B<-A>
+option will have no effect on packages with linked documentation
+directories, and F<copyright>, F<changelog>, F<README.Debian>, and F<TODO> files will
+not be installed.
+
+(An older method to accomplish the same thing, which is still supported,
+is to make the documentation directory of a package be a dangling symlink,
+before calling B<dh_installdocs>.)
+
+Please note that this option only applies to the documentation
+directory for the package itself. When the package ships
+documentation for another package (e.g. see B<--doc-main-package>), it
+will not use a symlink for the documentation of the other package.
+
+
+B<CAVEAT 1>: If a previous version of the package was built without this
+option and is now built with it (or vice-versa), it requires a "dir to
+symlink" (or "symlink to dir") migration. Since debhelper has no
+knowledge of previous versions, you have to enable this migration
+itself.
+
+This can be done by providing a "debian/I<package>.maintscript" file
+and using L<dh_installdeb(1)> to provide the relevant maintainer
+script snippets.
+
+B<CAVEAT 2>: The use of B<--link-doc> should only be done when the
+packages have same "architecture" type. A link from an architecture
+independent package to an architecture dependent package (or vice
+versa) will not work. Since compat 10, debhelper will actively reject
+unsupported combinations.
+
+=item I<file> ...
+
+Install these files as documentation into the first package acted on. (Or
+in all packages if B<-A> is specified).
+
+=back
+
+=head1 EXAMPLES
+
+This is an example of a F<debian/package.docs> file:
+
+ README
+ TODO
+ debian/notes-for-maintainers.txt
+ docs/manual.txt
+ docs/manual.pdf
+ docs/manual-html/
+
+=head1 NOTES
+
+Note that B<dh_installdocs> will happily copy entire directory hierarchies if
+you ask it to (similar to B<cp -a>). If it is asked to install a
+directory, it will install the complete contents of the directory.
+
+=cut
+
+my %docdir_created;
+# Create documentation directories on demand. This allows us to use dangling
+# symlinks for linked documentation directories unless additional files need
+# to be installed.
+sub ensure_docdir {
+ my $package=shift;
+ return if $docdir_created{$package};
+ my $tmp=tmpdir($package);
+
+ my $target;
+ if ($dh{LINK_DOC} && $dh{LINK_DOC} ne $package) {
+ $target="$tmp/usr/share/doc/$dh{LINK_DOC}";
+ }
+ else {
+ $target="$tmp/usr/share/doc/$package";
+ }
+
+ # If this is a symlink, leave it alone.
+ if (! -d $target && ! -l $target) {
+ install_dir($target);
+ }
+ $docdir_created{$package}=1;
+}
+
+init(options => {
+ "link-doc=s" => \$dh{LINK_DOC},
+ "sourcedir=s" => \$dh{SOURCEDIR},
+ 'doc-main-package=s' => \$dh{DOC_MAIN_PACKAGE},
+});
+
+my $called_getpackages = 0;
+my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject;
+my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0;
+# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled
+# or dh_missing might make noise.
+$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs;
+
+if (@{$dh{DOPACKAGES}} > 1 and $dh{DOC_MAIN_PACKAGE}) {
+ error('--doc-main-package should be used with -p<doc-pkg>');
+}
+
+if ($dh{DOC_MAIN_PACKAGE}) {
+ assert_opt_is_known_package($dh{DOC_MAIN_PACKAGE}, '--doc-main-package');
+}
+
+foreach my $package (getpackages()) {
+ next if is_udeb($package);
+
+ my $tmp=tmpdir($package);
+ my $file=pkgfile($package,"docs");
+ my $link_doc=($dh{LINK_DOC} && $dh{LINK_DOC} ne $package);
+ my $skip_install = process_pkg($package) ? 0 : 1;
+ my @search_dirs = ('.');
+ my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler;
+ @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10);
+
+ if (not $skip_install) {
+ if ($link_doc) {
+ getpackages('both') unless $called_getpackages++;
+
+ if (package_binary_arch($package) ne package_binary_arch($dh{LINK_DOC})) {
+ if (compat(9)) {
+ warning("WARNING: --link-doc between architecture all and not all packages breaks binNMUs");
+ } else {
+ error("--link-doc not allowed between ${package} and $dh{LINK_DOC} (one is arch:all and the other not)");
+ }
+ }
+ # Make sure that the parent directory exists.
+ if (!-d "$tmp/usr/share/doc" && !-l "$tmp/usr/share/doc") {
+ install_dir("$tmp/usr/share/doc");
+ }
+ # Create symlink to another documentation directory if
+ # necessary.
+ if (!-d "$tmp/usr/share/doc/$package" &&
+ !-l "$tmp/usr/share/doc/$package") {
+ make_symlink_raw_target($dh{LINK_DOC}, "$tmp/usr/share/doc/$package");
+ # Policy says that if you make your documentation
+ # directory a symlink, then you have to depend on
+ # the target.
+ addsubstvar($package, 'misc:Depends', "$dh{LINK_DOC} (= \${binary:Version})");
+ }
+ } else {
+ ensure_docdir($package);
+ }
+ }
+
+ my @docs;
+
+ if ($file) {
+ @docs = filearray($file, \@search_dirs, $error_handler);
+ }
+
+ if (($package eq $dh{FIRSTPACKAGE} || ($dh{PARAMS_ALL} && !$link_doc)) && @ARGV) {
+ push @docs, @ARGV;
+ }
+
+ log_installed_files($package, @docs);
+
+ next if $skip_install;
+
+ if (not $nodocs and @docs) {
+ my $exclude = ' -and ! -empty';
+ my $target_package = compute_doc_main_package($package);
+ if (not defined($target_package)) {
+ warning("Cannot auto-detect main package for ${package}. If the default is wrong, please use --doc-main-package");
+ $target_package = $package;
+ } elsif ($dh{PARAMS_ALL} and $package ne $target_package and not $dh{DOC_MAIN_PACKAGE}) {
+ warning("Not using auto-detected $target_package as main doc package for $package: With -A/--all, this would cause file-conflicts.");
+ $target_package = $package;
+ }
+ if ($dh{EXCLUDE_FIND}) {
+ $exclude .= ' -and ! \( '.$dh{EXCLUDE_FIND}.' \)';
+ }
+ my $target_dir = "${tmp}/usr/share/doc/${target_package}";
+ install_dir($target_dir) unless -l $target_dir;
+
+ foreach my $doc (@docs) {
+ next if excludefile($doc);
+ next if -f $doc && ! -s _; # ignore empty files
+ ensure_docdir($package);
+ if (-d $doc && length $exclude) {
+ my $basename = basename($doc);
+ my $dir = ($basename eq '.') ? $doc : "$doc/..";
+ my $pwd=`pwd`;
+ chomp $pwd;
+ # Gracefully handling tmpdir being absolute (-P/...)
+ my $docdir = $target_dir =~ m{^/} ? $target_dir : "${pwd}/${target_dir}";
+ complex_doit("cd '$dir' && " .
+ "find '$basename' \\( -type f -or -type l \\)$exclude -print0 | LC_ALL=C sort -z | " .
+ "xargs -0 -I {} cp --reflink=auto --parents -dp {} $docdir");
+ }
+ else {
+ doit("cp", '--reflink=auto', "-a", $doc, $target_dir);
+ }
+ }
+ doit("chown","-R","0:0","$tmp/usr/share/doc") if should_use_root();
+ doit("chmod","-R","u+rw,go=rX","$tmp/usr/share/doc");
+ }
+
+ # .Debian is correct, according to policy, but I'm easy.
+ my $readme_debian=pkgfile($package,'README.Debian');
+ if (! $readme_debian) {
+ $readme_debian=pkgfile($package,'README.debian');
+ }
+ if (! $link_doc && $readme_debian && ! excludefile($readme_debian)) {
+ ensure_docdir($package);
+ install_file($readme_debian,
+ "$tmp/usr/share/doc/$package/README.Debian");
+ }
+
+ my $todo=pkgfile($package,'TODO');
+ if (! $link_doc && $todo && ! excludefile($todo)) {
+ ensure_docdir($package);
+ if (isnative($package)) {
+ install_file($todo, "$tmp/usr/share/doc/$package/TODO");
+ }
+ else {
+ install_file($todo,
+ "$tmp/usr/share/doc/$package/TODO.Debian");
+ }
+ }
+
+ # If the "directory" is a dangling symlink, then don't install
+ # the copyright file. This is useful for multibinary packages
+ # that share a doc directory.
+ if (! $link_doc && (! -l "$tmp/usr/share/doc/$package" || -d "$tmp/usr/share/doc/$package")) {
+ # Support debian/package.copyright, but if not present, fall
+ # back on debian/copyright for all packages, not just the
+ # main binary package.
+ my $copyright=pkgfile($package,'copyright');
+ if (! $copyright && -e "debian/copyright") {
+ $copyright="debian/copyright";
+ }
+ if ($copyright && ! excludefile($copyright)) {
+ ensure_docdir($package);
+ install_file($copyright,
+ "$tmp/usr/share/doc/$package/copyright");
+ }
+ }
+
+ next if $nodocs;
+
+ # Handle doc-base files. There are two filename formats, the usual
+ # plus an extended format (debian/package.*).
+ my %doc_ids;
+
+ opendir(DEB,"debian/") || error("can't read debian directory: $!");
+ # If this is the main package, we need to handle unprefixed filenames.
+ # For all packages, we must support both the usual filename format plus
+ # that format with a period an something appended.
+ my $regexp="\Q$package\E\.";
+ if ($package eq $dh{MAINPACKAGE}) {
+ $regexp="(|$regexp)";
+ }
+ foreach my $fn (grep {/^${regexp}doc-base(\..*)?$/} readdir(DEB)) {
+ # .EX are example files, generated by eg, dh-make
+ next if $fn=~/\.EX$/;
+ next if excludefile($fn);
+ # Parse the file to get the doc id.
+ open(my $fd, '<', "debian/$fn") || die "Cannot read debian/$fn.";
+ while (<$fd>) {
+ s/\s*$//;
+ if (/^Document\s*:\s*(.*)/) {
+ $doc_ids{$fn}=$1;
+ last;
+ }
+ }
+ if (! exists $doc_ids{$fn}) {
+ warning("Could not parse $fn for doc-base Document id; skipping");
+ }
+ close($fd);
+ }
+ closedir(DEB);
+
+ if (%doc_ids) {
+ install_dir("$tmp/usr/share/doc-base/");
+ }
+ foreach my $fn (keys %doc_ids) {
+ # To avoid issues with duplicated document IDs, we will always
+ # install to usr/share/doc-base/<packagename>.<doc_id> instead
+ # of just usr/share/doc-base/<packagename> or just
+ # usr/share/doc-base/<doc_id>. See #525821 and #980903.
+ install_file("debian/$fn",
+ "$tmp/usr/share/doc-base/$package.$doc_ids{$fn}");
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installemacsen b/dh_installemacsen
new file mode 100755
index 0000000..bae900a
--- /dev/null
+++ b/dh_installemacsen
@@ -0,0 +1,149 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installemacsen - register an Emacs add on package
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installemacsen> [S<I<debhelper options>>] [B<-n>] [B<--priority=>I<n>] [B<--flavor=>I<foo>]
+
+=head1 DESCRIPTION
+
+B<dh_installemacsen> is a debhelper program that is responsible for installing
+files used by the Debian B<emacsen-common> package into package build
+directories.
+
+It also automatically generates the F<preinst> F<postinst> and F<prerm>
+commands needed to register a package as an Emacs add on package. The commands
+are added to the maintainer scripts by B<dh_installdeb>. See
+L<dh_installdeb(1)> for an explanation of how this works.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.emacsen-compat
+
+Installed into F<usr/lib/emacsen-common/packages/compat/package> in the
+package build directory.
+
+=item debian/I<package>.emacsen-install
+
+Installed into F<usr/lib/emacsen-common/packages/install/package> in the
+package build directory.
+
+=item debian/I<package>.emacsen-remove
+
+Installed into F<usr/lib/emacsen-common/packages/remove/package> in the
+package build directory.
+
+=item debian/I<package>.emacsen-startup
+
+Installed into etc/emacs/site-start.d/50I<package>.el in the package
+build directory. Use B<--priority> to use a different priority than 50.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-n>, B<--no-scripts>
+
+Do not modify F<postinst>/F<prerm> scripts.
+
+=item B<--priority=>I<n>
+
+Sets the priority number of a F<site-start.d> file. Default is 50.
+
+=item B<--flavor=>I<foo>
+
+Sets the flavor a F<site-start.d> file will be installed in. Default is
+B<emacs>, alternatives include B<xemacs> and B<emacs20>.
+
+=back
+
+=head1 NOTES
+
+Note that this command is not idempotent. L<dh_prep(1)> should be called
+between invocations of this command. Otherwise, it may cause multiple
+instances of the same text to be added to maintainer scripts.
+
+=cut
+
+init(options => {
+ "flavor=s" => \$dh{FLAVOR},
+ "priority=s" => \$dh{PRIORITY},
+});
+
+if (! defined $dh{PRIORITY}) {
+ $dh{PRIORITY}=50;
+}
+if (! defined $dh{FLAVOR}) {
+ $dh{FLAVOR}='emacs';
+}
+
+# PROMISE: DH NOOP WITHOUT emacsen-common emacsen-install emacsen-remove emacsen-startup cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ my $emacsen_compat=pkgfile($package,"emacsen-compat");
+ my $emacsen_install=pkgfile($package,"emacsen-install");
+ my $emacsen_remove=pkgfile($package,"emacsen-remove");
+ my $emacsen_startup=pkgfile($package,"emacsen-startup");
+
+ if ($emacsen_compat ne '') {
+ install_dir("$tmp/usr/lib/emacsen-common/packages/compat");
+ install_file($emacsen_compat,
+ "$tmp/usr/lib/emacsen-common/packages/compat/$package");
+ }
+
+ if ($emacsen_install ne '') {
+ install_dir("$tmp/usr/lib/emacsen-common/packages/install");
+ install_prog($emacsen_install,"$tmp/usr/lib/emacsen-common/packages/install/$package");
+ }
+
+ if ($emacsen_remove ne '') {
+ install_dir("$tmp/usr/lib/emacsen-common/packages/remove");
+ install_prog("$emacsen_remove","$tmp/usr/lib/emacsen-common/packages/remove/$package");
+ }
+
+ if ($emacsen_startup ne '') {
+ install_dir("$tmp/etc/$dh{FLAVOR}/site-start.d/");
+ install_file($emacsen_startup,"$tmp/etc/$dh{FLAVOR}/site-start.d/$dh{PRIORITY}$package.el");
+ }
+
+ if ($emacsen_install ne '' || $emacsen_remove ne '') {
+ if (! $dh{NOSCRIPTS}) {
+ autoscript($package,"preinst","preinst-emacsen",
+ { 'PACKAGE' => $package });
+ autoscript($package,"postinst","postinst-emacsen",
+ { 'PACKAGE' => $package });
+ autoscript($package,"prerm","prerm-emacsen",
+ { 'PACKAGE' => $package });
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+L</usr/share/doc/emacsen-common/debian-emacs-policy.gz>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installexamples b/dh_installexamples
new file mode 100755
index 0000000..ce970ac
--- /dev/null
+++ b/dh_installexamples
@@ -0,0 +1,192 @@
+#!/usr/bin/perl
+
+=encoding UTF-8
+
+=head1 NAME
+
+dh_installexamples - install example files into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installexamples> [S<I<debhelper options>>] [B<-A>] [B<-X>I<item>] [S<I<file> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installexamples> is a debhelper program that is responsible for
+installing examples into F<usr/share/doc/package/examples> in package
+build directories.
+
+From debhelper compatibility level 11 on, B<dh_install> will fall back to
+looking in F<debian/tmp> for files, if it does not find them in the current
+directory (or wherever you've told it to look using B<--sourcedir>).
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.examples
+
+Lists example files or directories to be installed.
+
+If upstream provides an F<examples> directory, you will often want to use B<examples/*> rather
+than B<examples> in this file. The latter would create
+F<< /usr/share/doc/I<package>/examples/examples >>, which is rarely what you want.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-A>, B<--all>
+
+Install any files specified by command line parameters in ALL packages
+acted on.
+
+=item B<--sourcedir=>I<dir>
+
+Look in the specified directory for files to be installed. This option
+requires compat 11 or later (it is silently ignored in compat 10 or earlier).
+
+Note that this is not the same as the B<--sourcedirectory> option used
+by the B<dh_auto_>I<*> commands. You rarely need to use this option, since
+B<dh_installexamples> automatically looks for files in F<debian/tmp> in debhelper
+compatibility level 11 and above.
+
+=item B<--doc-main-package=>I<main-package>
+
+Set the main package for a documentation package. This is used to
+install the documentation of the documentation package in F<<
+/usr/share/doc/I<main-package> >> as recommended by the Debian policy
+manual 3.9.7 in §12.3.
+
+In compat 11 (or later), this option is only useful if debhelper's
+auto-detection of the main package is wrong. The option can also be
+used to silence a warning from debhelper when the auto-detection fails
+but the default happens to be correct.
+
+This option cannot be used when B<dh_installexamples> is instructed to act
+on multiple packages. If you need this option, you will generally
+need to combine it with B<-p> to ensure exactly one package is acted
+on.
+
+=item B<-X>I<item>, B<--exclude=>I<item>
+
+Exclude files that contain I<item> anywhere in their filename from
+being installed.
+
+=item I<file> ...
+
+Install these files (or directories) as examples into the first package
+acted on. (Or into all packages if B<-A> is specified.)
+
+=back
+
+=head1 NOTES
+
+Note that B<dh_installexamples> will happily copy entire directory hierarchies
+if you ask it to (similar to B<cp -a>). If it is asked to install a
+directory, it will install the complete contents of the directory.
+
+=cut
+
+init(options => {
+ "sourcedir=s" => \$dh{SOURCEDIR},
+ 'doc-main-package=s' => \$dh{DOC_MAIN_PACKAGE},
+});
+
+# PROMISE: DH NOOP WITHOUT pkgfile-logged(examples) cli-options()
+
+my $pwd;
+my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject;
+my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0;
+# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled
+# or dh_missing might make noise.
+$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs;
+
+if (@{$dh{DOPACKAGES}} > 1 and $dh{DOC_MAIN_PACKAGE}) {
+ error('--doc-main-package should be used with -p<doc-pkg>');
+}
+if ($dh{DOC_MAIN_PACKAGE}) {
+ assert_opt_is_known_package($dh{DOC_MAIN_PACKAGE}, '--doc-main-package');
+}
+
+foreach my $package (getpackages()) {
+ next if is_udeb($package);
+
+ my $tmp=tmpdir($package);
+ my $file=pkgfile($package,"examples");
+ my @search_dirs = ('.');
+ my $skip_install = process_pkg($package) ? 0 : 1;
+ my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler;
+ @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10);
+
+ my @examples;
+
+ if ($file) {
+ @examples = filearray($file, \@search_dirs, $error_handler) if $file;
+ }
+
+ if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) {
+ push @examples, @ARGV;
+ }
+
+ log_installed_files($package, @examples);
+
+ next if $skip_install or $nodocs;
+
+ if (@examples) {
+ my $target_package = compute_doc_main_package($package);
+ if (not defined($target_package)) {
+ warning("Cannot auto-detect main package for ${package}. If the default is wrong, please use --doc-main-package");
+ $target_package = $package;
+ }
+ my $target_dir = "${tmp}/usr/share/doc/${target_package}/examples";
+ install_dir($target_dir);
+
+ my $exclude = '';
+ if ($dh{EXCLUDE_FIND}) {
+ $exclude .= ' -and ! \( '.$dh{EXCLUDE_FIND}.' \)';
+ }
+
+ foreach my $example (@examples) {
+ next if excludefile($example);
+ if (-d $example && $exclude) {
+ my $basename = basename($example);
+ my $dir = ($basename eq '.') ? $example : "$example/..";
+ chomp($pwd=`pwd`) if not defined($pwd);
+ # Gracefully handling tmpdir being absolute (-P/...)
+ my $destdir = $target_dir =~ m{^/} ? $target_dir : "${pwd}/${target_dir}";
+ complex_doit("cd '$dir' && " .
+ "find '$basename' -type f$exclude -print0 | LC_ALL=C sort -z | " .
+ "xargs -0 -I {} cp --reflink=auto --parents -dp {} ${destdir}");
+ }
+ else {
+ doit("cp", '--reflink=auto', "-a", $example, $target_dir);
+ }
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installgsettings b/dh_installgsettings
new file mode 100755
index 0000000..45b228e
--- /dev/null
+++ b/dh_installgsettings
@@ -0,0 +1,106 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installgsettings - install GSettings overrides and set dependencies
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installgsettings> [S<I<debhelper options>>] [B<--priority=<number>>]
+
+=head1 DESCRIPTION
+
+B<dh_installgsettings> is a debhelper program that is responsible for installing
+GSettings override files and generating appropriate dependencies on the
+GSettings backend.
+
+The dependency on the backend will be generated in B<${misc:Depends}>.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.gsettings-override
+
+Installed into usr/share/glib-2.0/schemas/10_I<package>.gschema.override in
+the package build directory, with "I<package>" replaced by the package name.
+
+The format of the file is the following:
+
+ [org.gnome.mypackage]
+ boolean-setting=true
+ string-setting='string'
+ ...
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--priority> I<priority>
+
+Use I<priority> (which should be a 2-digit number) as the override
+priority instead of 10. Higher values than ten can be used by
+derived distributions (20), blend distributions (50), or site-specific
+packages (90).
+
+=cut
+
+init(options => {
+ "priority=s" => \$dh{PRIORITY},
+});
+
+my $priority=10;
+if (defined $dh{PRIORITY}) {
+ $priority=$dh{PRIORITY};
+}
+
+# PROMISE: DH NOOP WITHOUT gsettings-override tmp(usr/share/glib-2.0/schemas) cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ my $gsettings_schemas_dir = "$tmp/usr/share/glib-2.0/schemas/";
+
+ my $override = pkgfile($package,"gsettings-override");
+ if ($override ne '') {
+ install_dir($gsettings_schemas_dir);
+ install_file($override,
+ "$gsettings_schemas_dir/${priority}_$package.gschema.override");
+ }
+
+ if (-d "$gsettings_schemas_dir") {
+ # Get a list of the schemas
+ my $schemas = qx_cmd('find', $gsettings_schemas_dir, '-type', 'f',
+ '(', '-name', '*.xml', '-o', '-name', '*.override',
+ ')', '-printf', '%P');
+ if ($schemas ne '') {
+ addsubstvar($package, "misc:Depends", "dconf-gsettings-backend | gsettings-backend");
+ }
+ }
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Laurent Bigonville <bigon@debian.org>,
+Josselin Mouette <joss@debian.org>
+
+=cut
+
diff --git a/dh_installifupdown b/dh_installifupdown
new file mode 100755
index 0000000..3d4cd93
--- /dev/null
+++ b/dh_installifupdown
@@ -0,0 +1,82 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installifupdown - install if-up and if-down hooks
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installifupdown> [S<I<debhelper options>>] [B<--name=>I<name>]
+
+=head1 DESCRIPTION
+
+B<dh_installifupdown> is a debhelper program that is responsible for installing
+F<if-up>, F<if-down>, F<if-pre-up>, and F<if-post-down> hook scripts into package build
+directories.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.if-up
+
+=item debian/I<package>.if-down
+
+=item debian/I<package>.if-pre-up
+
+=item debian/I<package>.if-post-down
+
+These files are installed into etc/network/if-*.d/I<package> in
+the package build directory.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+Look for files named F<debian/package.name.if-*> and install them as
+F<etc/network/if-*/name>, instead of using the usual files and installing them
+as the package name.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT if-pre-up if-up if-down if-post-down cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ foreach my $script (qw(pre-up up down post-down)) {
+ my $file=pkgfile($package, "if-$script");
+ if ($file ne '') {
+ install_dir("$tmp/etc/network/if-$script.d");
+ install_prog($file,"$tmp/etc/network/if-$script.d/".pkgfilename($package));
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installinfo b/dh_installinfo
new file mode 100755
index 0000000..3a25542
--- /dev/null
+++ b/dh_installinfo
@@ -0,0 +1,133 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installinfo - install info files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installinfo> [S<I<debhelper options>>] [B<-A>] [S<I<file> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installinfo> is a debhelper program that is responsible for installing
+info files into F<usr/share/info> in the package build directory.
+
+From debhelper compatibility level 11 on, B<dh_install> will fall back to
+looking in F<debian/tmp> for files, if it does not find them in the current
+directory (or wherever you've told it to look using B<--sourcedir>).
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.info
+
+List info files to be installed.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-A>, B<--all>
+
+Install all files specified by command line parameters in ALL packages
+acted on.
+
+=item B<--sourcedir=>I<dir>
+
+Look in the specified directory for files to be installed. This option
+requires compat 11 or later (it is silently ignored in compat 10 or earlier).
+
+Note that this is not the same as the B<--sourcedirectory> option used
+by the B<dh_auto_>I<*> commands. You rarely need to use this option, since
+B<dh_installinfo> automatically looks for files in F<debian/tmp> in debhelper
+compatibility level 11 and above.
+
+=item I<file> ...
+
+Install these info files into the first package acted on. (Or in
+all packages if B<-A> is specified).
+
+=back
+
+=cut
+
+init(options => {
+ "sourcedir=s" => \$dh{SOURCEDIR},
+});
+
+# PROMISE: DH NOOP WITHOUT pkgfile-logged(info) cli-options() tmp(usr/share/info/dir)
+
+my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject;
+my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0;
+# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled
+# or dh_missing might make noise.
+$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs;
+
+foreach my $package (getpackages()) {
+ my $tmp=tmpdir($package);
+ my $file=pkgfile($package,"info");
+ my @search_dirs = ('.');
+ my @ignored_files;
+ my $skip_install = process_pkg($package) ? 0 : 1;
+ my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler;
+ @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10);
+
+ my @info;
+
+ if ($file) {
+ @info = filearray($file, \@search_dirs, $error_handler) if $file;
+ }
+
+ if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) {
+ push @info, @ARGV;
+ }
+
+ # Pretend that we handled usr/share/info/dir. We do not want to install it as it causes
+ # file conflicts between packages and we also do not want dh_missing to drop a bomb on
+ # the user for it.
+ #
+ # See #971036
+ for my $path (@search_dirs) {
+ if ( -e "${path}/usr/share/info/dir" ) {
+ push(@ignored_files, 'usr/share/info/dir');
+ last;
+ }
+ }
+
+ log_installed_files($package, @info, @ignored_files);
+
+ next if $skip_install or $nodocs;
+
+ if (@info) {
+ install_dir("$tmp/usr/share/info");
+ xargs(\@info, "cp", '--reflink=auto', XARGS_INSERT_PARAMS_HERE, "$tmp/usr/share/info");
+ doit("chmod","-R", "u+rw,go=rX","$tmp/usr/share/info/");
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installinit b/dh_installinit
new file mode 100755
index 0000000..13df432
--- /dev/null
+++ b/dh_installinit
@@ -0,0 +1,427 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installinit - install service init files into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+use File::Find;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installinit> [S<I<debhelper options>>] [B<--name=>I<name>] [B<-n>] [B<-R>] [B<-r>] [B<-d>] [S<B<--> I<params>>]
+
+=head1 DESCRIPTION
+
+B<dh_installinit> is a debhelper program that is responsible for
+installing init scripts with associated defaults files. In
+compatibility levels up to and including 10, B<dh_installinit> will
+also install some systemd related files provided by the debian
+packaging (see the L</FILES> section below). In compatibility levels
+up to and including 11, B<dh_installinit> will also handle upstart
+jobs provided in the debian packaging (see the L</FILES> for more
+information on this as well).
+
+It also automatically generates the F<postinst> and F<postrm> and F<prerm>
+commands needed to set up the symlinks in F</etc/rc*.d/> to start and stop
+the init scripts.
+
+In compat 10 or earlier: If a package only ships a systemd service
+file and no sysvinit script is provided, you may want to exclude the
+call to dh_installinit for that package (e.g. via B<-N>). Otherwise,
+you may get warnings from lintian about init.d scripts not being
+included in the package.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.init
+
+If this exists, it is installed into etc/init.d/I<package> in the package
+build directory.
+
+=item debian/I<package>.default
+
+If this exists, it is installed into etc/default/I<package> in the package
+build directory.
+
+=item debian/I<package>.upstart
+
+In compatibility level 11, this file will trigger an error with a reminder
+about ensuring the proper removal of the upstart file in the previous package
+version. Please consider using the "rm_conffile" feature from
+L<dh_installdeb(1)> to ensure the proper removal of previous upstart files.
+
+In compatibility level 10, if this file exists, it is installed into
+etc/init/I<package>.conf in the package build directory.
+
+=item debian/I<package>.service
+
+If this exists, it is installed into F<< lib/systemd/system/I<package>.service >> in
+the package build directory. Only used in compat levels 10 and below.
+
+=item debian/I<package>.tmpfile
+
+If this exists, it is installed into usr/lib/tmpfiles.d/I<package>.conf in the
+package build directory. Only used in compat levels 10 and below.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-n>, B<--no-scripts>
+
+Do not modify F<postinst>/F<postrm>/F<prerm> scripts.
+
+=item B<-o>, B<--only-scripts>
+
+Only modify F<postinst>/F<postrm>/F<prerm> scripts, do not actually install
+any init script, default files, upstart job or systemd service file. May be
+useful if the file is shipped and/or installed by upstream in a way that
+doesn't make it easy to let B<dh_installinit> find it.
+
+B<Caveat>: This will bypass all the regular checks and
+I<unconditionally> modify the scripts. You will almost certainly want
+to use this with B<-p> to limit, which packages are affected by the
+call. Example:
+
+ override_dh_installinit:
+ dh_installinit -pfoo --only-scripts
+ dh_installinit --remaining
+
+=item B<-R>, B<--restart-after-upgrade>
+
+Do not stop the init script until after the package upgrade has been
+completed. This is the default behaviour in compat 10.
+
+In early compat levels, the default was to stop the script in the
+F<prerm>, and starts it again in the F<postinst>.
+
+This can be useful for daemons that should not have a possibly long
+downtime during upgrade. But you should make sure that the daemon will not
+get confused by the package being upgraded while it's running before using
+this option.
+
+=item B<--no-restart-after-upgrade>
+
+Undo a previous B<--restart-after-upgrade> (or the default of compat
+10). If no other options are given, this will cause the service to be
+stopped in the F<prerm> script and started again in the F<postinst>
+script.
+
+=item B<-r>, B<--no-stop-on-upgrade>, B<--no-restart-on-upgrade>
+
+Do not stop init script on upgrade. This has the side-effect of not
+restarting the service as a part of the upgrade.
+
+If you want to restart the service with minimal downtime, please use
+B<--restart-after-upgrade> (default in compat 10 or later). If you want
+the service to be restarted but be stopped during the upgrade, then please
+use B<--no-restart-after-upgrade> (note the "after-upgrade").
+
+Note that the B<--no-restart-on-upgrade> alias is deprecated and will
+be removed in compat 12. This is to avoid confusion with the
+B<--no-restart-after-upgrade> option. The B<--no-stop-on-upgrade>
+variant was introduced in debhelper 10.2 (included in Debian stretch).
+
+=item B<--no-start>
+
+Do not start the init script on install or upgrade, or stop it on removal.
+Only call B<update-rc.d>. Useful for rcS scripts.
+
+=item B<--no-enable>
+
+Disable the init script on purge, but do not enable them on install.
+This implies a versioned dependency on B<< init-system-helpers (E<gt>=
+1.51) >> as it is the first (functional) version that supports
+B<< update-rc.d E<lt>scriptE<gt> defaults-disabled >>.
+
+B<Note> that this option does not affect whether the services are
+started. Please remember to also use B<--no-start> if the service
+should not be started.
+
+Cannot be combined with B<-u>I<params>,
+B<--update-rcd-params=>I<params>, or B<--> I<params>.
+
+=item B<-d>, B<--remove-d>
+
+Remove trailing B<d> from the name of the package, and use the result for the
+filename the upstart job file is installed as in F<etc/init/> , and for the
+filename the init script is installed as in etc/init.d and the default file
+is installed as in F<etc/default/>. This may be useful for daemons with names
+ending in B<d>. (Note: this takes precedence over the B<--init-script> parameter
+described below.)
+
+=item B<-u>I<params> B<--update-rcd-params=>I<params>
+
+=item B<--> I<params>
+
+Pass I<params> to L<update-rc.d(8)>. If not specified, B<defaults> (or
+B<defaults-disabled> with B<--no-enable>) will be passed to
+L<update-rc.d(8)>.
+
+Cannot be combined with B<--no-enable>.
+
+=item B<--name=>I<name>
+
+Install the init script (and default file) as well as upstart job file
+using the filename I<name> instead of the default filename, which is
+the package name. When this parameter is used, B<dh_installinit> looks
+for and installs files named F<debian/package.name.init>,
+F<debian/package.name.default> and F<debian/package.name.upstart>
+instead of the usual F<debian/package.init>, F<debian/package.default> and
+F<debian/package.upstart>.
+
+=item B<--init-script=>I<scriptname>
+
+Use I<scriptname> as the filename the init script is installed as in
+F<etc/init.d/> (and also use it as the filename for the defaults file, if it
+is installed). If you use this parameter, B<dh_installinit> will look to see
+if a file in the F<debian/> directory exists that looks like
+F<package.scriptname> and if so will install it as the init script in
+preference to the files it normally installs.
+
+This parameter is deprecated, use the B<--name> parameter instead. This
+parameter is incompatible with the use of upstart jobs.
+
+=item B<--error-handler=>I<function>
+
+Call the named shell I<function> if running the init script fails. The
+function should be provided in the F<prerm> and F<postinst> scripts, before the
+B<#DEBHELPER#> token.
+
+=back
+
+=head1 NOTES
+
+Note that this command is not idempotent. L<dh_prep(1)> should be called
+between invocations of this command. Otherwise, it may cause multiple
+instances of the same text to be added to maintainer scripts.
+
+=cut
+
+$dh{RESTART_AFTER_UPGRADE} = '';
+$dh{NO_START} = '';
+
+init(options => {
+ "r" => \$dh{R_FLAG},
+ 'no-stop-on-upgrade' => \$dh{R_FLAG},
+ "no-restart-on-upgrade" => sub {
+ $dh{R_FLAG} = 1;
+ deprecated_functionality("--no-restart-on-upgrade has been renamed to --no-stop-on-upgrade",
+ 12);
+ },
+ "no-start" => \$dh{NO_START},
+ "R|restart-after-upgrade!" => \$dh{RESTART_AFTER_UPGRADE},
+ "init-script=s" => \$dh{INIT_SCRIPT},
+ "update-rcd-params=s", => \$dh{U_PARAMS},
+ "remove-d" => \$dh{D_FLAG},
+ "no-enable" => \$dh{NO_ENABLE},
+});
+
+if ($dh{RESTART_AFTER_UPGRADE} eq '') {
+ $dh{RESTART_AFTER_UPGRADE} = 1 if not defined($dh{R_FLAG}) and $dh{NO_START} eq '';
+}
+
+# PROMISE: DH NOOP WITHOUT service tmpfile default upstart init init.d tmp(usr/lib/tmpfiles.d) tmp(etc/tmpfiles.d) cli-options(--init-script|-d|--remove-d|-o|--only-scripts)
+
+my %snippet_options = ('snippet-order' => 'service');
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ # Figure out what filename to install it as.
+ my $script;
+ my $scriptsrc;
+ my $jobfile=$package;
+ if (defined $dh{NAME}) {
+ $jobfile=$script=$scriptsrc=$dh{NAME};
+ }
+ elsif ($dh{D_FLAG}) {
+ # -d on the command line sets D_FLAG. We will
+ # remove a trailing 'd' from the package name and
+ # use that as the name.
+ $script=$package;
+ if ($script=~m/(.*)d$/) {
+ $jobfile=$script=$1;
+ }
+ else {
+ warning("\"$package\" has no final d' in its name, but -d was specified.");
+ }
+ $scriptsrc=$script;
+ }
+ elsif ($dh{INIT_SCRIPT}) {
+ $script=$dh{INIT_SCRIPT};
+ $scriptsrc=$script;
+ }
+ else {
+ $script=$package;
+ if (compat(9)) {
+ $scriptsrc=$script;
+ }
+ else {
+ $scriptsrc="init";
+ }
+ }
+
+ my $service='';
+ $service=pkgfile($package,"service") if compat(10);
+ if ($service ne '' && ! $dh{ONLYSCRIPTS}) {
+ my $path="$tmp/lib/systemd/system";
+ install_dir($path);
+ install_file($service, "$path/$script.service");
+ }
+
+ my $tmpfile='';
+ $tmpfile=pkgfile($package,"tmpfile") if compat(10);
+ if ($tmpfile ne '' && ! $dh{ONLYSCRIPTS}) {
+ my $path="$tmp/usr/lib/tmpfiles.d";
+ install_dir($path);
+ install_file($tmpfile, "$path/$script.conf");
+ }
+
+ my $job=pkgfile($package,"upstart");
+ if ($job ne '' and not compat(11)) {
+ isnative($package); # For the side-effect of setting $dh{VERSION}
+ warning("Detected an upstart file; these are no longer supported by dh_installinit in compat 11");
+ warning("Please ensure a proper removal by adding a \"rm_conffile\" line in debian/<pkg>.maintscript");
+ warning("Example maintscript line:");
+ warning(" rm_conffile /etc/init/${jobfile}.conf $dh{VERSION}");
+ warning("(Note: the example is a best-effort guess and it is not always correct! Please verify before use)");
+ warning("see \"man dh_installdeb\" for more information about the maintscript file");
+ warning("");
+ error("upstart jobs are no longer supported! Please remove $job and check if you need to add a conffile removal");
+ }
+ if ($job ne '' && ! $dh{ONLYSCRIPTS}) {
+ install_dir("$tmp/etc/init");
+ install_file($job, "$tmp/etc/init/$jobfile.conf");
+ }
+
+ my $default=pkgfile($package,'default');
+ if ($default ne '' && ! $dh{ONLYSCRIPTS}) {
+ install_dir("$tmp/etc/default");
+ install_file($default, "$tmp/etc/default/$script");
+ }
+
+ my $init=pkgfile($package,$scriptsrc) || pkgfile($package,"init") ||
+ pkgfile($package,"init.d");
+
+ if ($init ne '' && ! $dh{ONLYSCRIPTS}) {
+ install_dir("$tmp/etc/init.d");
+ install_prog($init,"$tmp/etc/init.d/$script");
+ }
+
+ if ($dh{INIT_SCRIPT} && $job ne '' && $init ne '') {
+ error("Can't use --init-script with an upstart job");
+ }
+
+ if (compat(10) && !$dh{NOSCRIPTS}) {
+ # Include postinst-init-tmpfiles if the package ships any files
+ # in /usr/lib/tmpfiles.d or /etc/tmpfiles.d
+ my @tmpfiles;
+ find({
+ wanted => sub {
+ my $name = $File::Find::name;
+ return unless -f $name;
+ $name =~ s/^\Q$tmp\E//g;
+ if ($name =~ m,^/usr/lib/tmpfiles\.d/, ||
+ $name =~ m,^/etc/tmpfiles\.d/,) {
+ push(@tmpfiles, basename($name));
+ }
+ },
+ no_chdir => 1,
+ }, $tmp);
+ if (@tmpfiles > 0) {
+ # Not migrated to hashref based autoscripts. This will
+ # happen as people migrate to dh_installsystemd.
+ autoscript($package,"postinst", "postinst-init-tmpfiles",
+ "s,#TMPFILES#," . join(" ", sort @tmpfiles).",g");
+ }
+ }
+
+ if ($service ne '' || $job ne '' || $init ne '' || $dh{ONLYSCRIPTS}) {
+ # This is set by the -u "foo" command line switch, it's
+ # the parameters to pass to update-rc.d. If not set,
+ # we have to say "defaults".
+ my $params = 'defaults';
+ my $update_rcd_params = compat(11) ? '' : '--skip-systemd-native ';
+ if ($dh{NO_ENABLE}) {
+ $params = 'defaults-disabled';
+ addsubstvar($package, "misc:Depends", "init-system-helpers (>= 1.51)");
+ }
+
+ if (defined($dh{U_PARAMS}) and @{$dh{U_PARAMS}}) {
+ error("--no-enable and -- params/-u/--update-rcd-params/ are mutually exclusive") if $dh{NO_ENABLE};
+ $params=join(' ',@{$dh{U_PARAMS}});
+ }
+
+ if (! $dh{NOSCRIPTS}) {
+ my $replace = {
+ 'SCRIPT' => $script,
+ 'INITPARMS' => $params,
+ 'ERROR_HANDLER' => $dh{ERROR_HANDLER},
+ 'INVOKE_RCD_PARAMS' => $update_rcd_params,
+ };
+ autoscript($package, 'preinst', 'preinst-init-chmod', $replace, \%snippet_options);
+
+ if (! $dh{NO_START}) {
+ if ($dh{RESTART_AFTER_UPGRADE}) {
+ # update-rc.d, and restart (or
+ # start if new install) script
+ autoscript($package, 'postinst', 'postinst-init-restart', $replace, \%snippet_options);
+ }
+ else {
+ # update-rc.d, and start script
+ autoscript($package, 'postinst', 'postinst-init', $replace, \%snippet_options);
+ }
+
+ autoscript($package, 'preinst', 'preinst-init-stop', $replace, \%snippet_options)
+ unless ($dh{R_FLAG} || $dh{RESTART_AFTER_UPGRADE});
+
+ # stops script only on remove
+ autoscript($package, 'prerm', 'prerm-init-norestart', $replace, \%snippet_options);
+
+ # The --skip-systemd-native option requires
+ # init-system-helpers (>= 1.54) and since we need it
+ # from prerm we need init-system-helpers to have been
+ # unpacked before the package is being unpacked.
+ addsubstvar($package, 'misc:Pre-Depends', 'init-system-helpers (>= 1.54~)')
+ if $update_rcd_params !~ m/^\s*$/;
+ }
+ else {
+ # just update-rc.d
+ autoscript($package,"postinst", "postinst-init-nostart", $replace, \%snippet_options);
+ }
+
+ # removes rc.d links
+ autoscript($package,"postrm","postrm-init",
+ { 'SCRIPT' => $script, 'ERROR_HANDLER' => $dh{ERROR_HANDLER} },
+ \%snippet_options);
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>, L<dh_installsystemd(1)>
+
+This program is a part of debhelper.
+
+=head1 AUTHORS
+
+Joey Hess <joeyh@debian.org>
+
+Steve Langasek <steve.langasek@canonical.com>
+
+Michael Stapelberg <stapelberg@debian.org>
+
+=cut
diff --git a/dh_installinitramfs b/dh_installinitramfs
new file mode 100755
index 0000000..37fb27f
--- /dev/null
+++ b/dh_installinitramfs
@@ -0,0 +1,103 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installinitramfs - install initramfs hooks and setup maintscripts
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installinitramfs> [S<B<debhelper options>>] [B<-n>]
+
+=head1 DESCRIPTION
+
+B<dh_installinitramfs> is a debhelper program that is responsible for
+installing Debian package provided initramfs hooks.
+
+If B<dh_installinitramfs> installs or detects one or more initramfs
+hooks in the package, then it also automatically generates the noawait
+trigger B<update-initframfs> command needed to interface with the
+Debian initramfs system. This trigger is inserted into the
+packaging by L<dh_installdeb(1)>.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.initramfs-hook
+
+Assumed to be an initramfs hook that will be installed into F<<
+usr/share/initramfs-tools/hooks/I<package> >> in the package build
+directory. See B<HOOK SCRIPTS> in L<initramfs-tools(8)> for more
+information about initramfs hooks.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-n>, B<--no-scripts>
+
+Do not add the B<update-initramfs> trigger even if it seems like the package
+might need it. The option is called B<--no-scripts> for historical
+reasons as B<dh_installinitramfs> would previously generate maintainer
+scripts that called B<update-initramfs -u>.
+
+Use this option, if you need to interface with the B<update-initramfs>
+system that is not satisfied by the noawait trigger (e.g. because you need
+the extra guarantees and head-aches of a await trigger).
+
+=back
+
+=head1 NOTES
+
+Note that this command is not idempotent. L<dh_prep(1)> should be called
+between invocations of this command. Otherwise, it may cause multiple
+instances of the same text to be added to triggers file.
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT initramfs-hook tmp(usr/share/initramfs-tools/hooks) cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp = tmpdir($package);
+ my $hook_script = pkgfile($package, 'initramfs-hook');
+ my $has_hooks;
+ my $hook_dir = "${tmp}/usr/share/initramfs-tools/hooks";
+
+ if ($hook_script ne '') {
+ install_dir($hook_dir);
+ install_prog($hook_script, "${hook_dir}/${package}");
+ $has_hooks = 1;
+ } elsif (-d $hook_dir and not is_empty_dir($hook_dir)) {
+ $has_hooks = 1;
+ }
+
+ if ($has_hooks && ! $dh{NOSCRIPTS}) {
+ autotrigger($package, 'activate-noawait', 'update-initramfs');
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+L<update-initramfs(8)>
+L<initramfs-tools(8)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Niels Thykier <niels@thykier.net>
+
+=cut
diff --git a/dh_installlogcheck b/dh_installlogcheck
new file mode 100755
index 0000000..bf1f779
--- /dev/null
+++ b/dh_installlogcheck
@@ -0,0 +1,91 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installlogcheck - install logcheck rulefiles into etc/logcheck/
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installlogcheck> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+B<dh_installlogcheck> is a debhelper program that is responsible for
+installing logcheck rule files.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.logcheck.cracking
+
+=item debian/I<package>.logcheck.violations
+
+=item debian/I<package>.logcheck.violations.ignore
+
+=item debian/I<package>.logcheck.ignore.workstation
+
+=item debian/I<package>.logcheck.ignore.server
+
+=item debian/I<package>.logcheck.ignore.paranoid
+
+Each of these files, if present, are installed into corresponding
+subdirectories of F<etc/logcheck/> in package build directories.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+Look for files named F<debian/package.name.logcheck.*> and install
+them into the corresponding subdirectories of F<etc/logcheck/>, but
+use the specified name instead of that of the package.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT logcheck.cracking logcheck.violations logcheck.violations.ignore logcheck.ignore.workstation logcheck.ignore.server logcheck.ignore.paranoid cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ foreach my $type (qw{ignore.d.workstation ignore.d.server
+ ignore.d.paranoid cracking.d
+ violations.d violations.ignore.d}) {
+ my $typenod=$type;
+ $typenod=~s/\.d//;
+ my $logcheck=pkgfile($package,"logcheck.$typenod");
+ if ($logcheck) {
+ my $packagenodot=pkgfilename($package); # run-parts..
+ $packagenodot=~s/\./_/g;
+ install_dir("$tmp/etc/logcheck/$type");
+ install_file($logcheck, "$tmp/etc/logcheck/$type/$packagenodot");
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Jon Middleton <jjm@debian.org>
+
+=cut
diff --git a/dh_installlogrotate b/dh_installlogrotate
new file mode 100755
index 0000000..fc3d60a
--- /dev/null
+++ b/dh_installlogrotate
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installlogrotate - install logrotate config files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installlogrotate> [S<I<debhelper options>>] [B<--name=>I<name>]
+
+=head1 DESCRIPTION
+
+B<dh_installlogrotate> is a debhelper program that is responsible for installing
+logrotate config files into F<etc/logrotate.d> in package build directories.
+Files named F<debian/package.logrotate> are installed.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+Look for files named F<debian/package.name.logrotate> and install them as
+F<etc/logrotate.d/name>, instead of using the usual files and installing them
+as the package name.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT logrotate cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $file=pkgfile($package,"logrotate");
+
+ if ($file) {
+ install_dir("$tmp/etc/logrotate.d");
+ install_file($file,"$tmp/etc/logrotate.d/".pkgfilename($package));
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installman b/dh_installman
new file mode 100755
index 0000000..015d439
--- /dev/null
+++ b/dh_installman
@@ -0,0 +1,430 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installman - install man pages into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use File::Find;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installman> [S<I<debhelper options>>] [S<I<manpage> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installman> is a debhelper program that handles installing man
+pages into the correct locations in package build directories.
+
+In compat 10 and earlier, this program was primarily for when
+upstream's build system does not properly install them as a part of
+its install step (or it does not have an install step). In compat 11
+and later, it also supports the default searchdir plus --sourcedir
+like dh_install(1) and has the advantage that it respects the nodoc
+build profile (unlike dh_install(1)).
+
+Even if you prefer to use L<dh_install(1)> for installing the manpages,
+B<dh_installman> can still be useful for converting the manpage encoding
+to UTF-8 and for converting F<.so> links (as described below). However,
+that part happens automatically without any explicit configuration.
+
+
+You tell B<dh_installman> what man pages go in your packages, and it figures out
+where to install them based on the section field in their B<.TH> or
+B<.Dt> line. If you have a properly formatted B<.TH> or B<.Dt> line,
+your man page will be installed into the right directory, with the
+right name (this includes proper handling of pages with a subsection,
+like B<3perl>, which are placed in F<man3>, and given an extension of
+F<.3perl>). If your B<.TH> or B<.Dt> line is incorrect or missing, the
+program may guess wrong based on the file extension.
+
+It also supports translated man pages, by looking for extensions
+like F<.ll.8> and F<.ll_LL.8>, or by use of the B<--language> switch.
+
+If B<dh_installman> seems to install a man page into the wrong section or with
+the wrong extension, this is because the man page has the wrong section
+listed in its B<.TH> or B<.Dt> line. Edit the man page and correct the
+section, and B<dh_installman> will follow suit. See L<man(7)> for details
+about the B<.TH> section, and L<mdoc(7)> for the B<.Dt> section. If
+B<dh_installman> seems to install a man page into a directory
+like F</usr/share/man/pl/man1/>, that is because your program has a
+name like F<foo.pl>, and B<dh_installman> assumes that means it is translated
+into Polish. Use B<--language=C> to avoid this.
+
+After the man page installation step, B<dh_installman> will check to see if
+any of the man pages in the temporary directories of any of the packages it
+is acting on contain F<.so> links. If so, it changes them to symlinks.
+
+Also, B<dh_installman> will use man to guess the character encoding of each
+manual page and convert it to UTF-8. If the guesswork fails for some
+reason, you can override it using an encoding declaration. See
+L<manconv(1)> for details.
+
+From debhelper compatibility level 11 on, B<dh_install> will fall back to
+looking in F<debian/tmp> for files, if it does not find them in the current
+directory (or wherever you've told it to look using B<--sourcedir>).
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.manpages
+
+Lists man pages to be installed.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-A>, B<--all>
+
+Install all files specified by command line parameters in ALL packages
+acted on.
+
+=item B<--language=>I<ll>
+
+Use this to specify that the man pages being acted on are written in the
+specified language.
+
+=item B<--sourcedir=>I<dir>
+
+Look in the specified directory for files to be installed. This option
+requires compat 11 or later (it is silently ignored in compat 10 or earlier).
+
+Note that this is not the same as the B<--sourcedirectory> option used
+by the B<dh_auto_>I<*> commands. You rarely need to use this option, since
+B<dh_installman> automatically looks for files in F<debian/tmp> in debhelper
+compatibility level 11 and above.
+
+=item I<manpage> ...
+
+Install these man pages into the first package acted on. (Or in all
+packages if B<-A> is specified).
+
+=back
+
+=head1 EXAMPLES
+
+An example F<debian/manpages> file could look like this:
+
+ doc/man/foo.1
+ # Translations
+ doc/man/foo.da.1
+ doc/man/foo.de.1
+ doc/man/foo.fr.1
+ # NB: The following line is considered a polish translation
+ # of "foo.1" (and not a manpage written in perl called "foo.pl")
+ doc/man/foo.pl.1
+ # ...
+
+=head1 NOTES
+
+An older version of this program, L<dh_installmanpages(1)>, is still used
+by some packages, and so is still included in debhelper.
+It is, however, deprecated, due to its counterintuitive and inconsistent
+interface. Use this program instead.
+
+=cut
+
+init(options => {
+ "language=s" => \$dh{LANGUAGE},
+ "sourcedir=s" => \$dh{SOURCEDIR},
+});
+
+
+# PROMISE: DH NOOP WITHOUT pkgfile-logged(manpages) tmp(usr/share/man) cli-options()
+
+my (@sofiles, @sodests);
+my @all_packages = getpackages();
+
+my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject;
+my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0;
+# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled
+# or dh_missing might make noise.
+$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs;
+
+on_items_in_parallel(\@all_packages, sub {
+
+ foreach my $package (@_) {
+ next if is_udeb($package);
+
+ my $tmp = tmpdir($package);
+ my $file = pkgfile($package, "manpages");
+ my @manpages;
+ my @search_dirs = ('.');
+ my $skip_install = process_pkg($package) ? 0 : 1;
+ my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler;
+ @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10);
+
+ @manpages = filearray($file, \@search_dirs, $error_handler) if $file;
+
+ if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) {
+ push @manpages, @ARGV;
+ }
+
+ log_installed_files($package, @manpages);
+
+ next if $skip_install or $nodocs;
+
+ foreach my $page (@manpages) {
+ my $basename = basename($page);
+
+ # Support compressed pages.
+ my $gz = '';
+ if ($basename =~ m/(.*)(\.gz)/) {
+ $basename = $1;
+ $gz = $2;
+ }
+
+ my ($fd, $section);
+ # See if there is a .TH or .Dt entry in the man page. If so,
+ # we'll pull the section field from that.
+ if ($gz) {
+ $fd = open_gz($page) or error("open $page failed: $!");
+ }
+ else {
+ open($fd, '<', $page) or error("open $page failed: $!");
+ }
+ while (<$fd>) {
+ if (/^\.TH\s+\S+\s+"?(\d+[^"\s]*)"?/ ||
+ /^\.Dt\s+\S+\s+(\d+[^\s]*)/) {
+ $section = $1;
+ if ($section =~ m/^\d+[.]\d+/) {
+ warning("Ignoring section defined in TH/Dt for ${page} as it looks like a version number: ${section}");
+ $section = undef;
+ }
+ last;
+ }
+ }
+ close($fd);
+ # Failing that, we can try to get it from the filename.
+ if (!$section) {
+ ($section) = $basename =~ m/\.([1-9]\w*)$/;
+ }
+
+ # Now get the numeric component of the section.
+ my ($realsection) = $section =~ m/^(\d+)/ if defined $section;
+ if (!$realsection or ($realsection < 0 or $realsection > 9)) {
+ warning("Section for ${page} is computed as \"${section}\", which is not a valid section")
+ if defined($section);
+ error("Could not determine section for $page");
+ }
+
+ # Get the man page's name -- everything up to the last dot.
+ my ($instname) = $basename =~ m/^(.*)\./;
+
+ my $destdir = "$tmp/usr/share/man/man$realsection/";
+ my $langcode;
+ if (!defined $dh{LANGUAGE} || !exists $dh{LANGUAGE}) {
+ if (not compat(10) and $page =~ m{/man/(?:([a-z][a-z](?:_[A-Z][A-Z])?)(?:\.[^/]+)?)?/man[1-9]/}) {
+ # If it looks like it was installed in a proper man dir, assume the language
+ # from that is correct.
+ $langcode = $1;
+ } else {
+ # Translated man pages are typically specified by adding the
+ # language code to the filename, so detect that and
+ # redirect to appropriate directory, stripping the code.
+ ($langcode) = $basename =~ m/\.([a-z][a-z](?:_[A-Z][A-Z])?)\.(?:[1-9]|man)/;
+ # Avoid false positives such as /usr/share/man/man8/libnss_myhostname.so.2.8
+ if (defined $langcode && $langcode eq 'so' && $basename =~ /^lib.*\.so(\.[0-9]+)*$/) {
+ $langcode = '';
+ }
+ }
+ } elsif ($dh{LANGUAGE} ne 'C') {
+ $langcode = $dh{LANGUAGE};
+ }
+
+ if (defined $langcode && $langcode ne '') {
+ # Strip the language code from the instname.
+ $instname =~ s/\.$langcode$//;
+ }
+
+ if (defined $langcode && $langcode ne '') {
+ $destdir = "$tmp/usr/share/man/$langcode/man$realsection/";
+ }
+ $destdir =~ tr:/:/:s; # just for looks
+ my $instpage = "$destdir$instname.$section";
+
+ next if -l $instpage;
+
+ install_dir($destdir);
+ if ($gz) {
+ doit({ stdout => $instpage }, 'zcat', $page);
+ }
+ else {
+ install_file($page, $instpage);
+ }
+ }
+
+ # Now the .so conversion.
+ @sofiles = @sodests = ();
+ foreach my $dir (qw{usr/share/man}) {
+ if (-e "$tmp/$dir") {
+ find(\&find_so_man, "$tmp/$dir");
+ }
+ }
+ foreach my $sofile (@sofiles) {
+ my $sodest = shift(@sodests);
+ rm_files($sofile);
+ make_symlink_raw_target($sodest, $sofile);
+ }
+ }
+
+});
+
+# Now utf-8 conversion.
+my $has_man_recode = 0;
+$has_man_recode = 1 if has_man_db_tool('man-recode');
+
+if ($has_man_recode || has_man_db_tool('man')) {
+ my (@manpages_to_reencode, @issues);
+ for my $package (@{$dh{DOPACKAGES}}) {
+ next if is_udeb($package);
+ my $tmp = tmpdir($package);
+ foreach my $dir (qw{usr/share/man}) {
+ next unless -e "$tmp/$dir";
+ my %seen;
+ my $wanted = sub {
+ my $path = $File::Find::name;
+ return if -l $path || !-f _;
+ if ($path =~ m/\.dh-new$/) {
+ push(@issues, $path);
+ return;
+ }
+ my $uncompressed_name = $path;
+ $uncompressed_name =~ s/\.(?:gz|Z)$//;
+ if (exists($seen{$uncompressed_name})) {
+ my $msg = "Multiple definitions for manpage ${uncompressed_name} via different compressions.";
+ my @values = sort ($path, $seen{$uncompressed_name});
+ my $warn_msg = $msg . ' Picking ' . $values[0] . ' as the canonical definition.';
+ my $error_msg = $msg . ' Please ensure there is at most one definition.';
+ deprecated_functionality($warn_msg, 13, $error_msg);
+ $path = $values[0];
+ warn("Removing conflicting definition of ${uncompressed_name} (" . $values[1]
+ . ') to ensure deterministic behaviour.');
+ rm_files($values[1]);
+ }
+ $seen{$uncompressed_name} = $path;
+ };
+ find({
+ no_chdir => 1,
+ wanted => $wanted,
+ }, "$tmp/$dir");
+ push(@manpages_to_reencode, sort(values(%seen)));
+ }
+
+ if (@issues) {
+ warning("Removing temporary manpages from another dh_installman instance");
+ rm_files(@issues);
+ warning("Possibly race-condition detected or left-overs from an interrupted dh_installman (e.g. with ^C)");
+ error("Please ensure there are no parallel dh_installman's running (for this pkg) and then re-run dh_installman");
+ }
+ }
+ if (@manpages_to_reencode) {
+ on_items_in_parallel(\@manpages_to_reencode, \&reencode_manpages);
+ }
+} else {
+ # Should only occur during debhelper building itself (to avoid a B-D on man-db).
+ warning("man is not available. Skipping re-encode of UTF-8 manpages")
+}
+
+# Check if a file is a .so man page, for use by File::Find.
+sub find_so_man {
+ # The -s test is because a .so file tends to be small. We don't want
+ # to open every man page. 1024 is arbitrary.
+ if (! -f $_ || -s _ > 1024 || -s _ == 0) {
+ return;
+ }
+
+ # Test first line of file for the .so thing.
+ my $fd;
+ if (/\.gz$/) {
+ $fd = open_gz($_) or error("open $_ failed: $!");
+ }
+ else {
+ open($fd, '<', $_) || error("open $_ failed: $!");
+ }
+ my $l = <$fd>;
+ close($fd);
+
+ if (! defined $l) {
+ error("failed to read $_");
+ }
+
+ if ($l=~m/\.so\s+(.*)\s*/) {
+ my $solink=$1;
+ # This test is here to prevent links like ... man8/../man8/foo.8
+ if (basename($File::Find::dir) eq
+ dirname($solink)) {
+ $solink=basename($solink);
+ }
+ # A so link with a path is relative to the base of the man
+ # page hierarchy, but without a path, is relative to the
+ # current section.
+ elsif ($solink =~ m!/!) {
+ $solink="../$solink";
+ }
+
+ if (-e $solink || -e "$solink.gz") {
+ push @sofiles,"$File::Find::dir/$_";
+ push @sodests,$solink;
+ }
+ }
+}
+
+sub has_man_db_tool {
+ my ($tool) = @_;
+ open(my $old_stderr, '>&', *STDERR) or error("dup(STDERR, tmp_fd): $!");
+ # Ignore the error; it is intended as noise-reduction. As long as we can restore
+ # the stderr later, the log will just be slightly more noisy than planned.
+ open(*STDERR, '>', '/dev/null') or warn("redirect stderr to /dev/null failed: $!");
+
+ my $res = defined(`$tool --version`);
+ open(*STDERR, '>&', $old_stderr) or error("dup(tmp_fd, STDERR): $!");
+ close($old_stderr);
+ return $res;
+}
+
+sub reencode_manpages {
+ my (@manpages) = @_;
+ if ($has_man_recode) {
+ xargs(\@manpages, 'man-recode', '--to-code', 'UTF-8', '--suffix', '.dh-new');
+ }
+ for my $manpage (@manpages) {
+ my $manpage_tmp = "${manpage}.dh-new";
+ $manpage_tmp =~ s/\.(?:gz|Z)\.dh-new$/.dh-new/;
+ if (not $has_man_recode) {
+ my $manpage_cmd = ($manpage =~ m{^/}) ? $manpage : "./${manpage}";
+ doit({ stdout => $manpage_tmp }, 'man', '-l', '--recode', 'UTF-8', $manpage_cmd);
+ }
+ # recode uncompresses compressed pages
+ my $orig = $manpage;
+ rm_files($orig) if $manpage =~ s/\.(gz|Z)$//;
+ rename_path($manpage_tmp, $manpage);
+ }
+ # Bulk reset permissions of all re-encoded files
+ xargs(\@manpages, 'chmod', '0644', '--');
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installmanpages b/dh_installmanpages
new file mode 100755
index 0000000..22c669e
--- /dev/null
+++ b/dh_installmanpages
@@ -0,0 +1,208 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installmanpages - old-style man page installer (deprecated)
+
+=cut
+
+use strict;
+use warnings;
+use File::Find;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installmanpages> [S<I<debhelper options>>] [S<I<file> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installmanpages> is a debhelper program that is responsible for
+automatically installing man pages into F<usr/share/man/>
+in package build directories.
+
+This is a DWIM-style program, with an interface unlike the rest of
+debhelper. It is deprecated, and you are encouraged to use
+L<dh_installman(1)> instead.
+
+B<dh_installmanpages> scans the current directory and all subdirectories for
+filenames that look like man pages. (Note that only real files are looked
+at; symlinks are ignored.) It uses L<file(1)> to verify that the files are
+in the correct format. Then, based on the files' extensions, it installs
+them into the correct man directory.
+
+All filenames specified as parameters will be skipped by B<dh_installmanpages>.
+This is useful if by default it installs some man pages that you do not
+want to be installed.
+
+After the man page installation step, B<dh_installmanpages> will check to see
+if any of the man pages are F<.so> links. If so, it changes them to symlinks.
+
+=head1 OPTIONS
+
+=over 4
+
+=item I<file> ...
+
+Do not install these files as man pages, even if they look like valid man
+pages.
+
+=back
+
+=head1 BUGS
+
+B<dh_installmanpages> will install the man pages it finds into B<all> packages
+you tell it to act on, since it can't tell what package the man
+pages belong in. This is almost never what you really want (use B<-p> to work
+around this, or use the much better L<dh_installman(1)> program instead).
+
+Files ending in F<.man> will be ignored.
+
+Files specified as parameters that contain spaces in their filenames will
+not be processed properly.
+
+=cut
+
+warning("This program is deprecated, switch to dh_installman.");
+
+init();
+
+# Check if a file is a man page, for use by File::Find.
+my @manpages;
+my @allpackages;
+sub find_man {
+ # Does its filename look like a man page?
+ # .ex files are examples installed by deb-make,
+ # we don't want those, or .in files, which are
+ # from configure, nor do we want CVS .#* files.
+ if (! (-f $_ && /^.*\.[1-9].*$/ && ! /\.(ex|in)$/ && ! /^\.#/)) {
+ return;
+ }
+
+ # It's not in a tmp directory is it?
+ if ($File::Find::dir=~m:debian/.*tmp.*:) {
+ return;
+ }
+ foreach my $dir (@allpackages) {
+ if ($File::Find::dir=~m:debian/\Q$dir\E:) {
+ return;
+ }
+ }
+
+ # And file does think it's a real man page?
+ my $type=`file -z $_`;
+ if ($type !~ m/:.*roff/) {
+ return;
+ }
+
+ # Good enough.
+ push @manpages,"$File::Find::dir/$_";
+}
+
+# Check if a file is a .so man page, for use by File::Find.
+my @sofiles;
+my @sodests;
+sub find_so_man {
+ # The -s test is because a .so file tends to be small. We don't want
+ # to open every man page. 1024 is arbitrary.
+ if (! -f $_ || -s $_ > 1024) {
+ return;
+ }
+
+ # Test first line of file for the .so thing.
+ open(my $fd, '<', $_);
+ my $l = <$fd>;
+ close($fd);
+ if ($l=~m/\.so\s+(.*)/) {
+ my $solink=$1;
+ # This test is here to prevent links like ... man8/../man8/foo.8
+ if (basename($File::Find::dir) eq
+ dirname($solink)) {
+ $solink=basename($solink);
+ }
+ else {
+ $solink="../$solink";
+ }
+
+ push @sofiles,"$File::Find::dir/$_";
+ push @sodests,$solink;
+ }
+}
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ next if is_udeb($package);
+
+ my $tmp=tmpdir($package);
+
+ # Find all filenames that look like man pages.
+ @manpages=();
+ @allpackages=getpackages() if not @allpackages;
+ find(\&find_man,'.'); # populates @manpages
+
+ foreach my $page (@manpages) {
+ $page=~s:^\./::; # just for looks
+
+ my $basename=basename($page);
+
+ # Skip all files listed on command line.
+ my $install=1;
+ foreach my $skip (@ARGV) {
+ # Look at basename of what's on connect line
+ # for backwards compatibility.
+ if ($basename eq basename($skip)) {
+ $install=undef;
+ last;
+ }
+ }
+
+ if ($install) {
+ my $extdir="share";
+
+ my ($section)=$basename=~m/\.([1-9])/;
+
+ my $destdir="$tmp/usr/$extdir/man/man$section/";
+
+ # Handle translated man pages.
+ my $instname=$basename;
+ my ($langcode)=$basename=~m/\.([a-z][a-z])\.([1-9])/;
+ if (defined $langcode && $langcode ne '') {
+ $destdir="$tmp/usr/$extdir/man/$langcode/man$section/";
+ $instname=~s/\.$langcode\./\./;
+ }
+
+ $destdir=~tr:/:/:s; # just for looks
+
+ if (! -e "$destdir/$basename" && !-l "$destdir/$basename") {
+ install_dir($destdir);
+ install_file($page,$destdir.$instname);
+ }
+ }
+ }
+
+ # Now the .so conversion.
+ @sofiles=@sodests=();
+ foreach my $dir (qw{usr/share/man}) {
+ if (-e "$tmp/$dir") {
+ find(\&find_so_man, "$tmp/$dir");
+ }
+ }
+ foreach my $sofile (@sofiles) {
+ my $sodest=shift(@sodests);
+ doit "rm","-f",$sofile;
+ doit "ln","-sf",$sodest,$sofile;
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installmenu b/dh_installmenu
new file mode 100755
index 0000000..c30e90f
--- /dev/null
+++ b/dh_installmenu
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installmenu - install Debian menu files into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installmenu> [S<B<debhelper options>>] [B<-n>]
+
+=head1 DESCRIPTION
+
+B<dh_installmenu> is a debhelper program that is responsible for installing
+files used by the Debian B<menu> package into package build directories.
+
+It also automatically generates the F<postinst> and F<postrm> commands needed to
+interface with the Debian B<menu> package. These commands are inserted into
+the maintainer scripts by L<dh_installdeb(1)>.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.menu
+
+Debian menu files, installed into usr/share/menu/I<package> in the package
+build directory. See L<menufile(5)> for its format.
+
+=item debian/I<package>.menu-method
+
+Debian menu method files, installed into etc/menu-methods/I<package>
+in the package build directory.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-n>, B<--no-scripts>
+
+Do not modify F<postinst>/F<postrm> scripts.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT menu menu-method cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $menu=pkgfile($package,"menu");
+ my $menu_method=pkgfile($package,"menu-method");
+
+ if ($menu ne '') {
+ install_dir("$tmp/usr/share/menu");
+ install_file($menu,"$tmp/usr/share/menu/$package");
+
+ # Add the scripts if a menu-method file doesn't exist.
+ # The scripts for menu-method handle everything these do, too.
+ if ($menu_method eq "" && ! $dh{NOSCRIPTS}) {
+ autoscript($package,"postinst","postinst-menu");
+ autoscript($package,"postrm","postrm-menu")
+ }
+ }
+
+ if ($menu_method ne '') {
+ install_dir("$tmp/etc/menu-methods");
+ install_file($menu_method,"$tmp/etc/menu-methods/$package");
+
+ if (! $dh{NOSCRIPTS}) {
+ autoscript($package, 'postinst', 'postinst-menu-method', { 'PACKAGE' => $package });
+ autoscript($package, 'postrm', 'postrm-menu-method', { 'PACKAGE' => $package });
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+L<update-menus(1)>
+L<menufile(5)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installmime b/dh_installmime
new file mode 100755
index 0000000..5ad6190
--- /dev/null
+++ b/dh_installmime
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installmime - install mime files into package build directories
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installmime> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+B<dh_installmime> is a debhelper program that is responsible for installing
+mime files into package build directories.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.mime
+
+Installed into usr/lib/mime/packages/I<package> in the package build
+directory.
+
+=item debian/I<package>.sharedmimeinfo
+
+Installed into /usr/share/mime/packages/I<package>.xml in the package build
+directory.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT mime sharedmimeinfo cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ my $mime=pkgfile($package,"mime");
+ if ($mime ne '') {
+ install_dir("$tmp/usr/lib/mime/packages");
+ install_file($mime, "$tmp/usr/lib/mime/packages/$package");
+ }
+
+ my $sharedmimeinfo=pkgfile($package,"sharedmimeinfo");
+ if ($sharedmimeinfo ne '') {
+ install_dir("$tmp/usr/share/mime/packages");
+ install_file($sharedmimeinfo,
+ "$tmp/usr/share/mime/packages/$package.xml");
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installmodules b/dh_installmodules
new file mode 100755
index 0000000..924a338
--- /dev/null
+++ b/dh_installmodules
@@ -0,0 +1,119 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installmodules - register kernel modules
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+use File::Find;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installmodules> [S<I<debhelper options>>] [B<-n>] [B<--name=>I<name>]
+
+=head1 DESCRIPTION
+
+B<dh_installmodules> is a debhelper program that is responsible for
+registering kernel modules.
+
+Kernel modules are searched for in the package build directory and if
+found, F<preinst>, F<postinst> and F<postrm> commands are automatically generated to
+run B<depmod> and register the modules when the package is installed.
+These commands are inserted into the maintainer scripts by
+L<dh_installdeb(1)>.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.modprobe
+
+Installed to etc/modprobe.d/I<package>.conf in the package build directory.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-n>, B<--no-scripts>
+
+Do not modify F<preinst>/F<postinst>/F<postrm> scripts.
+
+=item B<--name=>I<name>
+
+When this parameter is used, B<dh_installmodules> looks for and
+installs files named debian/I<package>.I<name>.modprobe instead
+of the usual debian/I<package>.modprobe
+
+=back
+
+=head1 NOTES
+
+Note that this command is not idempotent. L<dh_prep(1)> should be called
+between invocations of this command. Otherwise, it may cause multiple
+instances of the same text to be added to maintainer scripts.
+
+=cut
+
+init();
+
+# Looks for kernel modules in the passed directory. If any are found,
+# returns the kernel version (or versions) that the modules seem to be for.
+sub find_kernel_modules {
+ my $searchdir=shift;
+ my %versions;
+
+ return unless -d $searchdir;
+ find(sub {
+ if (m/ [.]k?o (?:[.](?:[gx]z|bz2))? $/x) {
+ my ($kvers)=$File::Find::dir=~m!lib/modules/([^/]+)/!;
+ if (! defined $kvers || ! length $kvers) {
+ warning("Cannot determine kernel version for module $File::Find::name");
+ }
+ else {
+ $versions{$kvers}=1;
+ }
+ }
+ }, $searchdir);
+
+ return sort(keys(%versions));
+}
+
+# PROMISE: DH NOOP WITHOUT modprobe tmp(lib/modules) cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $modprobe_file=pkgfile($package,"modprobe");
+
+ if ($modprobe_file) {
+ my $path = '/etc/modprobe.d/' . pkgfilename($package) . '.conf';
+ install_dir("$tmp/etc/modprobe.d");
+ install_file($modprobe_file, "$tmp/$path");
+ }
+
+ if (! $dh{NOSCRIPTS}) {
+ foreach my $kvers (find_kernel_modules("$tmp/lib/modules")) {
+ autoscript($package, 'postinst', 'postinst-modules', { 'KVERS' => $kvers });
+ autoscript($package, 'postrm', 'postrm-modules', { 'KVERS' => $kvers });
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installpam b/dh_installpam
new file mode 100755
index 0000000..193e334
--- /dev/null
+++ b/dh_installpam
@@ -0,0 +1,81 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installpam - install pam support files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installpam> [S<I<debhelper options>>] [B<--name=>I<name>]
+
+=head1 DESCRIPTION
+
+B<dh_installpam> is a debhelper program that is responsible for installing
+files used by PAM into package build directories.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.pam
+
+Installed into usr/lib/pam.d/I<package> in the package build directory.
+
+Until compatibility level 14 this file was installed under
+etc/pam.d/I<package>. Please consider using the "rm_conffile" feature from
+L<dh_installdeb(1)> to ensure the proper removal of previous PAM files.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+Look for files named debian/I<package>.I<name>.pam and install them as
+usr/lib/pam.d/I<name>, instead of using the usual files and installing them
+using the package name.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT pam cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $pam=pkgfile($package,"pam");
+
+ my $pamd_dir="/usr/lib/pam.d";
+ if (compat(13)) {
+ $pamd_dir="/etc/pam.d";
+ }
+
+ if ($pam ne '') {
+ install_dir("$tmp/$pamd_dir");
+ install_file($pam,"$tmp/$pamd_dir/".pkgfilename($package));
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installppp b/dh_installppp
new file mode 100755
index 0000000..68784a6
--- /dev/null
+++ b/dh_installppp
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installppp - install ppp ip-up and ip-down files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installppp> [S<I<debhelper options>>] [B<--name=>I<name>]
+
+=head1 DESCRIPTION
+
+B<dh_installppp> is a debhelper program that is responsible for installing
+ppp ip-up and ip-down scripts into package build directories.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.ppp.ip-up
+
+Installed into etc/ppp/ip-up.d/I<package> in the package build directory.
+
+=item debian/I<package>.ppp.ip-down
+
+Installed into etc/ppp/ip-down.d/I<package> in the package build directory.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+Look for files named F<debian/package.name.ppp.ip-*> and install them as
+F<etc/ppp/ip-*/name>, instead of using the usual files and installing them
+as the package name.
+
+=back
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT ppp.ip-up ppp.ip-down cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ foreach my $script (qw(up down)) {
+ my $file=pkgfile($package, "ppp.ip-$script");
+ if ($file ne '') {
+ install_dir("$tmp/etc/ppp/ip-$script.d");
+ install_prog($file,"$tmp/etc/ppp/ip-$script.d/".pkgfilename($package));
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installsystemd b/dh_installsystemd
new file mode 100755
index 0000000..aba9d78
--- /dev/null
+++ b/dh_installsystemd
@@ -0,0 +1,453 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+dh_installsystemd - install systemd unit files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+use File::Find;
+use Cwd qw(getcwd abs_path);
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installsystemd> [S<I<debhelper options>>] [B<--restart-after-upgrade>] [B<--no-stop-on-upgrade>] [B<--no-enable>] [B<--no-start>] [B<--name=>I<name>] [S<I<unit file> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installsystemd> is a debhelper program that is responsible for
+installing package maintainer supplied systemd unit files.
+
+It also finds the service files installed by a package and generates
+F<preinst>, F<postinst>, and F<prerm> code blocks for enabling,
+disabling, starting, stopping, and restarting the corresponding
+systemd services, when the package is installed, updated, or
+removed. These snippets are added to the maintainer scripts by
+L<dh_installdeb(1)>.
+
+L<deb-systemd-helper(1)> is used to enable and disable systemd units,
+thus it is not necessary that the machine actually runs systemd during
+package installation time, enabling happens on all machines in order
+to be able to switch from sysvinit to systemd and back.
+
+B<dh_installsystemd> operates on all unit files installed by a
+package. For only generating blocks for specific unit files, pass them
+as arguments, C<dh_installsystemd quota.service>. Specific unit files
+can be excluded from processing using the B<-X> common L<debhelper(1)>
+option.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.mount,
+ debian/I<package>.path,
+ debian/I<package>@.path,
+ debian/I<package>.service,
+ debian/I<package>@.service,
+ debian/I<package>.socket,
+ debian/I<package>@.socket,
+ debian/I<package>.target,
+ debian/I<package>@.target,
+ debian/I<package>.timer,
+ debian/I<package>@.timer
+
+If any of those files exists, they are installed into
+F<lib/systemd/system/> in the package build directory.
+
+=item debian/I<package>.tmpfile
+
+Only used in compat 12 or earlier. In compat 13+, this file is
+handled by L<dh_installtmpfiles(1)> instead.
+
+If this exists, it is installed into F<usr/lib/tmpfiles.d/> in the
+package build directory. Note that the C<tmpfiles.d> mechanism is
+currently only used by systemd.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--no-enable>
+
+Disable the service(s) on purge, but do not enable them on install.
+
+B<Note> that this option does not affect whether the services are
+started. Please remember to also use B<--no-start> if the service
+should not be started.
+
+=item B<--name=>I<name>
+
+This option controls several things.
+
+It changes the name that B<dh_installsystemd> uses when it looks for
+maintainer provided systemd unit files as listed in the L</FILES>
+section. As an example, B<dh_installsystemd --name foo> will look for
+F<<< I<debian/package.>B<< I<foo> >>I<.service> >>> instead of
+F<< I<debian/package.service> >>). These unit files are installed as F<<
+I<name.unit-extension> >> (in the example, it would be installed as
+F<<< B<< I<foo> >>I<.service> >>>).
+
+Furthermore, if no unit files are passed explicitly as command line
+arguments, B<dh_installsystemd> will only act on unit files called
+I<name> (rather than all unit files found in the package).
+
+=item B<--restart-after-upgrade>
+
+Do not stop the unit file until after the package upgrade has been completed.
+This is the default behaviour in compat 10.
+
+In earlier compat levels the default was to stop the unit file in the
+F<prerm>, and start it again in the F<postinst>.
+
+This can be useful for daemons that should not have a possibly long
+downtime during upgrade. But you should make sure that the daemon will not
+get confused by the package being upgraded while it's running before using
+this option.
+
+=item B<--no-restart-after-upgrade>
+
+Undo a previous B<--restart-after-upgrade> (or the default of compat
+10). If no other options are given, this will cause the service to be
+stopped in the F<prerm> script and started again in the F<postinst>
+script.
+
+=item B<-r>, B<--no-stop-on-upgrade>, B<--no-restart-on-upgrade>
+
+Do not stop service on upgrade. This has the side-effect of not
+restarting the service as a part of the upgrade.
+
+If you want to restart the service with minimal downtime, please use
+B<--restart-after-upgrade> (default in compat 10 or later). If you want
+the service to be restarted but be stopped during the upgrade, then please
+use B<--no-restart-after-upgrade> (note the "after-upgrade").
+
+Note that the B<--no-restart-on-upgrade> alias is deprecated and will
+be removed in compat 14. This is to avoid confusion with the
+B<--no-restart-after-upgrade> option.
+
+=item B<--no-start>
+
+Do not start the unit file after upgrades and after initial installation (the
+latter is only relevant for services without a corresponding init script).
+
+B<Note> that this option does not affect whether the services are
+enabled. Please remember to also use B<--no-enable> if the services
+should not be enabled.
+
+=item S<B<unit file> ...>
+
+Only process and generate maintscripts for the installed unit files
+with the (base)name I<unit file>.
+
+Note: B<dh_installsystemd> will still install unit files from
+F<debian/> but it will not generate any maintscripts for them unless
+they are explicitly listed in S<B<unit file> ...>
+
+=back
+
+=head1 NOTES
+
+This command is not idempotent. L<dh_prep(1)> should be called between
+invocations of this command (with the same arguments). Otherwise, it
+may cause multiple instances of the same text to be added to
+maintainer scripts.
+
+=cut
+
+$dh{RESTART_AFTER_UPGRADE} = '';
+$dh{NO_START} = '';
+
+init(options => {
+ "no-enable" => \$dh{NO_ENABLE},
+ "r" => \$dh{R_FLAG},
+ 'no-stop-on-upgrade' => \$dh{R_FLAG},
+ "no-restart-on-upgrade" => \$dh{R_FLAG},
+ "no-start" => \$dh{NO_START},
+ "R|restart-after-upgrade!" => \$dh{RESTART_AFTER_UPGRADE},
+ "no-also" => \$dh{NO_ALSO}, # undocumented option
+});
+
+if ($dh{RESTART_AFTER_UPGRADE} eq '') {
+ $dh{RESTART_AFTER_UPGRADE} = 1 if not defined($dh{R_FLAG}) and $dh{NO_START} eq '';
+}
+
+sub quote {
+ # Add single quotes around the argument.
+ return '\'' . $_[0] . '\'';
+}
+
+sub uniq {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+sub contains_install_section {
+ my ($unit_path) = @_;
+
+ open(my $fh, '<', $unit_path) or error("Cannot open($unit_path): $!");
+
+ while (my $line = <$fh>) {
+ chomp($line);
+ return 1 if $line =~ /^\s*\[Install\]$/i;
+ }
+ close($fh);
+ return 0;
+}
+
+sub has_sysv_equivalent {
+ my ($tmpdir, $unit) = @_;
+
+ $unit =~ s/\.(?:mount|service|socket|target|path)$//g;
+ return -f "$tmpdir/etc/init.d/$unit";
+}
+
+sub install_unit {
+ my ($package, $script, $pkgsuffix, $path, $installsuffix) = @_;
+ $installsuffix = $installsuffix || $pkgsuffix;
+ my $unit = pkgfile($package, $pkgsuffix);
+ return if $unit eq '';
+ install_dir($path);
+ install_file($unit, "${path}/${script}.${installsuffix}");
+}
+
+# Extracts the directive values from a unit file. Handles repeated
+# directives in the same unit file. Assumes values can only be
+# composed of lists of unit names. This is good enough for the 'Also='
+# and 'Alias=' directives handled here.
+sub extract_key {
+ my ($unit_path, $key) = @_;
+ my @values;
+
+ open(my $fh, '<', $unit_path) or error("Cannot open($unit_path): $!");
+
+ while (my $line = <$fh>) {
+ chomp($line);
+
+ # Since unit names can't have whitespace in systemd, simply
+ # use split and strip any leading/trailing quotes. See
+ # systemd-escape(1) for examples of valid unit names.
+ if ($line =~ /^\s*$key=(.+)$/i) {
+ for my $value (split(/\s+/, $1)) {
+ $value =~ s/^(["'])(.*)\g1$/$2/;
+ push @values, $value;
+ }
+ }
+ }
+
+ close($fh);
+ return @values;
+}
+
+sub list_installed_units {
+ my ($tmpdir, $aliases) = @_;
+
+ my $lib_systemd_system = "$tmpdir/lib/systemd/system";
+ my @installed;
+
+ return unless -d $lib_systemd_system;
+ opendir(my $dh, "$lib_systemd_system") or error("Cannot opendir($lib_systemd_system): $!");
+
+ foreach my $name (readdir($dh)) {
+ my $path = "$lib_systemd_system/$name";
+ next unless -f $path;
+ if (-l "$path") {
+ my $dest = basename(readlink($path));
+ $aliases->{$dest} //= [ ];
+ push @{$aliases->{$dest}}, $name;
+ } else {
+ push @installed, $name;
+ }
+ }
+
+ closedir($dh);
+ return @installed;
+}
+
+
+# PROMISE: DH NOOP WITHOUT internal(bug#950723) tmp(lib/systemd/system) tmp(usr/lib/tmpfiles.d) tmp(etc/tmpfiles.d) mount path service socket target tmpfile timer cli-options()
+
+
+# Install package maintainer supplied unit files
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+
+ # Intall all unit files in the debian/ directory with names in the
+ # form $package.(service|target|socket|path|timer|mount|tmpfile)
+ # and their templated version when relevant.
+
+ # This can be modified with the --name option to look for unit
+ # files with names in the form $package.$name.(service|...) and
+ # $name.(service|target|socket|path|timer|mount|tmpfile) and their
+ # templated version when relevant.
+ my $name = $dh{NAME} // $package;
+
+ for my $type (qw(service target socket path timer)) {
+ install_unit($package, $name, $type, "$tmpdir/lib/systemd/system");
+ install_unit("${package}@", "${name}@", $type, "$tmpdir/lib/systemd/system");
+ }
+
+ install_unit($package, $name, 'mount', "$tmpdir/lib/systemd/system");
+ # In compat 13+, this is handled by dh_installtmpfiles
+ install_unit($package, $name, 'tmpfile', "$tmpdir/usr/lib/tmpfiles.d", 'conf') if compat(12);
+}
+
+
+if (compat(12)) {
+ # In compat 13+, this is handled by dh_installtmpfiles
+ # Add postinst code blocks to handle tmpfiles
+ foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+ my @tmpfiles;
+
+ my @dirs = grep { -d } map { "${tmpdir}/$_" } qw(usr/lib/tmpfiles.d etc/tmpfiles.d);
+
+ find({
+ wanted => sub {
+ my $name = $File::Find::name;
+ return if not -f $name or not $name =~ m{[.]conf$};
+ push(@tmpfiles, basename($name)); },
+ no_chdir => 1,
+ }, @dirs) if @dirs;
+
+ if (@tmpfiles) {
+ autoscript($package, 'postinst', 'postinst-init-tmpfiles', { 'TMPFILES' => join(' ', sort @tmpfiles) });
+ }
+ }
+}
+
+
+# Add postinst, prerm, and postrm code blocks to handle activation,
+# deactivation, start and stopping of services when the package is
+# installed, upgraded or removed.
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+ my (@args, @start_units, @enable_units, %aliases);
+
+ my @installed_units = list_installed_units($tmpdir, \%aliases);
+
+ # Handle either only the unit files which were passed as arguments
+ # or all unit files that are installed in this package.
+ if (@ARGV) {
+ @args = @ARGV;
+ }
+ elsif ($dh{NAME}) {
+ # Treat --name option as if the corresponding unit names were
+ # passed in the command line.
+ @args = grep /(^|\/)$dh{NAME}\.(mount|path|service|socket|target|timer)$/, @installed_units;
+ }
+ else {
+ @args = @installed_units;
+ }
+
+ # Support excluding units via the -X debhelper common option.
+ foreach my $x (@{$dh{EXCLUDE}}) {
+ @args = grep !/(^|\/)$x$/, @args;
+ }
+
+ # This hash prevents us from looping forever in the following
+ # while loop. An actual real-world example of such a loop is
+ # systemd's systemd-readahead-drop.service, which contains
+ # Also=systemd-readahead-collect.service, and that file in turn
+ # contains Also=systemd-readahead-drop.service, thus forming an
+ # endless loop.
+ my %seen;
+
+ # Must use while and shift because the loop alters the list.
+ while (@args) {
+ my $unit = shift @args;
+ my $path = "${tmpdir}/lib/systemd/system/${unit}";
+
+ error("Package '$package' does not install unit '$unit'.") unless (-f $path);
+
+ # Skip template service files. Enabling, disabling, starting
+ # or stopping those services without specifying the instance
+ # is not useful.
+ next if ($unit =~ /\@/);
+
+ # Handle all unit files specified via Also= explicitly. This
+ # is not necessary for enabling, but for disabling, as we
+ # cannot read the unit file when disabling as it has already
+ # been deleted. The undocumented --no-also option disables
+ # handling of units linked via Also=. This option is provided
+ # only to suport a very specific use case in network-manager.
+ unless ($dh{NO_ALSO}) {
+ push @args, $_ for grep { !$seen{$_}++ } extract_key($path, 'Also');
+ }
+
+ # Extract unit aliases.
+ push @{$aliases{$unit}}, $_ for extract_key($path, 'Alias');
+
+ # In compat 11 (and earlier), dh_installinit will handle services with
+ # a sysv-equivalent service. In compat 12, dh_installsystemd will
+ # take care of it.
+ if (not compat(11) or not grep { has_sysv_equivalent($tmpdir, $_) } ($unit, @{$aliases{$unit}})) {
+ push @start_units, $unit;
+ }
+
+ if (contains_install_section($path)) {
+ push @enable_units, $unit;
+ }
+ }
+
+ @enable_units = map { quote($_) } uniq sort @enable_units;
+ @start_units = map { quote($_) } uniq sort @start_units;
+
+ my %options = ('snippet-order' => 'service');
+
+ if (@enable_units) {
+ for my $unit (@enable_units) {
+ my $snippet = $dh{NO_ENABLE} ? 'postinst-systemd-dont-enable' : 'postinst-systemd-enable';
+ autoscript($package, 'postinst', $snippet, { 'UNITFILE' => $unit }, \%options);
+ }
+ autoscript($package, 'postrm', 'postrm-systemd', {'UNITFILES' => join(' ', @enable_units) });
+ }
+
+ if (@start_units) {
+ my $replace = { 'UNITFILES' => join(' ', @start_units) };
+
+ if ($dh{RESTART_AFTER_UPGRADE}) {
+ my $snippet;
+ if ($dh{NO_START}) {
+ $snippet = 'postinst-systemd-restartnostart';
+ $replace->{RESTART_ACTION} = 'try-restart';
+ } else {
+ $snippet = 'postinst-systemd-restart';
+ $replace->{RESTART_ACTION} = 'restart';
+ }
+ autoscript($package, 'postinst', $snippet, $replace, \%options);
+ } elsif (!$dh{NO_START}) {
+ # (stop|start) service (before|after) upgrade
+ autoscript($package, 'postinst', 'postinst-systemd-start', $replace, \%options);
+ }
+
+ # stop service before upgrade, if requested
+ autoscript($package, 'preinst', 'preinst-systemd-stop', $replace, \%options)
+ unless ($dh{R_FLAG} || $dh{RESTART_AFTER_UPGRADE});
+
+ # stop service only on remove
+ autoscript($package, 'prerm', 'prerm-systemd-restart', $replace, \%options)
+ unless ($dh{NO_START});
+
+ # Run this with "default" order so it is always after other
+ # service related autosnippets.
+ autoscript($package, 'postrm', 'postrm-systemd-reload-only', $replace);
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>, L<dh_installinit(1)>, L<deb-systemd-helper(1)>
+
+=head1 AUTHORS
+
+pkg-systemd-maintainers@lists.alioth.debian.org
+
+=cut
diff --git a/dh_installsystemduser b/dh_installsystemduser
new file mode 100755
index 0000000..7ab7933
--- /dev/null
+++ b/dh_installsystemduser
@@ -0,0 +1,288 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+dh_installsystemduser - install systemd unit files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installsystemduser> [S<I<debhelper options>>] [B<--no-enable>] [B<--name=>I<name>] [S<I<unit file> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installsystemduser> finds the systemd user instance service files
+installed by a package and generates F<preinst>, F<postinst>, and F<prerm>
+code blocks for enabling, disabling, starting, stopping, and restarting the
+corresponding systemd user instance services, when the package is installed,
+updated, or removed. These snippets are added to the maintainer scripts by
+L<dh_installdeb(1)>.
+
+L<deb-systemd-helper(1)> is used to enable and disable the systemd
+units, thus it is not necessary that the machine actually runs systemd
+during package installation time, enabling happens on all machines.
+
+B<dh_installsystemduser> operates on all user instance unit files
+installed by a package. For only generating blocks for specific unit
+files, pass them as arguments. Specific unit files can be excluded
+from processing using the B<-X> common L<debhelper(1)> option.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.user.path,
+ debian/I<package>@.user.path,
+ debian/I<package>.user.service,
+ debian/I<package>@.user.service,
+ debian/I<package>.user.socket,
+ debian/I<package>@.user.socket,
+ debian/I<package>.user.target,
+ debian/I<package>@.user.target,
+ debian/I<package>.user.timer,
+ debian/I<package>@.user.timer
+
+If any of those files exists, they are installed into
+F<usr/lib/systemd/user/> in the package build directory removing the
+F<.user> file name part.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+Install the service file as I<name.service> instead of the default
+filename I<package.service>. When this parameter is used,
+B<dh_installsystemd> looks for and installs files named
+F<debian/package.name.user.service> instead of the usual
+F<debian/package.user.service>. Moreover, maintainer scripts are only
+generated for units that match the given I<name>.
+
+=item B<--no-enable>
+
+Disable the service(s) on purge, but do not enable them on install.
+
+=back
+
+=head1 NOTES
+
+This command is not idempotent. L<dh_prep(1)> should be called between
+invocations of this command (with the same arguments). Otherwise, it
+may cause multiple instances of the same text to be added to
+maintainer scripts.
+
+=cut
+
+# PROMISE: DH NOOP WITHOUT internal(bug#950723) tmp(usr/lib/systemd/user) user.service
+
+init(options => {
+ "no-enable" => \$dh{NO_ENABLE},
+});
+
+sub quote {
+ # Add single quotes around the argument.
+ return '\'' . $_[0] . '\'';
+}
+
+sub uniq {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+sub contains_install_section {
+ my ($unit_path) = @_;
+
+ open(my $fh, '<', $unit_path) or error("Cannot open($unit_path) to check for [Install]: $!");
+
+ while (my $line = <$fh>) {
+ chomp($line);
+ return 1 if $line =~ /^\s*\[Install\]$/i;
+ }
+ close($fh);
+ return 0;
+}
+
+sub install_user_unit {
+ my ($package, $name, $suffix, $path) = @_;
+ my $unit = pkgfile($package, "user.$suffix");
+ return if $unit eq '';
+
+ install_dir($path);
+ install_file($unit, "$path/$name.$suffix");
+}
+
+# Extracts the directive values from a unit file. Handles repeated
+# directives in the same unit file. Assumes values can only be
+# composed of lists of unit names. This is good enough for the 'Also='
+# and 'Alias=' directives handled here.
+sub extract_key {
+ my ($unit_path, $key) = @_;
+ my @values;
+
+ open(my $fh, '<', $unit_path) or error("Cannot open($unit_path): $!");
+
+ while (my $line = <$fh>) {
+ chomp($line);
+
+ # Since unit names can't have whitespace in systemd, simply
+ # use split and strip any leading/trailing quotes. See
+ # systemd-escape(1) for examples of valid unit names.
+ if ($line =~ /^\s*$key=(.+)$/i) {
+ for my $value (split(/\s+/, $1)) {
+ $value =~ s/^(["'])(.*)\g1$/$2/;
+ push @values, $value;
+ }
+ }
+ }
+
+ close($fh);
+ return @values;
+}
+
+sub list_installed_user_units {
+ my ($tmpdir, $aliases) = @_;
+
+ my $lib_systemd_user = "$tmpdir/usr/lib/systemd/user";
+ my @installed;
+
+ return unless -d $lib_systemd_user;
+ opendir(my $dh, $lib_systemd_user) or error("Cannot opendir($lib_systemd_user): $!");
+
+ foreach my $name (readdir($dh)) {
+ my $path = "$lib_systemd_user/$name";
+ next unless -f $path;
+ if (-l $path) {
+ my $dest = basename(readlink($path));
+ $aliases->{$dest} //= [ ];
+ push @{$aliases->{$dest}}, $name;
+ } else {
+ push @installed, $name;
+ }
+ }
+
+ closedir($dh);
+ return @installed;
+}
+
+# Install package maintainer provided unit files.
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+
+ # unit file name
+ my $name = $dh{NAME} // $package;
+
+ my $path = "$tmpdir/usr/lib/systemd/user";
+ for my $type (qw(service target socket path timer)) {
+ install_user_unit($package, $name, $type, $path);
+ install_user_unit("${package}@", "${name}@", $type, $path);
+ }
+}
+
+# Generate postinst and prerm code blocks to enable and disable units
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my (@args, @start_units, @enable_units, %aliases);
+
+ my $tmpdir = tmpdir($package);
+ my @units = list_installed_user_units($tmpdir, \%aliases);
+
+ # Handle either only the unit files which were passed as arguments
+ # or all unit files that are installed in this package.
+ if (@ARGV) {
+ @args = @ARGV;
+ }
+ elsif ($dh{NAME}) {
+ # Treat --name flag as if the corresponding units were passed
+ # in the command line.
+ @args = grep /(^|\/)$dh{NAME}\.(service|target|socket|path|timer)$/, @units;
+ }
+ else {
+ @args = @units;
+ }
+
+ # Support excluding units via the -X debhelper common option.
+ foreach my $x (@{$dh{EXCLUDE}}) {
+ @args = grep !/(^|\/)$x$/, @args;
+ }
+
+ # This hash prevents us from looping forever in the following
+ # while loop. An actual real-world example of such a loop is
+ # systemd's systemd-readahead-drop.service, which contains
+ # Also=systemd-readahead-collect.service, and that file in turn
+ # contains Also=systemd-readahead-drop.service, thus forming an
+ # endless loop.
+ my %seen;
+
+ # Must use while and shift because the loop alters the list.
+ while (@args) {
+ my $name = shift @args;
+ my $path = "${tmpdir}/usr/lib/systemd/user/${name}";
+
+ error("User unit file \"$name\" not found in package \"$package\".") if ! -f $path;
+
+ # Skip template service files. Enabling or disabling those
+ # services without specifying the instance is not useful.
+ next if ($name =~ /\@/);
+
+ # Handle all unit files specified via Also= explicitly. This
+ # is not necessary for enabling, but for disabling, as we
+ # cannot read the unit file when disabling as it has already
+ # been deleted.
+ push @args, $_ for grep { !$seen{$_}++ } extract_key($path, 'Also');
+
+ push @enable_units, $name if contains_install_section($path);
+ push @start_units, $name;
+ }
+
+ @enable_units = map { quote($_) } sort(uniq(@enable_units));
+ @start_units = map { quote($_) } sort(uniq(@start_units));
+
+ if (@enable_units) {
+ # The generated maintainer script code blocks use the --user
+ # option that was added to deb-systemd-helper in version 1.52.
+ addsubstvar($package, 'misc:Depends', 'init-system-helpers', ">= 1.52");
+
+ my $postinst = $dh{NO_ENABLE} ? 'postinst-systemd-user-dont-enable' : 'postinst-systemd-user-enable';
+ foreach my $unit (@enable_units) {
+ autoscript($package, 'postinst', $postinst, { 'UNITFILE' => $unit });
+ }
+ autoscript($package, 'postrm', 'postrm-systemd-user', { 'UNITFILES' => join(' ', @enable_units) });
+ }
+
+ if (@start_units and not compat(13)) {
+ # The generated maintainer script code blocks use the --user
+ # option that was added to deb-systemd-invoke in version 1.61.
+ addsubstvar($package, 'misc:Depends', 'init-system-helpers', ">= 1.61~");
+
+ my %options = ('snippet-order' => 'service');
+
+ # restart service after install/upgrade
+ autoscript($package, 'postinst', 'postinst-systemd-user-restart', { 'UNITFILES' => join(' ', @start_units) }, \%options);
+
+ # stop service after removal
+ autoscript($package, 'prerm', 'prerm-systemd-user-stop', { 'UNITFILES' => join(' ', @start_units) }, \%options);
+
+ # Run this with "default" order so it is always after other
+ # service related autosnippets.
+ autoscript($package, 'postrm', 'postrm-systemd-user-reload-only', { 'UNITFILES' => join(' ', @start_units) });
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>, L<dh_installsystemd(1)>, L<deb-systemd-helper(1)>
+
+=head1 AUTHORS
+
+pkg-systemd-maintainers@lists.alioth.debian.org
+
+=cut
diff --git a/dh_installsysusers b/dh_installsysusers
new file mode 100755
index 0000000..faa4b8e
--- /dev/null
+++ b/dh_installsysusers
@@ -0,0 +1,115 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+dh_installsysusers - install and integrates systemd sysusers files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installsysusers> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+B<dh_installsysusers> is a debhelper program that is responsible for
+installing package maintainer supplied systemd sysusers files.
+
+It also finds the systemd sysusers files installed in a package and
+generates relevant integration snippets for enabling the users on
+installation. These snippets are added to the package by
+L<dh_installdeb(1)>.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.sysusers
+
+If the file exist, it will be installed as
+F<< /usr/lib/sysusers.d/I<package>.conf >>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+When this parameter is used, B<dh_installsysusers> looks for and
+installs files named debian/I<package>.I<name>.sysusers instead
+of the usual debian/I<package>.sysusers.
+
+Furthermore, the file is installed as F<< /usr/lib/sysusers.d/I<name>.conf >>
+rather than F<< /usr/lib/sysusers.d/I<package>.conf >>.
+
+=back
+
+=head1 NOTES
+
+This command is not idempotent. L<dh_prep(1)> should be called between
+invocations of this command (with the same arguments). Otherwise, it
+may cause multiple instances of the same text to be added to
+maintainer scripts.
+
+=cut
+
+init();
+
+
+# PROMISE: DH NOOP WITHOUT pkgfile(sysusers) tmp(usr/lib/sysusers.d) cli-options()
+
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+ my $sysusers = pkgfile($package,"sysusers");
+ my $sysusers_targetdir = "${tmpdir}/usr/lib/sysusers.d";
+ my $target = $dh{NAME} // $package;
+ my $typoed_name = pkgfile($package, "sysuser");
+
+ if ($sysusers eq '' and $typoed_name ne '') {
+ # Warn people in case they typo this as much as I did.
+ my $correct_name = $typoed_name;
+ $correct_name =~ s{^(?:.*[./])\Ksysuser}{sysusers};
+ warning("Possible typo in ${typoed_name} (expected ${correct_name}): File has been ignored");
+ }
+
+ if ($sysusers ne '') {
+ install_dir($sysusers_targetdir);
+ install_file($sysusers, "${sysusers_targetdir}/${target}.conf");
+ }
+
+ if (! $dh{NOSCRIPTS} && ($sysusers ne '' || -d $sysusers_targetdir)) {
+ my @sysusers_files;
+ opendir(my $dir_fd, $sysusers_targetdir) or error("opendir(${sysusers_targetdir}) failed: $!");
+ while (defined(my $entry = readdir($dir_fd))) {
+ next if $entry eq '.' or $entry eq '..' or $entry !~ m{[.]conf$};
+ push @sysusers_files, $entry;
+ }
+ closedir($dir_fd);
+
+ next if @sysusers_files == 0;
+
+ # Sort list of files so postinst content doesn't change if readdir's output is not stable
+ @sysusers_files = sort @sysusers_files;
+ # Generate a single systemd-sysusers invocation and just pass all detected files together
+ autoscript($package, 'postinst', 'postinst-sysusers', { 'CONFILE_BASENAME' => "@sysusers_files" });
+ addsubstvar($package, "misc:Depends", "systemd | systemd-standalone-sysusers | systemd-sysusers");
+ }
+
+}
+
+
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+=cut
diff --git a/dh_installtmpfiles b/dh_installtmpfiles
new file mode 100755
index 0000000..aa5ab72
--- /dev/null
+++ b/dh_installtmpfiles
@@ -0,0 +1,128 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+dh_installtmpfiles - install tmpfiles.d configuration files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+use File::Find;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installtmpfiles> [S<I<debhelper options>>][B<--name=>I<name>]
+
+=head1 DESCRIPTION
+
+B<dh_installtmpfiles> is a debhelper program that is responsible for
+installing package maintainer supplied tmpfiles.d configuration files
+(e.g. for systemd-tmpfiles).
+
+It also finds the tmpfiles.d configuration files installed by a package
+and generates F<postinst> code blocks for activating the tmpfiles.d
+configuration when the package is installed. These snippets are added
+to the maintainer scripts by L<dh_installdeb(1)>.
+
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+This option controls both a prefix used for lookng up maintainer provided
+tmpfiles.d configuration files (those mentioned in the L</FILES> section)
+and also the base name used for the installed version of the file.
+
+=back
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.tmpfiles
+
+If this exists, it is installed into F<usr/lib/tmpfiles.d/> in the
+package build directory. Note that the C<tmpfiles.d> mechanism is
+currently only used by systemd.
+
+=item debian/I<package>.tmpfile
+
+Deprecated name for debian/I<package>.tmpfiles.
+
+=back
+
+=head1 NOTES
+
+This command is not idempotent. L<dh_prep(1)> should be called between
+invocations of this command (with the same arguments). Otherwise, it
+may cause multiple instances of the same text to be added to
+maintainer scripts.
+
+=cut
+
+init();
+
+sub uniq {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+# PROMISE: DH NOOP WITHOUT tmp(usr/lib/tmpfiles.d) tmp(etc/tmpfiles.d) pkgfile(tmpfiles) pkgfile(tmpfile) cli-options()
+
+# Install package maintainer supplied tmpfiles files
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+ my $tmpfile = pkgfile($package, 'tmpfiles');
+ my $name = $dh{NAME} // $package;
+ my $old_tmpfile = pkgfile($package, 'tmpfile');
+ my $dir;
+ if (not $tmpfile) {
+ my $new_name;
+ next if not $old_tmpfile;
+ $tmpfile = $old_tmpfile;
+ $new_name = $old_tmpfile;
+ $new_name =~ s{^(.+[./])tmpfile(\..+|)$}{$1tmpfiles$2};
+ warning("The name $tmpfile is deprecated; please use $new_name instead");
+ warning(qq{Possible fix: mv -f "${tmpfile}" "${new_name}"});
+ } elsif ($old_tmpfile) {
+ warning("There is both a $tmpfile and a $old_tmpfile that is relevant for this package!?");
+ warning(qq{Possible fix: rm -f "${old_tmpfile}"});
+ error("Aborting; Please resolve the ambiguity between ${tmpfile} and ${old_tmpfile}.");
+ }
+
+ $dir = "$tmpdir/usr/lib/tmpfiles.d";
+ install_dir($dir);
+ install_file($tmpfile, "${dir}/${name}.conf");
+}
+
+# Add postinst code blocks to handle tmpfiles
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmpdir = tmpdir($package);
+ my @tmpfiles;
+
+ my @dirs = grep { -d } map { "${tmpdir}/$_" } qw(usr/lib/tmpfiles.d etc/tmpfiles.d);
+
+ find({
+ wanted => sub {
+ my $name = $File::Find::name;
+ return if not -f $name or not $name =~ m{[.]conf$};
+ push(@tmpfiles, basename($name)); },
+ no_chdir => 1,
+ }, @dirs) if @dirs;
+
+ if (@tmpfiles) {
+ autoscript($package, 'postinst', 'postinst-init-tmpfiles', { 'TMPFILES' => join(' ', uniq(sort(@tmpfiles))) });
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+=cut
diff --git a/dh_installudev b/dh_installudev
new file mode 100755
index 0000000..8e491d1
--- /dev/null
+++ b/dh_installudev
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installudev - install udev rules files
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installudev> [S<I<debhelper options>>] [B<-n>] [B<--name=>I<name>] [B<--priority=>I<priority>]
+
+=head1 DESCRIPTION
+
+B<dh_installudev> is a debhelper program that is responsible for
+installing B<udev> rules files.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.udev
+
+Installed into F<lib/udev/rules.d/> in the package build directory.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--name=>I<name>
+
+When this parameter is used, B<dh_installudev> looks for and
+installs files named debian/I<package>.I<name>.udev instead of the usual
+debian/I<package>.udev.
+
+=item B<--priority=>I<priority>
+
+Sets the priority the file. Default is 60.
+
+=back
+
+=head1 NOTES
+
+Note that this command is not idempotent. L<dh_prep(1)> should be called
+between invocations of this command. Otherwise, it may cause multiple
+instances of the same text to be added to maintainer scripts.
+
+=cut
+
+init(options => {
+ "priority=s" => \$dh{PRIORITY},
+});
+
+# The priority used to look like z60_;
+# we need to calculate that old value to handle
+# conffile moves correctly.
+my $old_priority=$dh{PRIORITY};
+
+# In case a caller still uses the `z` prefix, remove it.
+if (defined $dh{PRIORITY}) {
+ $dh{PRIORITY}=~s/^z//;
+}
+
+if (! defined $dh{PRIORITY}) {
+ $dh{PRIORITY}="60";
+ $old_priority="z60";
+}
+if ($dh{PRIORITY}) {
+ $dh{PRIORITY}.="-";
+ $old_priority.="_";
+}
+
+# PROMISE: DH NOOP WITHOUT udev cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $rules_file=pkgfile($package,"udev");
+ my $filename=basename($rules_file);
+ if ($filename eq 'udev') {
+ $filename = "$package.udev";
+ }
+ $filename=~s/\.udev$/.rules/;
+ if (defined $dh{NAME}) {
+ $filename="$dh{NAME}.rules";
+ }
+
+ if ($rules_file) {
+ my $rule="/lib/udev/rules.d/$dh{PRIORITY}$filename";
+ install_dir("$tmp/lib/udev/rules.d");
+ install_file($rules_file, "${tmp}${rule}");
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installwm b/dh_installwm
new file mode 100755
index 0000000..d5e0599
--- /dev/null
+++ b/dh_installwm
@@ -0,0 +1,140 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installwm - register a window manager
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installwm> [S<I<debhelper options>>] [B<-n>] [B<--priority=>I<n>] [S<I<wm> ...>]
+
+=head1 DESCRIPTION
+
+B<dh_installwm> is a debhelper program that is responsible for
+generating the F<postinst> and F<prerm> commands that register a window manager
+with L<update-alternatives(8)>. The window manager's man page is also
+registered as a slave symlink (in v6 mode and up). It must be installed in
+F<usr/share/man/man1/> in the package build directory prior to calling
+B<dh_installwm>. In compat 9 and earlier, the manpage was optional.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.wm
+
+List window manager programs to register.
+
+Supports substitution variables in compat 13 and later as
+documented in L<debhelper(7)>.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--priority=>I<n>
+
+Set the priority of the window manager. Default is 20, which is too low for
+most window managers; see the Debian Policy document for instructions on
+calculating the correct value.
+
+=item B<-n>, B<--no-scripts>
+
+Do not modify F<postinst>/F<prerm> scripts. Turns this command into a no-op.
+
+=item B<-A>, B<--all>
+
+Modify scripts for window managers specified by command line
+parameters in ALL packages acted on, not just the first.
+
+=item I<wm> ...
+
+Window manager programs to register.
+
+=back
+
+=head1 NOTES
+
+Note that this command is not idempotent. L<dh_prep(1)> should be called
+between invocations of this command. Otherwise, it may cause multiple
+instances of the same text to be added to maintainer scripts.
+
+=cut
+
+init(options => {
+ "priority=s" => \$dh{PRIORITY},
+});
+
+if (! defined $dh{PRIORITY}) {
+ $dh{PRIORITY}=20;
+}
+
+if (@ARGV) {
+ # This is here for backwards compatibility. If the filename doesn't
+ # include a path, assume it's in /usr/bin.
+ if ($ARGV[0] !~ m:/:) {
+ $ARGV[0]="/usr/bin/$ARGV[0]";
+ }
+}
+
+my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0;
+
+# PROMISE: DH NOOP WITHOUT wm cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+ my $file=pkgfile($package,"wm");
+
+ my @wm;
+ if ($file) {
+ @wm=filearray($file, '.');
+ }
+
+ if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) {
+ push @wm, @ARGV;
+ }
+
+ if (! $dh{NOSCRIPTS}) {
+WM: foreach my $wm (@wm) {
+ autoscript($package,"prerm","prerm-wm", { 'WM' => $wm });
+
+ my $wmman;
+ foreach my $ext (".1", ".1x") {
+ $wmman="/usr/share/man/man1/".basename($wm).$ext;
+ if (-e "$tmp$wmman" || -e "$tmp$wmman.gz") {
+ autoscript($package,"postinst","postinst-wm", { 'WM' => $wm, 'WMMAN' => "${wmman}.gz" , 'PRIORITY' => $dh{PRIORITY} });
+ next WM;
+ }
+ }
+ if (not compat(9) and not $nodocs) {
+ error("no manpage found (creating an x-window-manager alternative requires a slave symlink for the manpage)");
+ } else {
+ warning("no manpage found (creating an x-window-manager alternative requires a slave symlink for the manpage)");
+ }
+ # Reaching this code means a broken package will be produced.
+ autoscript($package,"postinst","postinst-wm-noman", { 'WM' => $wm, 'PRIORITY' => $dh{PRIORITY} });
+ }
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut
diff --git a/dh_installxfonts b/dh_installxfonts
new file mode 100755
index 0000000..c16659f
--- /dev/null
+++ b/dh_installxfonts
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_installxfonts - register X fonts
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_installxfonts> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+B<dh_installxfonts> is a debhelper program that is responsible for
+registering X fonts, so their corresponding F<fonts.dir>, F<fonts.alias>,
+and F<fonts.scale> be rebuilt properly at install time.
+
+Before calling this program, you should have installed any X fonts provided
+by your package into the appropriate location in the package build
+directory, and if you have F<fonts.alias> or F<fonts.scale> files, you should
+install them into the correct location under F<etc/X11/fonts> in your
+package build directory.
+
+Your package should depend on B<xfonts-utils> so that the
+B<update-fonts->I<*> commands are available. (This program adds that dependency to
+B<${misc:Depends}>.)
+
+This program automatically generates the F<postinst> and F<postrm> commands needed
+to register X fonts. These commands are inserted into the maintainer
+scripts by B<dh_installdeb>. See L<dh_installdeb(1)> for an explanation of how
+this works.
+
+=head1 NOTES
+
+See L<update-fonts-alias(8)>, L<update-fonts-scale(8)>, and
+L<update-fonts-dir(8)> for more information about X font installation.
+
+See Debian policy, section 11.8.5. for details about doing fonts the Debian
+way.
+
+=cut
+
+init();
+
+# PROMISE: DH NOOP WITHOUT tmp(usr/share/fonts/X11) cli-options()
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ my $tmp=tmpdir($package);
+
+ # Find all font directories in the package build directory.
+ my @fontdirs;
+ foreach my $parentdir ("$tmp/usr/share/fonts/X11/") {
+ opendir(DIR, $parentdir) || next;
+ @fontdirs = grep { -d "$parentdir/$_" && !/^\./ } (readdir DIR);
+ closedir DIR;
+ }
+
+ if (@fontdirs) {
+ # Figure out what commands the postinst and postrm will need
+ # to call.
+ my (@cmds, @cmds_postinst, @cmds_postrm);
+ # Sort items for reproducible binary package contents.
+ foreach my $f (sort @fontdirs) {
+ # This must come before update-fonts-dir.
+ push @cmds, "update-fonts-scale $f"
+ if -f "$tmp/etc/X11/fonts/$f/$package.scale";
+ push @cmds, "update-fonts-dir --x11r7-layout $f";
+ if (-f "$tmp/etc/X11/fonts/$f/$package.alias") {
+ push @cmds_postinst, "update-fonts-alias --include /etc/X11/fonts/$f/$package.alias $f";
+ push @cmds_postrm, "update-fonts-alias --exclude /etc/X11/fonts/$f/$package.alias $f";
+ }
+ }
+
+ autoscript($package, "postinst", "postinst-xfonts",
+ { 'CMDS' => join(";", @cmds, @cmds_postinst) });
+ autoscript($package, "postrm", "postrm-xfonts",
+ { 'CMDS' => join(";", @cmds, @cmds_postrm) });
+
+ addsubstvar($package, "misc:Depends", "xfonts-utils");
+ }
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut