#
# Conf.pm: configuration library for sbuild
# Copyright © 2005      Ryan Murray <rmurray@debian.org>
# Copyright © 2006-2010 Roger Leigh <rleigh@debian.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
#
#######################################################################

package Sbuild::Conf;

use strict;
use warnings;

use Cwd qw(cwd);
use File::Spec;
use POSIX  qw(getgroups getgid);
use Sbuild qw(isin);
use Sbuild::ConfBase;
use Sbuild::Sysconfig;
use Dpkg::BuildInfo;
use Sbuild::Utility qw(glob_to_regex natatime test_unshare);

BEGIN {
	use Exporter ();
	our (@ISA, @EXPORT);

	@ISA = qw(Exporter);

	@EXPORT = qw(new setup read);
}

my $ubuntu_mmdebstrap_extra_args = {};
use Debian::DistroInfo;
for my $series (UbuntuDistroInfo->new()->supported()) {
	$ubuntu_mmdebstrap_extra_args->{$series} = ['--components=main,universe'];
# We use snapshot.ubuntu.com here so it works both for ports and non-ports architectures
# without having to do complicated architecture detection logic
	$ubuntu_mmdebstrap_extra_args->{"$series-proposed"} = [
		'--components=main,universe',
		'--include=ca-certificates',
'--setup-hook=echo "deb [signed-by=/usr/share/keyrings/ubuntu-archive-keyring.gpg] https://snapshot.ubuntu.com/ubuntu '
		  . $series
		  . '-proposed main universe" > "$1"/etc/apt/sources.list.d/proposed.list',
		"--aptopt=APT::Default-Release \"$series\";" # this pins all pockets to 990 so we actually get proposed binaries
	];
	$ubuntu_mmdebstrap_extra_args->{"$series-backports"} = [
		'--components=main,universe',
		'--include=ca-certificates',
'--setup-hook=echo "deb [signed-by=/usr/share/keyrings/ubuntu-archive-keyring.gpg] https://snapshot.ubuntu.com/ubuntu '
		  . $series
		  . '-backports main universe" > "$1"/etc/apt/sources.list.d/backports.list'
	];
	# security we need to delete the -updates pocket
	$ubuntu_mmdebstrap_extra_args->{"$series-security"} = [
		'--components=main,universe',
		'--setup-hook=sed -i /-updates/d "$1"/etc/apt/sources.list',
	];
}

sub setup ($);
sub read ($);

sub new {
	my $conf = Sbuild::ConfBase->new(@_);
	Sbuild::Conf::setup($conf);
	Sbuild::Conf::read($conf);

	return $conf;
}

sub setup ($) {
	my $conf = shift;

	my $validate_program = sub {
		my $conf    = shift;
		my $entry   = shift;
		my $key     = $entry->{'NAME'};
		my $program = $conf->get($key);

		die "$key binary is not defined"
		  if !defined($program) || !$program;

		# Emulate execvp behaviour by searching the binary in the PATH.
		my @paths = split(/:/, $conf->get('PATH'));
		# Also consider the empty path for absolute locations.
		push(@paths, '');
		my $found = 0;
		foreach my $path (@paths) {
			$found = 1 if (-x File::Spec->catfile($path, $program));
		}

		die "$key binary '$program' does not exist or is not executable"
		  if !$found;
	};

	my $validate_directory = sub {
		my $conf      = shift;
		my $entry     = shift;
		my $key       = $entry->{'NAME'};
		my $directory = $conf->get($key);

		die "$key directory is not defined"
		  if !defined($directory) || !$directory;

		die "$key directory '$directory' does not exist"
		  if !-d $directory;
	};

	my $set_signing_option = sub {
		my $conf  = shift;
		my $entry = shift;
		my $value = shift;
		my $key   = $entry->{'NAME'};
		$conf->_set_value($key, $value);

		my @signing_options = ();
		push @signing_options, "-m" . $conf->get('MAINTAINER_NAME')
		  if defined $conf->get('MAINTAINER_NAME');
		push @signing_options, "-e" . $conf->get('UPLOADER_NAME')
		  if defined $conf->get('UPLOADER_NAME');
		$conf->set('SIGNING_OPTIONS', \@signing_options);
	};

	our $HOME = $conf->get('HOME');
	my $TMPDIR = $ENV{'TMPDIR'} // "/tmp";

	my @sbuild_keys = (
		'CHROOT' => {
			TYPE    => 'STRING',
			VARNAME => 'chroot',
			GROUP   => 'Chroot options',
			DEFAULT => undef,
			HELP    =>
'Default chroot (defaults to distribution[-arch][-sbuild]). In unshare mode, this setting is used to decide the name of the tarball in ~/.cache/sbuild. This setting does not influence what options are used to create the chroot in unshare mode (unless as the very last fallback for $unshare_mmdebstrap_extra_args). Use the distribution name (either in debian/changelog or by using --dist) for that.',
			CLI_OPTIONS => ['-c', '--chroot']
		},
		'BUILD_ARCH_ALL' => {
			TYPE    => 'BOOL',
			VARNAME => 'build_arch_all',
			GROUP   => 'Build options',
			DEFAULT => undef,
			GET     => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				if (!defined($retval)) {
					if ($conf->get('BUILD_ARCH') ne $conf->get('HOST_ARCH')) {
						# default for cross
						$retval = 0;
					} else {
						# default for native
						$retval = 1;
					}
				}

				return $retval;
			},
			HELP        => 'Build architecture: all packages by default.',
			CLI_OPTIONS => ['--arch-all', '--no-arch-all']
		},
		'BUILD_ARCH_ANY' => {
			TYPE        => 'BOOL',
			VARNAME     => 'build_arch_any',
			GROUP       => 'Build options',
			DEFAULT     => 1,
			HELP        => 'Build architecture: any packages by default.',
			CLI_OPTIONS => ['--arch-any', '--no-arch-any']
		},
		'BUILD_PROFILES' => {
			TYPE    => 'STRING',
			VARNAME => 'build_profiles',
			GROUP   => 'Build options',
			DEFAULT => undef,
			GET     => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				if (!defined($retval)) {
					if (exists $ENV{'DEB_BUILD_PROFILES'}) {
						$retval = $ENV{'DEB_BUILD_PROFILES'};
					} elsif (
						$conf->get('BUILD_ARCH') ne $conf->get('HOST_ARCH')) {
						$retval = 'cross nocheck';
					} else {
						# default for native
						$retval = '';
					}
				}

				return $retval;
			},
			HELP =>
'Build profiles. Separated by spaces. Defaults to the value of the DEB_BUILD_PROFILES environment variable when building natively and to the cross and nocheck profiles when cross-building.',
			CLI_OPTIONS => ['--profiles']
		},
		'SUDO' => {
			TYPE    => 'STRING',
			VARNAME => 'sudo',
			GROUP   => 'Programs',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				# Only validate if needed.
				if (
					$conf->get('CHROOT_MODE') eq 'split'
					|| (   $conf->get('CHROOT_MODE') eq 'schroot'
						&& $conf->get('CHROOT_SPLIT'))
				) {
					$validate_program->($conf, $entry);

					local (%ENV) = %ENV;    # make local environment
					$ENV{'DEBIAN_FRONTEND'} = "noninteractive";
					$ENV{'APT_CONFIG'}      = "test_apt_config";
					$ENV{'SHELL'}           = '/bin/sh';

					my $sudo = $conf->get('SUDO');
					chomp(my $test_df
						  = `$sudo sh -c 'echo \$DEBIAN_FRONTEND'`);
					chomp(my $test_ac = `$sudo sh -c 'echo \$APT_CONFIG'`);
					chomp(my $test_sh = `$sudo sh -c 'echo \$SHELL'`);

					if (   $test_df ne "noninteractive"
						|| $test_ac ne "test_apt_config"
						|| $test_sh ne '/bin/sh') {
						print STDERR
"$sudo is stripping APT_CONFIG, DEBIAN_FRONTEND and/or SHELL from the environment\n";
						print STDERR "'Defaults:"
						  . $conf->get('USERNAME')
						  . " env_keep+=\"APT_CONFIG DEBIAN_FRONTEND SHELL\"' is not set in /etc/sudoers\n";
						die "$sudo is incorrectly configured";
					}
				}
			},
			DEFAULT => 'sudo',
			HELP    => 'Path to sudo binary'
		},
		'SCHROOT' => {
			TYPE    => 'STRING',
			VARNAME => 'schroot',
			GROUP   => 'Programs',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				# Only validate if needed.
				if (defined $conf->_get('CHROOT_MODE')
					&& $conf->_get('CHROOT_MODE') eq 'schroot') {
					$validate_program->($conf, $entry);
				}
			},
			DEFAULT => 'schroot',
			HELP    => 'Path to schroot binary'
		},
		'SCHROOT_OPTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'schroot_options',
			GROUP   => 'Programs',
			DEFAULT => ['-q'],
			HELP    => 'Additional command-line options for schroot'
		},
		'UNSHARE_TMPDIR_TEMPLATE' => {
			TYPE    => 'STRING',
			VARNAME => 'unshare_tmpdir_template',
			GROUP   => 'Chroot options (unshare)',
			DEFAULT => "$TMPDIR/tmp.sbuild.XXXXXXXXXX",
			EXAMPLE => '# Choose /var/tmp if /tmp is too small
$unshare_tmpdir_template = \'/var/tmp/tmp.sbuild.XXXXXXXXXX\';',
			HELP =>
'Template used to create the temporary unpack directory for the unshare chroot mode. Uses $TMPDIR if set and /tmp otherwise. In unshare mode, all components of the path need to be accessible by the unshared user (world execute permissions). In unshare mode, the unshared user needs to have write permissions to this directory.'
			# CLI_OPTIONS => ['--unshare-tmpdir-template']
		},
		'UNSHARE_BIND_MOUNTS' => {
			TYPE    => 'ARRAY',
			VARNAME => 'unshare_bind_mounts',
			GROUP   => 'Chroot options (unshare)',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};
				for my $entry (@{ $conf->get($key) }) {
					die "$entry->{directory} doesn't exist"
					  if !-e $entry->{directory};
					die
"mountpoint $entry->{mountpoint} must be an absolute path inside the chroot"
					  if $entry->{mountpoint} !~ /^\//;
				}
			},
			DEFAULT => [],
			HELP    =>
'Bind mount directories from the outside to a mountpoint inside the chroot in unshare mode.',
			EXAMPLE =>
'$unshare_bind_mounts = [ { directory => "/home/path/outside", mountpoint => "/path/inside" } ];'
		},
		'UNSHARE_MMDEBSTRAP_AUTO_CREATE' => {
			TYPE    => 'BOOL',
			DEFAULT => 1,
			VARNAME => 'unshare_mmdebstrap_auto_create',
			GROUP   => 'Chroot options (unshare)',
			HELP    =>
'This is an experimental feature. In unshare mode, if the desired chroot tarball does not exist or if it is too old (see UNSHARE_MMDEBSTRAP_MAX_AGE), run mmdebstrap to create a new chroot that will be used for the build. Refer to UNSHARE_MMDEBSTRAP_EXTRA_ARGS to learn how to customize the mmdebstrap invocation for your chroots.'
		},
		'MMDEBSTRAP' => {
			TYPE    => 'STRING',
			VARNAME => 'mmdebstrap',
			GROUP   => 'Chroot options (unshare)',
			CHECK   => sub {
				# Do not check for the presence of mmdebstrap, even if
				# "unshare" is the chosen chroot mode. It should be perfectly
				# possible to run sbuild without mmdebstrap installed.
			},
			DEFAULT => 'mmdebstrap',
			HELP    => 'Path to mmdebstrap binary',
		},
		'UNSHARE_MMDEBSTRAP_KEEP_TARBALL' => {
			TYPE    => 'BOOL',
			DEFAULT => 0,
			VARNAME => 'unshare_mmdebstrap_keep_tarball',
			GROUP   => 'Chroot options (unshare)',
			HELP    =>
'This is an experimental feature. In unshare mode and only if UNSHARE_MMDEBSTRAP_AUTO_CREATE is true, write the created tarball back to its appropriate location in ~/.cache/sbuild/${release}-${arch}.tar. If a chroot tarball was given explicitly by passing a path with the --chroot option, that chroot will never be updated by sbuild. But if the chroot tarball was outdated (see UNSHARE_MMDEBSTRAP_MAX_AGE), it will still get re-created and used but not saved back to the given path.'
		},
		'UNSHARE_MMDEBSTRAP_EXTRA_ARGS' => {
			TYPE    => 'HASH:STRING',
			DEFAULT => [
qr/^(.*)-(backports|backports-sloppy|proposed-updates|updates)$/
				  => [
'--setup-hook=echo "deb http://deb.debian.org/debian %r main" > "$1"/etc/apt/sources.list.d/%r.list'
				  ],
				qr/^(experimental|rc-buggy)$/ => [
'--setup-hook=echo "deb http://deb.debian.org/debian %r main" > "$1"/etc/apt/sources.list.d/%r.list'
				],
				qr/^unreleased$/ => [
'--setup-hook=echo "deb http://deb.debian.org/debian-ports %r main" > "$1"/etc/apt/sources.list.d/%r.list'
				],
				%$ubuntu_mmdebstrap_extra_args,
			],
			GET => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});
				if (!defined $retval) {
					return [];
				}
				if (ref($retval) eq "HASH") {
					print STDERR
"E: Since sbuild 0.87.2, \$unshare_mmdebstrap_extra_args is an ARRAY, not a HASH. Please convert {\"foo\"=>[],\"bar\"=>[]} into [\"foo\"=>[],\"bar\"=>[]]\n";
					die
					  '$unshare_mmdebstrap_extra_args must be ARRAY, not HASH';
				}

				my $dist     = $conf->get('DISTRIBUTION');
				my $hostarch = $conf->get('HOST_ARCH');
				my %percent  = (
					'%'                   => '%',
					'a'                   => $hostarch,
					'SBUILD_HOST_ARCH'    => $hostarch,
					'r'                   => $dist,
					'SBUILD_DISTRIBUTION' => $dist,
				);

				my $keyword_pat = join("|",
					sort { length $b <=> length $a || $a cmp $b }
					  keys %percent);
				my $newretval = [];
				my $it        = natatime 2, @{$retval};
				while (my ($key, $oldval) = $it->()) {
					# first mangle the values
					my $newval = [];
					foreach my $val (@{$oldval}) {
						$val =~ s{\%($keyword_pat)}{$percent{$1} || $&}msxge;
						push @{$newval}, $val;
					}
					# then mangle the key unless the key is a regex
					if (ref($key) eq "Regexp") {
						push @{$newretval}, $key, $newval;
						next;
					}
					(my $newkey = $key) =~ s{
	                # Match a percent followed by a valid keyword
	                \%($keyword_pat)
	            }{
	                # Substitute with the appropriate value only if it's defined
	                $percent{$1} || $&
	            }msxge;
					# if key contains glob chars, turn it into a regex
					if ($newkey =~ m/[{}\[\]*?]/) {
						$newkey = glob_to_regex($newkey);
					}
					push @{$newretval}, $newkey, $newval;
				}
				return $newretval;
			},
			VARNAME => 'unshare_mmdebstrap_extra_args',

			GROUP => 'Chroot options (unshare)',
			HELP  =>
'This is an experimental feature. In unshare mode, when mmdebstrap is run because UNSHARE_MMDEBSTRAP_AUTO_CREATE was set to true, pass these extra arguments to the mmdebstrap invocation. The option array is given as key/value pairs. Each key will be matched against strings created from sbuild configuration variables, namely: DISTRIBUTION, DISTRIBUTION-BUILD_ARCH, DISTRIBUTION-BUILD_ARCH-HOST_ARCH as well as against the name of the chroot itself (defined if you use --chroot). If a key matches one of these strings, the value, containing extra mmdebstrap arguments is appended to the mmdebstrap argument list. A key can be a plain string, in which case glob-style expressions can be used. If the key is a plain string, it has to fully match. If the key is a plain string, percentage escapes %a and %r will be replaced by host architecture and distribution of the current build, respectively. A key can also be a precompiled qr// regular expression but match groups cannot be referenced in the extra arguments. The value is an array of extra arguments which are appended to the end of the mmdebstrap exec array.',
			EXAMPLE => '
$unshare_mmdebstrap_extra_args = [
   "*-%a-arm64" => [ ... ] # options for cross-builds with arm64 as the host architecture
   "debcargo-unstable-%a" => ["--include=dh-cargo,cargo"], # %a will be replaced by the host architecture
   "ubuntu-*" => [ "--components=main,universe,multiverse" ], # add universe and multiverse for ubuntu
   "/srv/custom-chroot.tar" => [ "--variant=apt", --arch="i386,ppc64el" ],
   qr/(jessie|stretch)-amd64/ => [ ... ] # do something special for jessie and stretch
   "{jessie,stretch}-amd64"   => [ ... ] # the same as above but with a glob instead of a regex
];'
		},
		'UNSHARE_MMDEBSTRAP_ENV_CMD' => {
			TYPE    => 'ARRAY',
			VARNAME => 'unshare_mmdebstrap_env_cmd',
			GROUP   => 'Chroot options (unshare)',
			DEFAULT => [],
			HELP    =>
'This is an experimental feature. In unshare mode, when mmdebstrap is used to create the chroot environment, prefix that command with this option array.',
			EXAMPLE =>
			  '$unshare_mmdebstrap_env_cmd = [ "env", "TMPDIR=/dev/shm/" ];'
		},
		'UNSHARE_MMDEBSTRAP_MAX_AGE' => {
			TYPE    => 'NUMERIC',
			DEFAULT => 604800,      # one week like the buildds use
			VARNAME => 'unshare_mmdebstrap_max_age',
			GROUP   => 'Chroot options (unshare)',
			HELP    =>
'This is an experimental feature. In unshare mode, with UNSHARE_MMDEBSTRAP_AUTO_CREATE=1, consider tarballs as outdated if they are older than the number of seconds given by this option. A negative value completely disables this check.'
		},
		'UNSHARE_MMDEBSTRAP_DISTRO_MANGLE' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'unshare_mmdebstrap_distro_mangle',
			GROUP   => 'Chroot options (unshare)',
			DEFAULT => [
				qr/^(experimental|rc-buggy|unreleased|UNRELEASED.*)$/ =>
				  'unstable',
qr/^(.*)-(security|backports|backports-sloppy|proposed|proposed-updates|updates)$/
				  => '$1',
			],
			HELP =>
'The distribution you want to build for might be an "overlay" for another distribution. For example if you build for stable-backports, you want to create a chroot for stable and then add backports or to build for experimental you want to build for unstable and then add experimental on top. This option allows one to perform this name mangling from distribution name to desired base distribution. The option array is given as substitution pairs. The first regex which matches is applied and the remaining regexes are skipped. If instead you want to define an alias of one name to another like the "aliases" schroot configuration option or symlinks in unshare mode, use the $chroot_aliases option instead. This option is an array instead of a hash because the order of entries matters.',
			EXAMPLE =>
"\$unshare_mmdebstrap_distro_mangle = [qr/(.*)-armhf\$/ => '\$1-arm64'];"
		},
		'ENABLE_NETWORK' => {
			TYPE    => 'STRING',
			VARNAME => 'enable_network',
			GROUP   => 'Build options',
			DEFAULT => 0,
			HELP    =>
'By default network access is blocked during build (only implemented for the unshare mode). This lifts the restriction.',
			CLI_OPTIONS => ['--enable-network']
		},
		'AUTOPKGTEST_VIRT_SERVER' => {
			TYPE    => 'STRING',
			VARNAME => 'autopkgtest_virt_server',
			GROUP   => 'Programs',
			CHECK   => sub {
				my $conf    = shift;
				my $entry   = shift;
				my $key     = $entry->{'NAME'};
				my $program = $conf->get($key);

				# if the autopkgtest virtualization server name is only letters
				# a-z then it is missing the autopkgtest-virt- prefix
				if ($program =~ /^[a-z]+$/) {
					$conf->set($key, "autopkgtest-virt-$program");
				}

				# Only validate if needed.
				if ($conf->get('CHROOT_MODE') eq 'autopkgtest') {
					$validate_program->($conf, $entry);
				}
			},
			DEFAULT => 'autopkgtest-virt-schroot',
			HELP    =>
'Path to autopkgtest-virt-* binary, selecting the virtualization server.',
			CLI_OPTIONS => ['--autopkgtest-virt-server']
		},
		'AUTOPKGTEST_VIRT_SERVER_OPTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'autopkgtest_virt_server_options',
			GROUP   => 'Programs',
			DEFAULT => [],
			GET     => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				my $dist     = $conf->get('DISTRIBUTION');
				my $hostarch = $conf->get('HOST_ARCH');
				my %percent  = (
					'%'                   => '%',
					'a'                   => $hostarch,
					'SBUILD_HOST_ARCH'    => $hostarch,
					'r'                   => $dist,
					'SBUILD_DISTRIBUTION' => $dist,
				);

				my $keyword_pat = join("|",
					sort { length $b <=> length $a || $a cmp $b }
					  keys %percent);
				foreach (@{$retval}) {
					s{
			# Match a percent followed by a valid keyword
			\%($keyword_pat)
		    }{
			# Substitute with the appropriate value only if it's defined
			$percent{$1} || $&
		    }msxge;
				}
				return $retval;
			},
			HELP => 'Additional command-line options for autopkgtest-virt-*',
			CLI_OPTIONS => [
				'--autopkgtest-virt-server-opt',
				'--autopkgtest-virt-server-opts'
			]
		},
		'APT_GET' => {
			TYPE    => 'STRING',
			VARNAME => 'apt_get',
			GROUP   => 'Programs',
			DEFAULT => 'apt-get',
			HELP    => 'Path to apt-get binary'
		},
		'APT_CACHE' => {
			TYPE    => 'STRING',
			VARNAME => 'apt_cache',
			GROUP   => 'Programs',
			DEFAULT => 'apt-cache',
			HELP    => 'Path to apt-cache binary'
		},
		'APTITUDE' => {
			TYPE    => 'STRING',
			VARNAME => 'aptitude',
			GROUP   => 'Programs',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				# Only validate if needed.
				if ($conf->get('BUILD_DEP_RESOLVER') eq 'aptitude') {
					$validate_program->($conf, $entry);
				}
			},
			DEFAULT => 'aptitude',
			HELP    => 'Path to aptitude binary'
		},
		'XAPT' => {
			TYPE    => 'STRING',
			VARNAME => 'xapt',
			GROUP   => 'Programs',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				# Only validate if needed.
				if ($conf->get('BUILD_DEP_RESOLVER') eq 'xapt') {
					$validate_program->($conf, $entry);
				}
			},
			DEFAULT => 'xapt'
		},
		'DPKG_BUILDPACKAGE_USER_OPTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'dpkg_buildpackage_user_options',
			GROUP   => 'Programs',
			DEFAULT => [],
			HELP => 'Additional command-line options for dpkg-buildpackage.',
			CLI_OPTIONS => ['--debbuildopt', '--debbuildopts', '--jobs']
		},
		'DPKG_FILE_SUFFIX' => {
			TYPE    => 'STRING',
			VARNAME => 'dpkg_file_suffix',
			GROUP   => 'Programs',
			DEFAULT => '',
			HELP    =>
'Suffix to add to filename for files generated by dpkg-buildpackage',
			CLI_OPTIONS => ['--dpkg-file-suffix']
		},
		'DPKG_SOURCE' => {
			TYPE    => 'STRING',
			VARNAME => 'dpkg_source',
			GROUP   => 'Programs',
			DEFAULT => 'dpkg-source',
			HELP    => 'Path to dpkg-source binary'
		},
		'DPKG_SOURCE_OPTIONS' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'dpkg_source_opts',
			GROUP       => 'Programs',
			DEFAULT     => [],
			HELP        => 'Additional command-line options for dpkg-source',
			CLI_OPTIONS => ['--dpkg-source-opt', '--dpkg-source-opts']
		},
		'STATS_DIR' => {
			TYPE           => 'STRING',
			VARNAME        => 'stats_dir',
			GROUP          => 'Statistics',
			IGNORE_DEFAULT => 1,               # Don't dump the current home
			DEFAULT        => "$HOME/stats",
			HELP           => 'Directory for writing build statistics to',
			CLI_OPTIONS    => ['--stats-dir']
		},
		'PACKAGE_CHECKLIST' => {
			TYPE    => 'STRING',
			VARNAME => 'package_checklist',
			GROUP   => 'Chroot options',
			DEFAULT =>
"$Sbuild::Sysconfig::paths{'SBUILD_LOCALSTATE_DIR'}/package-checklist",
			HELP =>
			  'Where to store list currently installed packages inside chroot'
		},
		'BUILD_ENV_CMND' => {
			TYPE    => 'STRING',
			VARNAME => 'build_env_cmnd',
			GROUP   => 'Build options',
			DEFAULT => "",
			HELP    =>
'This command is run with the dpkg-buildpackage command line passed to it (in the chroot, if doing a chrooted build).  It is used by the sparc buildd (which is sparc64) to call the wrapper script that sets the environment to sparc (32-bit).  It could be used for other build environment setup scripts.  Note that this is superseded by schroot\'s \'command-prefix\' option'
		},
		'PGP_OPTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'pgp_options',
			GROUP   => 'Build options',
			DEFAULT => ['-us', '-uc'],
			HELP    => 'Additional signing options for dpkg-buildpackage'
		},
		'NOLOG' => {
			TYPE        => 'BOOL',
			VARNAME     => 'nolog',
			GROUP       => 'Logging options',
			DEFAULT     => 0,
			HELP        => 'Disable use of log file',
			CLI_OPTIONS => ['-n', '--nolog']
		},
		'LOG_DIR' => {
			TYPE    => 'STRING',
			VARNAME => 'log_dir',
			GROUP   => 'Logging options',
			CHECK   => sub {
				my $conf      = shift;
				my $entry     = shift;
				my $key       = $entry->{'NAME'};
				my $directory = $conf->get($key);
			},
			GET => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				# user mode defaults to the build directory, while buildd mode
				# defaults to $HOME/logs.
				if (!defined($retval)) {
					$retval = $conf->get('BUILD_DIR');
					if ($conf->get('SBUILD_MODE') eq 'buildd') {
						$retval = "$HOME/logs";
					}
				}

				return $retval;
			},
			HELP =>
'Directory for storing build logs.  This defaults to \'.\' when SBUILD_MODE is set to \'user\' (the default), and to \'$HOME/logs\' when SBUILD_MODE is set to \'buildd\'.'
		},
		'LOG_FILENAME_TIMESTAMP_FORMAT' => {
			TYPE    => 'STRING',
			VARNAME => 'log_filename_timestamp_format',
			GROUP   => 'Logging options',
			DEFAULT => "%FT%TZ",
			HELP    =>
			  'Set the format of the timestamp used in the build log filename',
			CLI_OPTIONS => ['--log-filename-ts-format']
		},
		'LOG_COLOUR' => {
			TYPE    => 'BOOL',
			VARNAME => 'log_colour',
			GROUP   => 'Logging options',
			DEFAULT => 1,
			HELP    =>
'Add colour highlighting to interactive log messages (informational, warning and error messages).  Log files will not be coloured.'
		},
		'LOG_FILTER' => {
			TYPE    => 'BOOL',
			VARNAME => 'log_filter',
			GROUP   => 'Logging options',
			DEFAULT => 0,
			HELP    =>
'Filter variable strings from log messages such as the chroot name and build directory'
		},
		'LOG_DIR_AVAILABLE' => {
			TYPE  => 'BOOL',
			GROUP => '__INTERNAL',
			GET   => sub {
				my $conf  = shift;
				my $entry = shift;

				my $nolog             = $conf->get('NOLOG');
				my $directory         = $conf->get('LOG_DIR');
				my $log_dir_available = 1;

				if ($nolog) {
					$log_dir_available = 0;
				} elsif ($conf->get('SBUILD_MODE') ne "buildd") {
					if ($directory && !-d $directory) {
						$log_dir_available = 0;
					}
				} elsif ($directory
					&& !-d $directory
					&& !mkdir $directory) {
					# Only create the log dir in buildd mode
					warn "Could not create '$directory': $!\n";
					$log_dir_available = 0;
				}

				return $log_dir_available;
			}
		},
		'LOG_HIDDEN_SECTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'log_hidden_sections',
			GROUP   => 'Logging options',
			DEFAULT => [],
			HELP    =>
'Log sections to prevent from being printed to standard output. Supported section names that can be hidden are: postbuild, cleanup, changes, buildinfo, contents and summary.',
			CLI_OPTIONS => ['--hide-log-sections'],
		},
		'MAILTO' => {
			TYPE    => 'STRING',
			VARNAME => 'mailto',
			GROUP   => 'Logging options',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};
			},
			GET => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				# Now, we might need to adjust the MAILTO based on the
				# config data. We shouldn't do this if it was already
				# explicitly set by the command line option:
				if (   defined($conf->get('MAILTO_FORCED_BY_CLI'))
					&& !$conf->get('MAILTO_FORCED_BY_CLI')
					&& defined($conf->get('DISTRIBUTION'))
					&& $conf->get('DISTRIBUTION')
					&& defined($conf->get('MAILTO_HASH'))
					&& $conf->get('MAILTO_HASH')
					->{ $conf->get('DISTRIBUTION') }) {
					$retval = $conf->get('MAILTO_HASH')
					  ->{ $conf->get('DISTRIBUTION') };
				}

				return $retval;
			},
			DEFAULT     => "",
			HELP        => 'email address to mail build logs to',
			CLI_OPTIONS => ['--mail-log-to']
		},
		'MAILTO_FORCED_BY_CLI' => {
			TYPE    => 'BOOL',
			GROUP   => '__INTERNAL',
			DEFAULT => 0
		},
		'MAILTO_HASH' => {
			TYPE    => 'HASH:STRING',
			VARNAME => 'mailto_hash',
			GROUP   => 'Logging options',
			DEFAULT => {},
			HELP    =>
'Like MAILTO, but per-distribution.  This is a hashref mapping distribution name to MAILTO.  Note that for backward compatibility, this is also settable using the hash %mailto (deprecated), rather than a hash reference.'
		},
		'MAILFROM' => {
			TYPE        => 'STRING',
			VARNAME     => 'mailfrom',
			GROUP       => 'Logging options',
			DEFAULT     => "Source Builder <sbuild>",
			HELP        => 'email address set in the From line of build logs',
			CLI_OPTIONS => ['--mailfrom']
		},
		'COMPRESS_BUILD_LOG_MAILS' => {
			TYPE    => 'BOOL',
			VARNAME => 'compress_build_log_mails',
			GROUP   => 'Logging options',
			DEFAULT => 1,
			HELP    => 'Should build log mails be compressed?'
		},
		'MIME_BUILD_LOG_MAILS' => {
			TYPE    => 'BOOL',
			VARNAME => 'mime_build_log_mails',
			GROUP   => 'Logging options',
			DEFAULT => 1,
			HELP    => 'Should build log mails be MIME encoded?'
		},
		'PURGE_BUILD_DEPS' => {
			TYPE    => 'STRING',
			VARNAME => 'purge_build_deps',
			GROUP   => 'Chroot options',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				die "Bad purge mode \'"
				  . $conf->get('PURGE_BUILD_DEPS')
				  . "\'"
				  if !isin(
					$conf->get('PURGE_BUILD_DEPS'),
					qw(always successful never)
				  );
			},
			DEFAULT => 'always',
			HELP    =>
'When to purge the build dependencies after a build; possible values are "never", "successful", and "always"',
			CLI_OPTIONS => ['-p', '--purge', '--purge-deps']
		},
		'PURGE_BUILD_DIRECTORY' => {
			TYPE    => 'STRING',
			VARNAME => 'purge_build_directory',
			GROUP   => 'Chroot options',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				die "Bad purge mode \'"
				  . $conf->get('PURGE_BUILD_DIRECTORY')
				  . "\'"
				  if !isin($conf->get('PURGE_BUILD_DIRECTORY'),
					qw(always successful never));
			},
			DEFAULT => 'always',
			HELP    =>
'When to purge the build directory after a build; possible values are "never", "successful", and "always"',
			CLI_OPTIONS => ['-p', '--purge', '--purge-build']
		},
		'PURGE_SESSION' => {
			TYPE    => 'STRING',
			VARNAME => 'purge_session',
			GROUP   => 'Chroot options',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				die "Bad purge mode \'"
				  . $conf->get('PURGE_SESSION')
				  . "\'"
				  if !isin($conf->get('PURGE_SESSION'),
					qw(always successful never));
			},
			DEFAULT => 'always',
			HELP    =>
'Purge the schroot session following a build.  This is useful in conjunction with the --purge and --purge-deps options when using snapshot chroots, since by default the snapshot will be deleted. Possible values are "always" (default), "never", and "successful"',
			CLI_OPTIONS => ['-p', '--purge', '--purge-session']
		},
		'TOOLCHAIN_REGEX' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'toolchain_regex',
			GROUP   => 'Build options',
			DEFAULT => [
				'binutils$',             'dpkg-dev$',
				'gcc-[\d.]+$',           'g\+\+-[\d.]+$',
				'libstdc\+\+',           'libc[\d.]+-dev$',
				'linux-kernel-headers$', 'linux-libc-dev$',
				'gnumach-dev$',          'hurd-dev$',
				'kfreebsd-kernel-headers$'
			],
			HELP =>
'Regular expressions identifying toolchain packages.  Note that for backward compatibility, this is also settable using the array @toolchain_regex (deprecated), rather than an array reference.'
		},
		'STALLED_PKG_TIMEOUT' => {
			TYPE    => 'NUMERIC',
			VARNAME => 'stalled_pkg_timeout',
			GROUP   => 'Build timeouts',
			DEFAULT => 150,                     # minutes
			HELP    =>
'Time (in minutes) of inactivity after which a build is terminated. Activity is measured by output to the log file.'
		},
		'MAX_LOCK_TRYS' => {
			TYPE    => 'NUMERIC',
			VARNAME => 'max_lock_trys',
			GROUP   => 'Build timeouts',
			DEFAULT => 120,
			HELP    => 'Number of times to try waiting for a lock.'
		},
		'LOCK_INTERVAL' => {
			TYPE    => 'NUMERIC',
			VARNAME => 'lock_interval',
			GROUP   => 'Build timeouts',
			DEFAULT => 5,
			HELP    =>
'Lock wait interval (seconds).  Maximum wait time is (max_lock_trys x lock_interval).'
		},
		'CHROOT_MODE' => {
			TYPE    => 'STRING',
			VARNAME => 'chroot_mode',
			GROUP   => 'Chroot options',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				die "Bad chroot mode \'"
				  . $conf->get('CHROOT_MODE')
				  . "\'"
				  if !isin(
					$conf->get('CHROOT_MODE'),
					qw(schroot autopkgtest unshare)
				  );
				if (!defined($conf->_get($entry->{'NAME'}))) {
					my $config_path = '~/.config/sbuild/config.pl';
					if (length($ENV{'XDG_CONFIG_HOME'})) {
						$config_path
						  = $ENV{'XDG_CONFIG_HOME'} . '/sbuild/config.pl';
					}
				}
				if ($conf->get('CHROOT_MODE') eq 'schroot') {
					my $sbuild_schroot
					  = `dpkg-query --show --showformat='\${db:Status-Status}' sbuild-schroot`;
					if ($sbuild_schroot ne 'installed') {
						print STDERR
"Please make sure to have the sbuild-schroot package installed.\n";
					}
				}
			},
			DEFAULT => undef,
			GET     => sub {
				my $conf  = shift;
				my $entry = shift;

				my $default_mode = 'unshare';
				if (!test_unshare) {    # i.e. hurd
					$default_mode = 'schroot';
				}

				return ($conf->_get($entry->{'NAME'}) // $default_mode);
			},
			HELP =>
'Mechanism to use for chroot virtualisation.  Possible value are "schroot", "sudo", "autopkgtest" and "unshare" (default).',
			CLI_OPTIONS => ['--chroot-mode']
		},
		'CHROOT_ALIASES' => {
			TYPE    => 'HASH:STRING',
			VARNAME => 'chroot_aliases',
			GROUP   => 'Chroot options',
			# This list should not contain entries that might be valid
			# chroot names as otherwise a user of the schroot backend
			# will no longer be able to use chroots named that way as their
			# names would get mapped to different ones unless they change
			# the default mapping in their ~/.config/sbuild/config.pl
			# Only 1.3% of packages in the archive use 'sid' in d/changelog
			# anyways. Users of the unshare backend can either
			#  - live with sbuild managing a separate 'sid' chroot
			#  - change this mapping in their ~/.config/sbuild/config.pl
			#  - put a symlink into ~/.cache/sbuild
			DEFAULT => {
				"UNRELEASED" => "unstable",
				"rc-buggy"   => "experimental"
			},
			HELP =>
'A mapping of distribution names to their aliases which will be used to look up chroots. This is similar to the "aliases" schroot configuration option. With the unshare backend, this has a similar effect as placing a symlink into ~/.cache/sbuild. Use the $unshare_mmdebstrap_distro_mangle option instead of this option if you want to declare a mapping from an "overlay" distribution (like experimental or backcports) to its base. This option is a hash because the order of its entries does not matter. Note, that if you are using unshare mode, you might still need an entry in $unshare_mmdebstrap_distro_mangle in addition to an entry in this option because this option only influences the chroot lookup. The distribution name passed to mmdebstrap is not influenced by this option.'
		},
		'CHROOT_SPLIT' => {
			TYPE    => 'BOOL',
			VARNAME => 'chroot_split',
			GROUP   => 'Chroot options',
			DEFAULT => 0,
			HELP    =>
'Run in split mode?  In split mode, apt-get and dpkg are run on the host system, rather than inside the chroot.'
		},
		'CLEAN_APT_CACHE' => {
			TYPE    => 'BOOL',
			VARNAME => 'clean_apt_cache',
			GROUP   => 'Build options',
			DEFAULT => 1,
			HELP    =>
'Run apt-get distclean before the build to clean the apt package cache if the command is available. This makes sure that the build environment is not polluted by extra information but means that you need to run apt update before manually installing packages for debugging. You can disable this to save the extra call but remember to not use the information from the apt cache. Also note that this silently fails when the command is not available to be compatible with older releases.',
			CLI_OPTIONS => ['--apt-distclean', '--no-apt-distclean']
		},
		'CHECK_SPACE' => {
			TYPE    => 'BOOL',
			VARNAME => 'check_space',
			GROUP   => 'Build options',
			DEFAULT => 1,
			HELP    =>
'Check free disk space prior to starting a build.  sbuild requires the free space to be at least twice the size of the unpacked sources to allow a build to proceed.  Can be disabled to allow building if space is very limited, but the threshold to abort a build has been exceeded despite there being sufficient space for the build to complete.'
		},
		'BUILD_DIR' => {
			TYPE    => 'STRING',
			VARNAME => 'build_dir',
			GROUP   => 'Core options',
			DEFAULT => undef,
			GET     => sub {
				my $conf   = shift;
				my $entry  = shift;
				my $retval = $conf->_get($entry->{'NAME'});
				if (!defined($retval)) {
					$retval = cwd();
				}
				return $retval;
			},
			IGNORE_DEFAULT => 1,    # Don't dump class to config
			EXAMPLE        => '$build_dir = \'/home/pete/build\';',
			CHECK          => $validate_directory,
			HELP           =>
'Output directory for build artifacts created by dpkg-buildpackage and the log file. Defaults to the current directory if unspecified. It is used as the location of chroot symlinks (obsolete) and for current build log symlinks and some build logs.  There is no default; if unset, it defaults to the current working directory.  $HOME/build is another common configuration.',
			CLI_OPTIONS => ['--build-dir']
		},
		'BUILD_PATH' => {
			TYPE    => 'STRING',
			VARNAME => 'build_path',
			GROUP   => 'Build options',
			DEFAULT => '/build/reproducible-path',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};
				if ($conf->get($key) !~ /^($|\/)/) {
					die "BUILD_PATH must be an absolute path";
				}
			},
			HELP =>
'This option allows one to specify a custom path where the package is built inside the chroot. The sbuild user in the chroot must have permissions to create the path. Common writable locations are subdirectories of /tmp or /build. Using /tmp might be dangerous, because (depending on the chroot backend) the /tmp inside the chroot might be a world writable location that can be accessed by processes outside the chroot. The directory /build can only be accessed by the sbuild user and group and should be a safe location. The buildpath must be an empty directory because the last component of the path will be removed after the build is finished. Notice that depending on the chroot backend (see CHROOT_MODE), some locations inside the chroot might be bind mounts that are shared with other sbuild instances. You must avoid using these shared locations as the build path or otherwise concurrent runs of sbuild will likely fail. With the default schroot chroot backend, the directory /build is shared between multiple schroot sessions. You can change this behaviour in /etc/schroot/sbuild/fstab. The behaviour of other chroot backends will vary. To let sbuild choose a random build location of the format /build/packagename-XXXXXX/packagename-version/ where XXXXXX is a random ascii string, set this variable to the empty string.',
			CLI_OPTIONS => ['--build-path']
		},
		'DSC_DIR' => {
			TYPE    => 'STRING',
			VARNAME => 'dsc_dir',
			GROUP   => 'Build options',
			DEFAULT => undef,
			HELP    =>
'By default the package is built in a path of the following format /build/packagename-XXXXXX/packagename-version/ where packagename-version are replaced by the values in debian/changelog. This option allows one to specify a custom packagename-version path where the package is built inside the chroot. This is useful to specify a static path for different versions for example for ccache.',
			CLI_OPTIONS => ['--dsc-dir']
		},
		'SBUILD_MODE' => {
			TYPE    => 'STRING',
			VARNAME => 'sbuild_mode',
			GROUP   => 'Core options',
			DEFAULT => 'user',
			HELP    =>
'sbuild behaviour; possible values are "user" (exit status reports build failures) and "buildd" (exit status does not report build failures) for use in a buildd setup.  "buildd" also currently implies enabling of "legacy features" such as chroot symlinks in the build directory and the creation of current symlinks in the build directory.',
			CLI_OPTIONS => ['--sbuild-mode']
		},
		'CHROOT_SETUP_SCRIPT' => {
			TYPE    => 'STRING',
			VARNAME => 'chroot_setup_script',
			GROUP   => 'Chroot options',
			DEFAULT => undef,
			HELP    =>
			  'Script to run to perform custom setup tasks in the chroot.',
			CLI_OPTIONS => ['--setup-hook']
		},
		'FORCE_ORIG_SOURCE' => {
			TYPE    => 'BOOL',
			VARNAME => 'force_orig_source',
			GROUP   => 'Build options',
			DEFAULT => 0,
			HELP    =>
'By default, the -s option only includes the .orig.tar.gz when needed (i.e. when the Debian revision is 0 or 1).  By setting this option to 1, the .orig.tar.gz will always be included when -s is used.',
			CLI_OPTIONS => ['--force-orig-source']
		},
		'INDIVIDUAL_STALLED_PKG_TIMEOUT' => {
			TYPE    => 'HASH:NUMERIC',
			VARNAME => 'individual_stalled_pkg_timeout',
			GROUP   => 'Build timeouts',
			DEFAULT => {},
			HELP    =>
'Some packages may exceed the general timeout (e.g. redirecting output to a file) and need a different timeout.  This has is a mapping between source package name and timeout.  Note that for backward compatibility, this is also settable using the hash %individual_stalled_pkg_timeout (deprecated) , rather than a hash reference.',
			EXAMPLE =>
			  '$individual_stalled_pkg_timeout->{\'llvm-toolchain-3.8\'} = 300;
$individual_stalled_pkg_timeout->{\'kicad-packages3d\'} = 90;'
		},
		'ENVIRONMENT_FILTER' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'environment_filter',
			GROUP   => 'Core options',
			DEFAULT =>
			  [sort (map "^$_\$", Dpkg::BuildInfo::get_build_env_allowed())],
	#	    GET => sub {
	#		my $conf = shift;
	#		my $entry = shift;
	#
	#		my $retval = $conf->_get($entry->{'NAME'});
	#
	#		if (!defined($retval)) {
	#		    $retval = [ map "^$_\$", Dpkg::BuildInfo::get_build_env_allowed() ];
	#		}
	#
	#		return $retval;
	#	    },
			HELP =>
'Only environment variables matching one of the regular expressions in this arrayref will be passed to dpkg-buildpackage and other programs run by sbuild. The default value for this configuration setting is the list of variable names as returned by Dpkg::BuildInfo::get_build_env_allowed() which is also the list of variable names that is whitelisted to be recorded in .buildinfo files. Caution: the default value listed below was retrieved from the dpkg Perl library version available when this man page was generated. It might be different if your dpkg Perl library version differs.',
			EXAMPLE => '# Setting the old environment filter
$environment_filter = [\'^PATH$\',
			\'^DEB(IAN|SIGN)?_[A-Z_]+$\',
			\'^(C(PP|XX)?|LD|F)FLAGS(_APPEND)?$\',
			\'^USER(NAME)?$\',
			\'^LOGNAME$\',
			\'^HOME$\',
			\'^TERM$\',
			\'^SHELL$\'];
# Appending FOOBAR to the default
use Dpkg::BuildInfo;
$environment_filter = [(sort (map "^$_\$", Dpkg::BuildInfo::get_build_env_allowed())), \'^FOOBAR$\'];
# Removing FOOBAR from the default
use Dpkg::BuildInfo;
$environment_filter = [ sort (map /^FOOBAR$/ ? () : "^$_\$", Dpkg::BuildInfo::get_build_env_allowed()) ];
'
		},
		'BUILD_ENVIRONMENT' => {
			TYPE    => 'HASH:STRING',
			VARNAME => 'build_environment',
			GROUP   => 'Core options',
			DEFAULT => {},
			HELP    =>
'Environment to set during the build.  Defaults to setting PATH and LD_LIBRARY_PATH only.  Note that these environment variables are not subject to filtering with ENVIRONMENT_FILTER.  Example:',
			EXAMPLE => '$build_environment = {
        \'CCACHE_DIR\' => \'/build/cache\'
};'
		},
		'LD_LIBRARY_PATH' => {
			TYPE        => 'STRING',
			VARNAME     => 'ld_library_path',
			GROUP       => 'Build environment',
			DEFAULT     => undef,
			HELP        => 'Library search path to use inside the chroot.',
			CLI_OPTIONS => ['--use-snapshot']
		},
		'MAINTAINER_NAME' => {
			TYPE    => 'STRING',
			VARNAME => 'maintainer_name',
			GROUP   => 'Maintainer options',
			DEFAULT => undef,
			SET     => $set_signing_option,
			HELP    =>
'Name to use as override in .changes files for the Maintainer field.  The Maintainer field will not be overridden unless set here.',
			CLI_OPTIONS => ['-m', '--maintainer']
		},
		'UPLOADER_NAME' => {
			VARNAME => 'uploader_name',
			TYPE    => 'STRING',
			GROUP   => 'Maintainer options',
			DEFAULT => undef,
			SET     => $set_signing_option,
			HELP    =>
'Name to use as override in .changes file for the Changed-By: field.',
			CLI_OPTIONS => ['-e', '--uploader']
		},
		'KEY_ID' => {
			TYPE    => 'STRING',
			VARNAME => 'key_id',
			GROUP   => 'Maintainer options',
			DEFAULT => undef,
			HELP    =>
'Key ID to use in .changes for the current upload.  It overrides both $maintainer_name and $uploader_name.',
			CLI_OPTIONS => ['-k', '--keyid']
		},
		'SIGNING_OPTIONS' => {
			TYPE    => 'STRING',
			GROUP   => '__INTERNAL',
			DEFAULT => "",
			HELP    =>
'PGP-related identity options to pass to dpkg-buildpackage. Usually neither .dsc nor .changes files are not signed automatically.'
		},
		'APT_CLEAN' => {
			TYPE    => 'BOOL',
			VARNAME => 'apt_clean',
			GROUP   => 'Chroot options',
			DEFAULT => 0,
			HELP    =>
'APT clean.  1 to enable running "apt-get clean" at the start of each build, or 0 to disable.',
			CLI_OPTIONS => ['--apt-clean', '--no-apt-clean']
		},
		'APT_KEEP_DOWNLOADED_PACKAGES' => {
			TYPE    => 'BOOL',
			VARNAME => 'apt_keep_downloaded_packages',
			GROUP   => 'Chroot options',
			DEFAULT => 0,
			HELP    =>
'Keep downloaded packages in cache by APT. Controls APT::Keep-Downloaded-Packages option used when downloading dependencies. This option only makes sense if /var/cache/apt/archive inside the chroot is made persistent between multiple sbuild invocations. 1 to keep downloaded packages in cache, or 0 to delete them after installation.'
		},
		'APT_UPDATE' => {
			TYPE    => 'BOOL',
			VARNAME => 'apt_update',
			GROUP   => 'Chroot options',
			DEFAULT => 1,
			HELP    =>
'APT update.  1 to enable running "apt-get update" at the start of each build, or 0 to disable. This option only applies to the default repositories of the chroot: the internal sbuild apt repository, the repository for extra packages (see EXTRA_PACKAGES) and any repositories set via EXTRA_REPOSITORIES are always updated. It is not recommended to set this option to 0 because you should build using the latest available
packages in each distribution. If you do disable updates you need to ensure that the chroot contains downloaded package lists or apt will be unable to install any packages. If you are using the
unshare chroot mode you can add "--skip=cleanup/apt/lists" to UNSHARE_MMDEBSTRAP_EXTRA_ARGS to retain the package lists inside the chroot taball.',
			CLI_OPTIONS => ['--apt-update', '--no-apt-update']
		},
		'APT_UPGRADE' => {
			TYPE    => 'BOOL',
			VARNAME => 'apt_upgrade',
			GROUP   => 'Chroot options',
			DEFAULT => 0,
			HELP    =>
'APT upgrade.  1 to enable running "apt-get upgrade" at the start of each build, or 0 to disable.',
			CLI_OPTIONS => ['--apt-upgrade', '--no-apt-upgrade']
		},
		'APT_DISTUPGRADE' => {
			TYPE    => 'BOOL',
			VARNAME => 'apt_distupgrade',
			GROUP   => 'Chroot options',
			DEFAULT => 1,
			HELP    =>
'APT distupgrade.  1 to enable running "apt-get dist-upgrade" at the start of each build, or 0 to disable.',
			CLI_OPTIONS => ['--apt-distupgrade', '--no-apt-distupgrade']
		},
		'APT_ALLOW_UNAUTHENTICATED' => {
			TYPE    => 'BOOL',
			VARNAME => 'apt_allow_unauthenticated',
			GROUP   => 'Chroot options',
			DEFAULT => 0,
			HELP    =>
'Force APT to accept unauthenticated packages.  By default, unauthenticated packages are not allowed.  This is to keep the build environment secure, using apt-secure(8).  By setting this to 1, APT::Get::AllowUnauthenticated is set to "true" when running apt-get. This is disabled by default: only enable it if you know what you are doing.'
		},
		'BATCH_MODE' => {
			TYPE        => 'BOOL',
			VARNAME     => 'batch_mode',
			GROUP       => 'Core options',
			DEFAULT     => 0,
			HELP        => 'Enable batch mode?',
			CLI_OPTIONS => ['-b', '--batch']
		},
		'CORE_DEPENDS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'core_depends',
			GROUP   => 'Core options',
			DEFAULT => ['build-essential:native'],
			HELP    =>
			  'Packages which must be installed in the chroot for all builds.'
		},
		'MANUAL_DEPENDS' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'manual_depends',
			GROUP       => 'Core options',
			DEFAULT     => [],
			HELP        => 'Additional per-build dependencies.',
			CLI_OPTIONS => ['--add-depends']
		},
		'MANUAL_CONFLICTS' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'manual_conflicts',
			GROUP       => 'Core options',
			DEFAULT     => [],
			HELP        => 'Additional per-build dependencies.',
			CLI_OPTIONS => ['--add-conflicts']
		},
		'MANUAL_DEPENDS_ARCH' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'manual_depends_arch',
			GROUP       => 'Core options',
			DEFAULT     => [],
			HELP        => 'Additional per-build dependencies.',
			CLI_OPTIONS => ['--add-depends-arch']
		},
		'MANUAL_CONFLICTS_ARCH' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'manual_conflicts_arch',
			GROUP       => 'Core options',
			DEFAULT     => [],
			HELP        => 'Additional per-build dependencies.',
			CLI_OPTIONS => ['--add-conflicts-arch']
		},
		'MANUAL_DEPENDS_INDEP' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'manual_depends_indep',
			GROUP       => 'Core options',
			DEFAULT     => [],
			HELP        => 'Additional per-build dependencies.',
			CLI_OPTIONS => ['--add-depends-indep']
		},
		'MANUAL_CONFLICTS_INDEP' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'manual_conflicts_indep',
			GROUP       => 'Core options',
			DEFAULT     => [],
			HELP        => 'Additional per-build dependencies.',
			CLI_OPTIONS => ['--add-conflicts-indep']
		},
		'CROSSBUILD_CORE_DEPENDS' => {
			TYPE    => 'HASH:ARRAY:STRING',
			VARNAME => 'crossbuild_core_depends',
			GROUP   => 'Multiarch support (transitional)',
			DEFAULT => {},
			HELP    =>
'Per-architecture dependencies required for cross-building. By default, if a Debian architecture is not found as a key in this hash, the following will be added to the Build-Depends: crossbuild-essential-${hostarch}:native, libc-dev, libstdc++-dev. The latter two are to work around bug #815172.',
			EXAMPLE => '
$crossbuild_core_depends = {
    nios2 => [\'crossbuild-essential-nios2:native\', \'special-package\'],
    musl-linux-mips => [\'crossbuild-essential-musl-linux-mips:native\', \'super-special\'],
};
'
		},
		'BUILD_SOURCE' => {
			TYPE    => 'BOOL',
			VARNAME => 'build_source',
			GROUP   => 'Build options',
			DEFAULT => 0,
			HELP    =>
'By default, do not build a source package (binary only build).  Set to 1 to force creation of a source package, but note that this is inappropriate for binary NMUs, where the option will always be disabled.',
			CLI_OPTIONS => ['-s', '--source', '--no-source']
		},
		'ARCHIVE' => {
			TYPE    => 'STRING',
			VARNAME => 'archive',
			GROUP   => 'Core options',
			DEFAULT => undef,
			HELP    =>
'Archive being built.  Only set in build log.  This might be useful for derivative distributions.',
			CLI_OPTIONS => ['--archive']
		},
		'BIN_NMU' => {
			TYPE        => 'STRING',
			VARNAME     => 'bin_nmu',
			GROUP       => 'Build options',
			DEFAULT     => undef,
			HELP        => 'Binary NMU changelog entry.',
			CLI_OPTIONS => ['--make-binNMU']
		},
		'BIN_NMU_VERSION' => {
			TYPE        => 'STRING',
			VARNAME     => 'bin_nmu_version',
			GROUP       => 'Build options',
			DEFAULT     => undef,
			HELP        => 'Binary NMU version number.',
			CLI_OPTIONS => ['--binNMU', '--make-binNMU']
		},
		'BIN_NMU_TIMESTAMP' => {
			TYPE    => 'STRING',
			VARNAME => 'bin_nmu_timestamp',
			GROUP   => 'Build options',
			DEFAULT => 'increment',
			HELP    =>
'Binary NMU timestamp. It can be conveyed in several ways. An integer prefixed with a plus sign is considered a Unix time. The special value "now" is interpreted as the current time. The special value "increment" deterministically computes a timestamp from the NMU version and the last sourceful changelog timestamp. Any other value should be a string in the format compatible with Debian changelog entries (i.e. as it is generated by date -R). The "increment" special value is used by default.',
			CLI_OPTIONS => ['--binNMU-timestamp']
		},
		'APPEND_TO_VERSION' => {
			TYPE    => 'STRING',
			VARNAME => 'append_to_version',
			GROUP   => 'Build options',
			DEFAULT => undef,
			HELP    =>
'Suffix to append to version number.  May be useful for derivative distributions.',
			CLI_OPTIONS => ['--append-to-version']
		},
		'BIN_NMU_CHANGELOG' => {
			TYPE    => 'STRING',
			VARNAME => 'bin_nmu_changelog',
			GROUP   => 'Build options',
			DEFAULT => undef,
			HELP    =>
'The content of a binary-only changelog entry. Leading and trailing newlines will be stripped.',
			CLI_OPTIONS => ['--binNMU-changelog']
		},
		'GCC_SNAPSHOT' => {
			TYPE        => 'BOOL',
			VARNAME     => 'gcc_snapshot',
			GROUP       => 'Build options',
			DEFAULT     => 0,
			HELP        => 'Build using current GCC snapshot?',
			CLI_OPTIONS => ['--use-snapshot']
		},
		'JOB_FILE' => {
			TYPE    => 'STRING',
			VARNAME => 'job_file',
			GROUP   => 'Core options',
			DEFAULT => 'build-progress',
			HELP    => 'Job status file (only used in batch mode)'
		},
		'BUILD_DEP_RESOLVER' => {
			TYPE    => 'STRING',
			VARNAME => 'build_dep_resolver',
			GROUP   => 'Dependency resolution',
			DEFAULT => 'apt',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				if ($conf->get($key) eq 'internal') {
					warn
"W: Build dependency resolver 'internal' has been removed; defaulting to 'apt'.  Please update your configuration.\n";
					$conf->set('BUILD_DEP_RESOLVER', 'apt');
				}

				die '$key: Invalid build-dependency resolver \''
				  . $conf->get($key)
				  . "'\nValid algorithms are 'apt', 'aptitude', 'aspcud', 'xapt', and 'null'\n"
				  if !isin($conf->get($key),
					qw(apt aptitude aspcud xapt null));
			},
			HELP =>
'Build dependency resolver.  The \'apt\' resolver is currently the default, and recommended for most users.  This resolver uses apt-get to resolve dependencies.  Alternative resolvers are \'apt\', \'aptitude\' and \'aspcud\'. The \'apt\' resolver uses a built-in resolver module while the \'aptitude\' resolver uses aptitude to resolve build dependencies.  The aptitude resolver is similar to apt, but is useful in more complex situations, such as where multiple distributions are required, for example when building from experimental, where packages are needed from both unstable and experimental, but defaulting to unstable. If the dependency situation is too complex for either apt or aptitude to solve it, you can use the \'aspcud\' resolver which (in contrast to apt and aptitude) is a real solver (in the math sense) and will thus always find a solution if a solution exists. Additionally, the \'null\' solver is provided. It is a dummy resolver which does not install, upgrade or remove any packages. This allows one to completely control package installation via hooks.',
			CLI_OPTIONS => ['--build-dep-resolver']
		},
		'ASPCUD_CRITERIA' => {
			TYPE    => 'STRING',
			VARNAME => 'aspcud_criteria',
			GROUP   => 'Dependency resolution',
			DEFAULT => '-removed,-changed,-new',
			HELP    =>
'Optimization criteria in extended MISC 2012 syntax passed to aspcud through apt-cudf.  Optimization criteria are separated by commas, sorted by decreasing order of priority and are prefixed with a polarity (+ to maximize and - to minimize).  The default criteria is \'-removed,-changed,-new\' which first minimizes the number of removed packages, then the number of changed packages (up or downgrades) and then the number of new packages. A common task is to minimize the number of packages from experimental.  To do this you can add a criteria like \'-count(solution,APT-Release:=/a=experimental/)\' to the default criteria.  This will then minimize the number of packages in the solution which contain the string \'a=experimental\' in the \'APT-Release\' field of the EDSP output created by apt. See the apt-cudf man page help on the --criteria option for more information.',
			CLI_OPTIONS => ['--aspcud-criteria']
		},
		'CLEAN_SOURCE' => {
			TYPE    => 'BOOL',
			VARNAME => 'clean_source',
			GROUP   => 'Build options',
			DEFAULT => 1,
			HELP    =>
'When running sbuild from within an unpacked source tree, run the \'clean\' target before generating the source package. This might require some of the build dependencies necessary for running the \'clean\' target to be installed on the host machine. Only disable if you start from a clean checkout and you know what you are doing.',
			CLI_OPTIONS => ['--clean-source', '--no-clean-source']
		},
		'LINTIAN' => {
			TYPE    => 'STRING',
			VARNAME => 'lintian',
			GROUP   => 'Build validation',
			DEFAULT => 'lintian',
			HELP    => 'Path to lintian binary'
		},
		'RUN_LINTIAN' => {
			TYPE    => 'BOOL',
			VARNAME => 'run_lintian',
			GROUP   => 'Build validation',
			CHECK   => sub {
				my $conf = shift;
				$conf->check('LINTIAN');
			},
			DEFAULT     => 1,
			HELP        => 'Run lintian?',
			CLI_OPTIONS => ['--run-lintian', '--no-run-lintian']
		},
		'LINTIAN_OPTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'lintian_opts',
			GROUP   => 'Build validation',
			DEFAULT => [],
			HELP    =>
'Options to pass to lintian.  Each option is a separate arrayref element.  For example, [\'-i\', \'-v\'] to add -i and -v.',
			CLI_OPTIONS => ['--lintian-opt', '--lintian-opts']
		},
		'LINTIAN_REQUIRE_SUCCESS' => {
			TYPE    => 'BOOL',
			VARNAME => 'lintian_require_success',
			GROUP   => 'Build validation',
			DEFAULT => 0,
			HELP    => 'Let sbuild fail if lintian fails.'
		},
		'PIUPARTS' => {
			TYPE    => 'STRING',
			VARNAME => 'piuparts',
			GROUP   => 'Build validation',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				# Only validate if needed.
				if ($conf->get('RUN_PIUPARTS')) {
					$validate_program->($conf, $entry);
				}
			},
			DEFAULT     => 'piuparts',
			HELP        => 'Path to piuparts binary',
			CLI_OPTIONS => ['--piuparts-opt', '--piuparts-opts']
		},
		'RUN_PIUPARTS' => {
			TYPE    => 'BOOL',
			VARNAME => 'run_piuparts',
			GROUP   => 'Build validation',
			CHECK   => sub {
				my $conf = shift;
				$conf->check('PIUPARTS');
			},
			DEFAULT     => 0,
			HELP        => 'Run piuparts',
			CLI_OPTIONS => ['--run-piuparts', '--no-run-piuparts']
		},
		'PIUPARTS_OPTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'piuparts_opts',
			GROUP   => 'Build validation',
			DEFAULT => undef,
			GET     => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				if (!defined($retval)) {
					if ($conf->get('CHROOT_MODE') eq 'unshare') {
						$retval = [
							'--distribution=%SBUILD_DISTRIBUTION',
							'--arch=%SBUILD_HOST_ARCH',
"--bootstrapcmd=mmdebstrap --skip=check/empty --variant=minbase",
						];
					} else {
						$retval = [];
					}
				}

				my $dist     = $conf->get('DISTRIBUTION');
				my $hostarch = $conf->get('HOST_ARCH');
				my %percent  = (
					'%'                   => '%',
					'a'                   => $hostarch,
					'SBUILD_HOST_ARCH'    => $hostarch,
					'r'                   => $dist,
					'SBUILD_DISTRIBUTION' => $dist,
				);

				my $keyword_pat = join("|",
					sort { length $b <=> length $a || $a cmp $b }
					  keys %percent);
				foreach (@{$retval}) {
					s{
			# Match a percent followed by a valid keyword
			\%($keyword_pat)
		    }{
			# Substitute with the appropriate value only if it's defined
			$percent{$1} || $&
		    }msxge;
				}
				return $retval;
			},
			HELP =>
'Options to pass to piuparts.  Each option is a separate arrayref element.  For example, [\'-b\', \'<chroot_tarball>\'] to add -b and <chroot_tarball>.'
		},
		'PIUPARTS_ROOT_ARGS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'piuparts_root_args',
			GROUP   => 'Build validation',
			DEFAULT => [],
			HELP    =>
'Preceding arguments to launch piuparts as root. With the default value (the empty array) "sudo --" will be used as a prefix unless sbuild is run in unshare mode. If the first element in the array is the empty string, no prefixing will be done. If the value is a scalar, it will be prefixed by that string. If the scalar is an empty string, no prefixing will be done.',
			EXAMPLE => '# prefix with "sudo --":
$piuparts_root_args = [];
$piuparts_root_args = [\'sudo\', \'--\'];
# prefix with "env":
$piuparts_root_args = [\'env\'];
$piuparts_root_args = \'env\';
# prefix with nothing:
$piuparts_root_args = \'\';
$piuparts_root_args = [\'\'];
$piuparts_root_args = [\'\', \'whatever\'];
',
			CLI_OPTIONS => ['--piuparts-root-arg', '--piuparts-root-args']
		},
		'PIUPARTS_REQUIRE_SUCCESS' => {
			TYPE    => 'BOOL',
			VARNAME => 'piuparts_require_success',
			GROUP   => 'Build validation',
			DEFAULT => 0,
			HELP    => 'Let sbuild fail if piuparts fails.'
		},
		'AUTOPKGTEST' => {
			TYPE    => 'STRING',
			VARNAME => 'autopkgtest',
			GROUP   => 'Build validation',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				# Only validate if needed.
				if ($conf->get('RUN_AUTOPKGTEST')) {
					$validate_program->($conf, $entry);
				}
			},
			DEFAULT     => 'autopkgtest',
			HELP        => 'Path to autopkgtest binary',
			CLI_OPTIONS => ['--autopkgtest-opt', '--autopkgtest-opts']
		},
		'RUN_AUTOPKGTEST' => {
			TYPE    => 'BOOL',
			VARNAME => 'run_autopkgtest',
			GROUP   => 'Build validation',
			CHECK   => sub {
				my $conf = shift;
				$conf->check('AUTOPKGTEST');
			},
			DEFAULT     => 0,
			HELP        => 'Run autopkgtest',
			CLI_OPTIONS => ['--run-autopkgtest', '--no-run-autopkgtest']
		},
		'AUTOPKGTEST_OPTIONS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'autopkgtest_opts',
			GROUP   => 'Build validation',
			DEFAULT => undef,
			GET     => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				if (!defined($retval)) {
					if ($conf->get('CHROOT_MODE') eq 'unshare') {
						$retval = [
							'--',     'unshare', '--release', '%r',
							'--arch', '%a'
						];
					} else {
						$retval = [];
					}
				}

				my $dist     = $conf->get('DISTRIBUTION');
				my $hostarch = $conf->get('HOST_ARCH');
				my %percent  = (
					'%'                   => '%',
					'a'                   => $hostarch,
					'SBUILD_HOST_ARCH'    => $hostarch,
					'r'                   => $dist,
					'SBUILD_DISTRIBUTION' => $dist,
				);

				my $keyword_pat = join("|",
					sort { length $b <=> length $a || $a cmp $b }
					  keys %percent);
				foreach (@{$retval}) {
					s{
			# Match a percent followed by a valid keyword
			\%($keyword_pat)
		    }{
			# Substitute with the appropriate value only if it's defined
			$percent{$1} || $&
		    }msxge;
				}
				return $retval;
			},
			HELP =>
'Options to pass to autopkgtest.  Each option is a separate arrayref element.  For example, [\'-b\', \'<chroot_tarball>\'] to add -b and <chroot_tarball>.'
		},
		'AUTOPKGTEST_ROOT_ARGS' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'autopkgtest_root_args',
			GROUP   => 'Build validation',
			DEFAULT => [],
			HELP    =>
'Preceding arguments to launch autopkgtest as root. With the default value (the empty array) "sudo --" will be used as a prefix unless sbuild is run in unshare mode. If the first element in the array is the empty string, no prefixing will be done. If the value is a scalar, it will be prefixed by that string. If the scalar is an empty string, no prefixing will be done.',
			EXAMPLE => '# prefix with "sudo --":
$autopkgtest_root_args = [];
$autopkgtest_root_args = [\'sudo\', \'--\'];
# prefix with "env":
$autopkgtest_root_args = [\'env\'];
$autopkgtest_root_args = \'env\';
# prefix with nothing:
$autopkgtest_root_args = \'\';
$autopkgtest_root_args = [\'\'];
$autopkgtest_root_args = [\'\', \'whatever\'];
',
			CLI_OPTIONS =>
			  ['--autopkgtest-root-arg', '--autopkgtest-root-args']
		},
		'AUTOPKGTEST_REQUIRE_SUCCESS' => {
			TYPE    => 'BOOL',
			VARNAME => 'autopkgtest_require_success',
			GROUP   => 'Build validation',
			DEFAULT => 0,
			HELP    => 'Let sbuild fail if autopkgtest fails.'
		},
		'EXTERNAL_COMMANDS' => {
			TYPE    => 'HASH:ARRAY:STRING',
			VARNAME => 'external_commands',
			GROUP   => 'Chroot options',
			DEFAULT => {
				"pre-build-commands"            => [],
				"chroot-setup-commands"         => [],
				"chroot-update-failed-commands" => [],
				"build-deps-failed-commands"    => [],
				"build-failed-commands"         => [],
				"starting-build-commands"       => [],
				"finished-build-commands"       => [],
				"chroot-cleanup-commands"       => [],
				"post-build-commands"           => [],
			},
			HELP =>
'External commands to run at various stages of a build. Commands are held in a hash of arrays of arrays data structure. There is no equivalent for the --anything-failed-commands command line option. All percent escapes mentioned in the sbuild man page can be used.',
			EXAMPLE => '# general format
$external_commands = {
    "pre-build-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "chroot-setup-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "chroot-update-failed-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "build-deps-failed-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "build-failed-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "starting-build-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "finished-build-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "chroot-cleanup-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "post-build-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
    "post-build-failed-commands" => [
        [\'foo\', \'arg1\', \'arg2\'],
        [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
    ],
};
# the equivalent of specifying --anything-failed-commands=%SBUILD_SHELL on the
# command line
$external_commands = {
    "chroot-update-failed-commands" => [ [ \'%SBUILD_SHELL\' ] ],
    "build-deps-failed-commands" => [ [ \'%SBUILD_SHELL\' ] ],
    "build-failed-commands" => [ [ \'%SBUILD_SHELL\' ] ],
    "post-build-failed-commands" => [ [ \'%SBUILD_SHELL\' ] ],
};',
			CLI_OPTIONS => [
				'--setup-hook',
				'--pre-build-commands',
				'--chroot-setup-commands',
				'--chroot-update-failed-commands',
				'--build-deps-failed-commands',
				'--build-failed-commands',
				'--anything-failed-commands',
				'--starting-build-commands',
				'--finished-build-commands',
				'--chroot-cleanup-commands',
				'--post-build-commands',
				'--post-build-failed-commands'
			]
		},
		'LOG_EXTERNAL_COMMAND_OUTPUT' => {
			TYPE        => 'BOOL',
			VARNAME     => 'log_external_command_output',
			GROUP       => 'Chroot options',
			DEFAULT     => 1,
			HELP        => 'Log standard output of commands run by sbuild?',
			CLI_OPTIONS => ['--log-external-command-output']
		},
		'LOG_EXTERNAL_COMMAND_ERROR' => {
			TYPE        => 'BOOL',
			VARNAME     => 'log_external_command_error',
			GROUP       => 'Chroot options',
			DEFAULT     => 1,
			HELP        => 'Log standard error of commands run by sbuild?',
			CLI_OPTIONS => ['--log-external-command-error']
		},
		'RESOLVE_ALTERNATIVES' => {
			TYPE    => 'BOOL',
			VARNAME => 'resolve_alternatives',
			GROUP   => 'Dependency resolution',
			DEFAULT => undef,
			GET     => sub {
				my $conf  = shift;
				my $entry = shift;

				my $retval = $conf->_get($entry->{'NAME'});

				if (!defined($retval)) {
					$retval = 0;
					$retval = 1
					  if ($conf->get('BUILD_DEP_RESOLVER') eq 'aptitude');
				}

				return $retval;
			},
			EXAMPLE => '$resolve_alternatives = 0;',
			HELP    =>
'Should the dependency resolver use alternatives in Build-Depends, Build-Depends-Arch and Build-Depends-Indep?  By default, using \'apt\' resolver, only the first alternative will be used; all other alternatives will be removed.  When using the \'aptitude\' resolver, it will default to using all alternatives.  Note that this does not include architecture-specific alternatives, which are reduced to the build architecture prior to alternatives removal.  This should be left disabled when building for unstable; it may be useful when building for experimental or backports.  Set to undef to use the default, 1 to enable, or 0 to disable.',
			CLI_OPTIONS =>
			  ['--resolve-alternatives', '--no-resolve-alternatives']
		},
		'EXTRA_PACKAGES' => {
			TYPE    => 'ARRAY:STRING',
			VARNAME => 'extra_packages',
			GROUP   => 'Dependency resolution',
			DEFAULT => [],
			HELP    =>
			  'Additional per-build packages available as build dependencies.',
			CLI_OPTIONS => ['--extra-package']
		},
		'EXTRA_REPOSITORY_KEYS' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'extra_repository_keys',
			GROUP       => 'Dependency resolution',
			DEFAULT     => [],
			HELP        => 'Additional per-build apt repository keys.',
			CLI_OPTIONS => ['--extra-repository-key']
		},
		'EXTRA_REPOSITORIES' => {
			TYPE        => 'ARRAY:STRING',
			VARNAME     => 'extra_repositories',
			GROUP       => 'Dependency resolution',
			DEFAULT     => [],
			HELP        => 'Additional per-build apt repositories.',
			CLI_OPTIONS => ['--extra-repository']
		},
		'SOURCE_ONLY_CHANGES' => {
			TYPE    => 'BOOL',
			VARNAME => 'source_only_changes',
			GROUP   => 'Build options',
			DEFAULT => 0,
			HELP    =>
			  'Also produce a changes file suitable for a source-only upload.',
			CLI_OPTIONS => ['--source-only-changes']
		},
		'BUILD_AS_ROOT_WHEN_NEEDED' => {
			TYPE    => 'BOOL',
			VARNAME => 'build_as_root_when_needed',
			GROUP   => 'Build options',
			DEFAULT => 0,
			HELP    =>
'Build packages as root when Rules-Requires-Root: binary-targets.'
		},
		'BD_UNINSTALLABLE_EXPLAINER' => {
			TYPE    => 'STRING',
			VARNAME => 'bd_uninstallable_explainer',
			GROUP   => 'Dependency resolution',
			CHECK   => sub {
				my $conf  = shift;
				my $entry = shift;
				my $key   = $entry->{'NAME'};

				die "Bad bd-uninstallable explainer \'"
				  . $conf->get('BD_UNINSTALLABLE_EXPLAINER')
				  . "\'"
				  if defined $conf->get('BD_UNINSTALLABLE_EXPLAINER')
				  && !isin($conf->get('BD_UNINSTALLABLE_EXPLAINER'),
					('apt', 'dose3', '', 'none'));
			},
			DEFAULT => 'dose3',
			HELP    =>
'Method to use for explaining build dependency installation failures. Possible value are "dose3" (default), "apt" and "none". Set to "none", the empty string "" or Perl undef to disable running any explainer.',
			CLI_OPTIONS => ['--bd-uninstallable-explainer']
		},
		'PURGE_EXTRA_PACKAGES' => {
			TYPE    => 'BOOL',
			VARNAME => 'purge_extra_packages',
			GROUP   => 'Chroot options',
			DEFAULT => 0,
			HELP    =>
'Try to remove all additional packages that are not strictly required for the build right after build dependencies were installed. This currently works best with the aspcud resolver. The apt resolver will not make as much effort to remove all unneeded packages and will keep all providers of a virtual package and all packages from any dependency alternative that happen to be installed. The aptitude and xapt resolver do not implement this feature yet. The removed packages are not yet added again after the build finished. This can have undesirable side effects like lintian not working (because there is no apt to install its dependencies) or bare chroots becoming totally unusable after apt was removed from them. Thus, this option should only be used with throw-away chroots like schroot provides them where the original state is automatically restored after each build.',
			CLI_OPTIONS => ['--purge-extra-packages'] });

	$conf->set_allowed_keys(@sbuild_keys);
}

sub read ($) {
	my $conf = shift;

	# Set here to allow user to override.
	if (-t STDIN && -t STDOUT) {
		$conf->_set_default('VERBOSE', 1);
	} else {
		$conf->_set_default('VERBOSE', 0);
	}

	my $HOME = $conf->get('HOME');

	my $files = ["$Sbuild::Sysconfig::paths{'SBUILD_CONF'}"];

	# prefer ~/.config/sbuild/config.pl over ~/.sbuildrc
	my $sbuild_config_home = $conf->get('HOME') . "/.config/sbuild";
	if (length($ENV{'XDG_CONFIG_HOME'})) {
		$sbuild_config_home = $ENV{'XDG_CONFIG_HOME'} . '/sbuild';
	}
	if (-r "$sbuild_config_home/config.pl") {
		push @{$files}, "$sbuild_config_home/config.pl";
	} elsif (-r "$HOME/.sbuildrc") {
		print STDOUT ("I: consider moving your ~/.sbuildrc "
			  . "to $sbuild_config_home/config.pl\n");
		push @{$files}, "$HOME/.sbuildrc";
	}

	push @{$files}, $ENV{'SBUILD_CONFIG'} if defined $ENV{'SBUILD_CONFIG'};

	# For compatibility only.  Non-scalars are deprecated.
	my $deprecated_init = <<END;
my \%mailto;
undef \%mailto;
my \@toolchain_regex;
undef \@toolchain_regex;
my \%individual_stalled_pkg_timeout;
undef \%individual_stalled_pkg_timeout;
END

	my $deprecated_setup = <<END;
# Non-scalar values, for backward compatibility.
if (\%mailto) {
    warn 'W: \%mailto is deprecated; please use the hash reference \$mailto{}\n';
    \$conf->set('MAILTO_HASH', \\\%mailto);
}
if (\@toolchain_regex) {
    warn 'W: \@toolchain_regex is deprecated; please use the array reference \$toolchain_regexp[]\n';
    \$conf->set('TOOLCHAIN_REGEX', \\\@toolchain_regex);
}
if (\%individual_stalled_pkg_timeout) {
    warn 'W: \%individual_stalled_pkg_timeout is deprecated; please use the hash reference \$individual_stalled_pkg_timeout{}\n';
    \$conf->set('INDIVIDUAL_STALLED_PKG_TIMEOUT',
		\\\%individual_stalled_pkg_timeout);
}
END

	my $custom_setup = <<END;
push(\@{\${\$conf->get('EXTERNAL_COMMANDS')}{"chroot-user-setup-commands"}},
\$chroot_setup_script) if (\$chroot_setup_script);

    # Trigger log directory creation if needed
    \$conf->get('LOG_DIR_AVAILABLE');

END

	$conf->read($files, $deprecated_init, $deprecated_setup, $custom_setup);
}

1;
