#!/usr/bin/perl -w
use strict;

# $Id: debget,v 1.17 2005-07-11 21:22:33 roderick Exp $
#
# Roderick Schertler <roderick@argon.org>

# Copyright (C) 1998, 2005 Roderick Schertler
#
# 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.
#
# For a copy of the GNU General Public License write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# A full spec is a list ref:
#
#    [host, initial-directory, directory, RE]
#
# This specifies that files in host:initial-directory/directory which
# match the RE are to be downloaded.  These are coalesced by host and
# initial-directory leaving specs, which are just the final two elements.

# XXX
#    - download the *.dsc, then skip downloading files which are already
#      present locally
#    - add --clean (see CVS diffs for original documentation)
#    - don't install all produced packages if she gave binary package
#      names to download

use Debian::Debget	qw(
    $Debug
    $Exit
    $Me
    binary_package_info
    debug
    getopt
    package_component
    package_source
    pool_dir
    source_package_info
    upstream_version
    xdie
    xwarn
);

use Net::FTP		  ();
use Proc::WaitStat	qw(waitstat waitstat_die);

our $VERSION = '1.5';

use vars qw(%O);
%O = (
    'arch'		=> undef,
    'binary'		=> 0,
    'build'		=> undef,
    'debug'		=> 0,
    'dir'		=> 'debian',
    'dist'		=> 'unstable',
    'host'		=> 'ftp.debian.org',
    'install'		=> undef,
    'no'		=> 0,
    'no-config'		=> 0,
    'no-download-re'	=> [],
    'no-download-tar'	=> 0,
    'no-dscverify'	=> 0,
    'no-user-config'	=> 0,
    'non-us-dir'	=> 'debian-non-US',
    'non-us-host'	=> 'non-us.debian.org',
    'root-build'	=> undef,
    'root-install'	=> undef,
    'source'		=> 1,
    'unpack'		=> undef,
    'verbose'		=> 0,
);

my @Option_spec = (
    'arch=s',
    'binary|b!',
    'B'			=> sub { $O{'binary'} = 0 },
    'build!',
    'debug+',
    'dir=s',
    'dist|d=s',
    'help!',		# can't die from here because the code is eval()led
    'host|h=s',
    'install|i!',
    'no|n!',
    'no-config|f!',
    'no-download-re=s@',
    'no-download-tar!',
    'no-dscverify!',
    'no-user-config|F!',
    'non-us-dir=s',
    'non-us-host|H=s',
    'root-build|r=s',
    'root-install|R=s',
    'source|s!',
    'S'			=> sub { $O{'source'} = 0 },
    'unpack|u!',
    'verbose|v!',
    'version'		=> sub { print "$Me version $VERSION\n"; exit },
);

# Booleans with single-letter aliases need special handling to avoid a
# bogus warning from Getopt::Long.  Move the single-letter version to
# its own spec.

for (my $i = 0; $i <= $#Option_spec; $i++) {
    next if ref $Option_spec[$i];
    if ($Option_spec[$i] =~ s/(.*)\|(\w)!$/$1!/) {
	push @Option_spec, $2 => \$O{$1};
    }
}

my $Usage = <<EOF;
usage: $Me [switch]... { package | section/package | file.dsc | file.deb }...

switches:
    	--arch arch		installation architecture, default from dpkg
    -b, --binary		download binary packages
    -B, --nobinary		don\'t download binary packages (default)
    -u, --build			build binary packages from source
				packages and .dsc files (implies --unpack)
        --debug			turn debugging on (multiple times for more)
        --dir dir		path to debian directory, default $O{'dir'}
    -d, --dist dist		distribution, default $O{'dist'}
        --help			show this and then die
    -h, --host host		FTP host, default $O{'host'}
    -i, --install		install binary packages (implies --build)
    -n, --no			don\'t download anything
    -f, --no-config		don\'t read any config files
	--no-download-re re	don\'t download files which match regexp re
	--no-download-tar	don\'t download *.tar.gz files
    	--no-dscverify		don\'t run dscverify before unpacking
    -F, --no-user-config	don\'t read the user\'s config file
        --non-us-dir dir	path to non-US directory, default $O{'non-us-dir'}
    -H, --non-us-host host	non-US FTP host, default $O{'non-us-host'}
    -r, --root-build cmd	use cmd to become root to build (see docs)
    -R, --root-install cmd	use cmd to become root to install (see docs)
    -s, --source		download source packages (default)
    -S, --nosource		don\'t download source packages
	--unpack		unpack source packages and .dsc files
    -v, --verbose		be verbose
        --version		print the version and exit

Use section/package (eg, base/dpkg or non-free/games/quake2) for packages
which aren\'t in the available file, or for which $Me guesses the section
wrong.

See the man page or \`perldoc $Me\' for the full documentation.
EOF

sub usage {
    xwarn @_ if @_;
    die $Usage;
}

sub verbose {
    print @_, "\n" if $O{'verbose'};
}

# Uniqify the give list refs, based on stringwise list contents joined
# by \0.

sub uniq_lref {
    my @l = @_;
    my %seen;

    return grep { !$seen{join "\0", @$_}++ } @l;
}

sub find_prog {
    my ($msg, @prog) = @_;

    my $path = defined $ENV{PATH} ? $ENV{PATH} : ':/bin:/usr/bin';
    for my $prog (@prog) {
	for my $dir (split /:/, $path, -1) {
	    $dir = '.' if $dir eq '';
	    $dir = '' if $dir eq '/';
	    return $prog if -x "$dir/$prog" && -f _;
	}
    }
    xdie "can't find a program $msg, tried: @prog\n";
}

sub dirents {
    my $dir = shift;
    local *DIR;

    opendir DIR, $dir
	or xdie "can't opendir $dir: $!\n";
    my @ents = grep { $_ ne '.' && $_ ne '..' } readdir DIR;
    closedir DIR
	or xdie "error running closedir on $dir: $!\n";
    return @ents;
}

sub net_warndie_mess {
    my $cmd = shift;
    my $text = join '', @_;
    my $code = $cmd->code;
    my $message = $cmd->message;
    chomp $message;
    return "$text ($code $message)\n";
}

sub net_warn {
    xwarn net_warndie_mess @_;
}

sub net_die {
    xdie net_warndie_mess @_;
}

sub do_file {
    my $file = shift;
    debug "do $file";
    # XXX Need to fix Perl to be able to detect if the file was
    # unreadable or the like.
    do $file;
    $@ and xdie "error in $file: $@";
}

# Return the host/dir that should be used for a file in DIST.

sub host_dir {
    # XXX
    return ($O{'host'}, $O{'dir'});
}

# Return the installation architecture.

sub arch {
    if (!defined $O{'arch'}) {
	chomp($O{'arch'} = `dpkg --print-installation-architecture`);
	waitstat_die $?, 'dpkg';
	$O{'arch'} ne '' or xdie "dpkg didn't print the architecture";
    }
    return $O{'arch'};
}

sub init {
    # This is a bit of a hack.  I have to parse the command line to
    # learn whether -f or -F was given before I read the rc files, but I
    # want the command line to override the settings in the rc files.  I
    # had used globals for the configuration until I needed to do this.
    my @copy_key = qw(no-config no-user-config);
    # I have to use the real %O, because @Option_spec contains references
    # to it.  So, set up to restore the original values except for those
    # I'm checking right now.
    my %orig_o = %O;
    {
	local @ARGV = @ARGV;
	getopt -bundle, \%O, @Option_spec;
    }
    my %new_o = %O;
    @O{keys %orig_o} = values %orig_o;	# use slice to retain old SVs
    @O{@copy_key} = @new_o{@copy_key};

    do_file '/etc/debget.rc'	unless $O{'no-config'};
    do_file "$ENV{HOME}/.debget.rc"
				unless $O{'no-config'} || $O{'no-user-config'};

    getopt -bundle, \%O, @Option_spec or usage;
    usage if $O{'help'};

    $Debug = $O{'debug'};
    $| = 1 if $Debug;
}

# Return the full file specs which should be downloaded for PACKAGE.

sub make_full_specs {
    my $orig_package = shift;
    my (@ret, $pool_dir, $ver);

    # XXX allow specifying everything you're going to derive based on
    # the package name, using apt-get syntax where possible (foo=1.2-1
    # etc)
    my $package = $orig_package;

    my $dist = $O{dist};
    my $arch = arch;

    # Figure out what version to download.  I want the same version for
    # both binary and source packages.  The name I've got might be
    # either, so try it both ways if necessary.

    if (!defined $ver) {
    	my $source_info = source_package_info $package;
	if ($source_info) {
	    my $r = $source_info->{$dist};
	    $ver = $r->[0] if $r;
	}
    }

    if (!defined $ver) {
	my $binary_info = binary_package_info $package;
	if ($binary_info) {
	    $ver = $binary_info->{$dist}{$arch};
	}
    }

    if (!defined $ver) {
    	xwarn "can't get version for $package in distribution $dist\n";
	# XXX tell how to specify it
	return;
    }

    $pool_dir = pool_dir $package unless defined $pool_dir;
    if (!defined $pool_dir) {
    	xwarn "can't get pool directory for $package\n";
	# XXX tell how to specify it
	return;
    }

    if ($O{'source'}) {
	my $source	= package_source $package;
	my $upstream	= upstream_version $ver;
	my $is_native	= $upstream eq $ver;
	my $orig	= $is_native ? '' : '.orig';
	push @ret, (
	    [host_dir, $pool_dir, qr/^\Q${source}_${ver}.\E(dsc|diff\.gz)$/],
	    [host_dir, $pool_dir, qr/^\Q${source}_${upstream}$orig.tar.gz\E$/],
	);
    }

    if ($O{'binary'}) {
	my $arch = arch;
	push @ret, [host_dir,
	    	    $pool_dir,
		    qr/^\Q${package}_${ver}_\E(\Q$arch\E|all)\.deb$/];
    }

    return @ret;
}

# Run nlst via FTP on directory DIR, return the file names (without
# directory).

{
my %cache;

sub ls {
    my ($ftp, $dir) = @_;

    if (!$cache{$ftp}{$dir}) {
	verbose "ls  $dir";
	my $rls = $ftp->ls($dir)
	    or net_die $ftp, "error running ls for $dir";
	debug "ls output\n", join "\n", @$rls;
	# Some implementations return just the base names, some include
	# the directory.  Strip the directories if they're there.
	for (@$rls) {
	    s-.*/--;
	}
	$cache{$ftp}{$dir} = $rls;
    }
    return @{ $cache{$ftp}{$dir} };
} }

# Take an FTP connection and a SPEC and return the actual files which
# should be downloaded.

sub glob_spec {
    my ($ftp, $spec) = @_;
    my ($dir, $re) = @$spec;

    my @file = map { "$dir/$_" } grep { /$re/ } ls $ftp, $dir;
    if (!@file) {
	xwarn "no files in $dir match /$re/\n";
    }
    return @file;
}

# Return true if I'm allowed to download a file named FILE.

sub allow_download {
    my ($file) = @_;

    return 0 if $file =~ /\.tar\.gz$/ && $O{'no-download-tar'};
    for (@{ $O{'no-download-re'} }) {
	return 0 if $file =~ /$_/;
    }
    return 1;
}

# Connect to HOST, chdir to DIR and retrieve the files required by SPECs.

sub do_ftp {
    my ($host, $dir, @spec) = @_;
    my ($ftp, @got);

    verbose "connect to $host";
    $ftp = Net::FTP->new($host, Debug => $Debug)
	or xdie "can't connect to $host: $@\n";
    $ftp->login		or net_die $ftp, "error logging in to $host";
    $ftp->binary	or net_die $ftp, "error setting binary mode";
    verbose "chdir $dir";
    $ftp->cwd($dir)	or net_die $ftp, "error doing cd to $dir";

    for (@spec) {
	for my $file (glob_spec $ftp, $_) {
	    if (!allow_download $file) {
		print "# skip download of $file\n" if $O{'verbose'};
		next;
	    }
	    print "get " if $O{'verbose'};
	    print "$file\n";
	    next if $O{'no'};
	    if ($ftp->get($file)) {
		push @got, $file;
		$got[-1] =~ s-.*/--;
    	    }
	    else {
		net_warn $ftp, "error getting $file";
	    }
	}
    }

    verbose "disconnect from $host";
    $ftp->quit or net_warn $ftp, "error closing FTP session to $host";
    return @got;
}

# Run the given COMMAND like system(), but don't output the stdout
# unless verbose mode is on.

sub run {
    my @cmd = @_;
    my ($ret);

    if (!$O{'verbose'}) {
	open SAVEOUT, '>&STDOUT' or xdie "can't dup stdout: $!\n"
	    unless defined fileno SAVEOUT;
	open STDOUT, '>/dev/null' or xdie "can't write to /dev/null: $!\n";
    }

    $ret = system @cmd;

    open STDOUT, '>&SAVEOUT' or xdie "can't redup stdout: $!\n"
	if !$O{'verbose'};

    return $ret;
}

# Using SUPROG run the given COMMAND, with special handling for an
# SUPROG of su.

sub root_run {
    my ($su, @cmd) = @_;
    my (@arg);

    # This is more complicated than it needs to be because of GNU's
    # accursed argument reordering.  If you have an argument to the
    # command which looks like a switch GNU's su will act on it unless
    # you use -- to stop option processing or set $POSIXLY_CORRECT.  I
    # don't want to set $POSIXLY_CORRECT because that will affect the
    # behavior of the command run.  Using -- on its own doesn't work
    # because the su which comes with the shadow tools (packaged as
    # secure-su) doesn't do real option processing and doesn't recognize
    # it.
    #
    # So, I have this.  I put the -- there and then remove it myself if
    # necessary.  With GNU su the "" arg ends up in $0.  With secure-su
    # the -- ends up in $0 and the "" in $1, and I shift it out of $1.

    @arg = ('root', '-c', '[ -z "$1" ] && shift; exec "$@"', '--', '')
	if $su eq 'su';

    run $su, @arg, @cmd;
}

# Unpack the given DSC_FILE, returning the name of the directory created
# if all went well.

sub dscunpack {
    my $dsc = shift;
    my ($dir, $skipping);

    if (!$O{'no-dscverify'}) {
	verbose "dscverify $dsc";
	run 'dscverify', $dsc;
	if ($?) {
	    xwarn "non-zero exit (", waitstat $?, ") verifying $dsc,",
	    	    " skipping it\n";
	    if (!eval { find_prog 'x', 'dscverify' }
		    || ! -f '/usr/share/keyrings/debian-keyring.gpg') {
		xwarn "install the devscripts and debian-keyring packages",
		    	" or specify --no-dscverify\n";
	    }
	    return;
	}
    }

    $dir = $dsc;
    $dir =~ s/\.dsc$//;
    $dir =~ s/(_.*?)-[^\-]+/$1/;
    $dir =~ s/_/-/;
    print "$dir\n" unless $O{'verbose'};

    # I won't actually be skipping anything unless I'm going to build or
    # install.
    $skipping = $O{'build'} || $O{'install'};

    verbose "unpack $dsc";
    run 'dpkg-source', '-x', $dsc;
    if ($?) {
	xwarn "non-zero exit (", waitstat $?, ") from dpkg-source",
	    	" unpacking $dsc", $skipping ? ", skipping it" : '', "\n";
	return;
    }

    unless (-d $dir) {
	xwarn "directory $dir wasn't created by dpkg-source unpacking $dsc",
    	    	$skipping ? ", skipping it" : '', "\n";
	return;
    }

    return $dir;
}

# Build the package which is in DIRECTORY, returning the names of the
# generated binary packages if all went well.

sub build {
    my $dir = shift;
    my ($skipping, %before, @deb);

    verbose "$dir/debian/rules build";
    run 'sh', '-c', 'cd "$1" && debian/rules build', 'x', $dir;
    if ($?) {
	xwarn "non-zero exit (", waitstat $?, ") from `debian/rules build'",
	    	" in $dir, skipping it\n";
	return;
    }

    # Save the existing *.deb files and the age of each.
    %before = map { $_ => -M $_ } grep { /\.deb$/ } dirents '.';

    verbose "$dir/debian/rules binary";
    root_run $O{'root-build'},
	'sh', '-c', 'cd "$1" && debian/rules binary', 'x', $dir;
    if ($?) {
	xwarn "non-zero exit (", waitstat $?, ") from `debian/rules binary'",
	    	" in $dir via $O{'root-build'}",
	    	$O{'install'} ? ", skipping it" : '', "\n";
	return;
    }

    # Find either new or updated *.deb files.  XXX This is a race with other
    # processes working in this directory.
    @deb = grep { /\.deb$/ && (!$before{$_} || -M $_ < $before{$_}) }
	    dirents '.';
    if (!@deb) {
	xwarn "no *.deb files were produced by $dir\n";
	return;
    }

    print join "\n", @deb, '' unless $O{'verbose'};
    return @deb;
}

sub main {
    my (@dsc, @src, @deb, @full_spec);

    init;

    # Separate .dsc and .deb files from package names.  There's no way
    # to give a directory name and have that mean "build the package in
    # this directory" because there'd be an ambiguity (if you have a
    # directory with the same name as a package).
    for (@ARGV) {
	if (/\.dsc$/) {
	    push @dsc, $_;
	}
	elsif (/\.deb$/) {
	    push @deb, $_;
	}
	else {
	    push @full_spec, make_full_specs $_;
	}
    }

    # Automatically turn on --build/--install if .dsc/.deb were given.
    $O{'build'}		= 1 if @dsc	&& !defined $O{'build'};
    $O{'install'}	= 1 if @deb	&& !defined $O{'install'};

    # Automatically turn on prerequisites.
    $O{'build'}		= 1 if $O{'install'}	&& !defined $O{'build'};
    $O{'unpack'}	= 1 if $O{'build'}	&& !defined $O{'unpack'};

    # Look for the su-type commands if necessary, before starting
    # downloads.
    $O{'root-install'} = $O{'root-build'}
    	if defined $O{'root-build'} && !defined $O{'root-install'};
    $O{'root-build'} = find_prog 'to become root to build',
				    qw(fakeroot sudo super su)
	if $O{'build'} && !defined $O{'root-build'};
    $O{'root-install'} = find_prog 'to become root to install',
				    qw(sudo super su)
    	if $O{'install'} && !defined $O{'root-install'};

    # Make sure there's something to do.
    unless (@dsc || @deb) {
	$O{'source'} || $O{'binary'}
	    or xdie "neither source nor binary packages",
	    	    " are being downloaded\n";
	@ARGV or usage "no packages or files specified\n";
    }

    # Uniqify the full specs because, eg, multiple packages can come
    # from the same source.
    @full_spec = uniq_lref @full_spec;

    # Split the full specs up into specs grouped by host/directory.
    my %work;
    for (@full_spec) {
	my ($host, $dir, @spec) = @$_;
	push @{ $work{$host}{$dir} }, \@spec;
    }

    # For each host/directory pair, connect and download the given
    # specs.
    for my $host (sort keys %work) {
	for my $dir (sort keys %{ $work{$host} }) {
	    for (do_ftp $host, $dir, @{ $work{$host}{$dir} }) {
		if (/\.dsc$/) {
		    push @dsc, $_;
		}
		elsif (/\.deb$/) {
		    push @deb, $_;
		}
	    }
	}
    }

    if ($O{'unpack'} && @dsc) {
	for (@dsc) {
	    push @src, dscunpack $_;
	}
    }

    if ($O{'build'} && @src) {
	for (@src) {
	    push @deb, build $_;
	}
    }

    if ($O{'install'} && @deb) {
	root_run $O{'root-install'}, 'dpkg', '-i', @deb;
	if ($?) {
	    xwarn "non-zero exit (", waitstat $?,
		    ") running dpkg via $O{'root-install'} to install @deb\n";
	}
    }

    return 0;
}

$Exit = main || $Exit;
$Exit = 1 if $Exit and not $Exit % 256;
exit $Exit;

__END__

=head1 NAME

debget - download source and binary Debian packages

=head1 SYNOPSIS

B<debget> [I<switch>]... { I<package> | I<section/package> | I<file>.dsc
| I<file>.deb }...

=head1 DESCRIPTION

B<debget> downloads source and binary Debian packages by name and
optionally unpacks, compiles and installs them.  The default behavior
is to download the source for packages, to unpack and build F<*.dsc>
files and to install F<*.deb> files.  For detailed defaults on FTP
server names and such run C<debget --help>.

B<debget> doesn't require a local copy of the F<Packages> files, instead it
lists directories on the FTP site to find out what versions are available.

Non-switch arguments are F<*.dsc> files, F<*.deb> files, and package
names or I<section/package>, eg B<base/dpkg> or B<non-free/games/quake2>.
There are two cases in which you've got to specify the section:

=over 4

=item -

Information about the package isn't in the local F<available> file (as
shown by C<dpkg --print-avail>), or the information there is wrong.

=item -

You're downloading a source package which doesn't generate a binary
package of the same name.  Normally B<debget> infers the correct
source package to download based on the C<dpkg --print-avail> output.
(Eg, if you say to download the source for B<perl-base>, it will
really download the B<perl> sources.)  This isn't possible if the
source package doesn't have an F<available> file entry (which is the
case when the source package doesn't generate a binary package of the
same name).  In this case B<debget> will use the section for the
package which you specified (B<perl-base> in this case).  If the
section for that package isn't available, or if it's not the same as
the section for the source package, you have to specify the section
yourself.

=back

To handle either of these cases, specify the package with the section
prepended, as it would appear in the F<available> file.  Eg, B<base/dpkg>
or B<non-free/games/quake2>.

=head1 OPTIONS

=over 4

=item B<--arch> I<arch>

Specify the installation architecture (used to find binary packages).
The default is the output of C<dpkg --print-installation-architecture>.

=item B<-b>, B<--binary>

Download binary packages.  The default is not to download them.

=item B<-B>, B<--nobinary>

Don't download binary packages.  This is the default.

=item B<-u>, B<--build>

Build downloaded source packages.  This implies B<--unpack>.  B<--build>
is turned off by default, but it is turned on if you specify any F<*.dsc>
or F<*.deb> files on the command line.

=item B<--debug>

Turn debugging on.  Specify multiple times for more detail.

=item B<--dir> I<dir>

Specify the path to the top of the Debian hierarchy on the primary FTP
server.

=item B<-d> I<dist>, B<--dist> I<dist>

Specify the distribution from which to download packages.  The
default is B<unstable>.  You can use the name of any subdirectory
in the F<dists> directory in the Debian archive, or B<experimental>
(which is special-cased).

=item B<--help>

Show the usage message and die.

=item B<-h> I<host>, B<--host> I<host>

Specify the host name of the primary FTP server.

=item B<-i>, B<--install>

Install binary packages.  This turns on B<--unpack> and B<--build>, so
specifying it will cause B<debget> to install just about everything you
mention on the command line.  Packages will be downloaded, unpacked, built,
and installed, F<*.dsc> files will be unpacked, built, and installed, and
F<*.deb> files will be installed.

=item B<-n>, B<--no>

Go through the motions, but don't actually download any packages.

=item B<-f>, B<--no-config>

Don't process either /etc/debget.rc or ~/.debget.rc.

=item B<--no-download-re> I<re>

Don't download files whose name match the Perl regexp I<re>.  This
option can be specified multiple times.

=item B<--no-download-tar>

Don't download F<*.tar.gz> files.  This is normally used when downloading
sources, when specified you'll just fetch the F<*.diff.gz> and F<*.dsc>
files.

=item B<--no-dscverify>

Don't run B<dscverify> before unpacking sources.  B<dscverify> checks
that the F<.dsc> file is signed by a Debian developer and that the MD5
sums and file sizes given in it match the files about to be unpacked.
These are good things, so B<debget> will try to run B<dscverify> by
default.  The B<dscverify> program is in the F<devscripts> package.

=item B<-F>, B<--no-user-config>

Don't process ~/.debget.rc.

=item B<--non-us-dir> I<dir>

Specify the path to the top of the Debian hierarchy for non-US packages.

=item B<-H> I<host>, B<--non-us-host> I<host>

Specify the host name of the non-US FTP server.

=item B<-r> I<cmd>, B<--root-build> I<cmd>

Use I<cmd> to become root when building a package from source.  The
default is the first of F<fakeroot>, F<sudo>, F<super>, or F<su> which
is present on the system.

=item B<-R> I<cmd>, B<--root-install> I<cmd>

Use I<cmd> to become root when installing a package.  The default is
what you gave for B<--root-build> if you specified anything, otherwise
the first of F<sudo>, F<super>, or F<su> which is present on the system.

=item B<-s>, B<--source>

Download source packages.  This is the default.

=item B<-S>, B<--nosource>

Don't download source packages.  The default is to download them.

=item B<--unpack>

Unpack downloaded source packages.

=item B<-v>, B<--verbose>

Be verbose.

=item B<--version>

Print the version number and exit.

=back

=head1 CONFIGURATION FILES

The default behavior of B<debget> can be modified by the configuration
files F</etc/debget.rc> and F<~/.debget.rc> (unless modified by the
B<-f> or B<-F> switches).  These files are processed as Perl code.  They
can set these variables to control the program (with their corresponding
switches):

=over 4

=item C<$O{'arch'}>

B<--arch>

=item C<$O{'binary'}>

B<--binary>, boolean

=item C<{$O{'build'}>

B<--build>, boolean

=item C<$O{'debug'}>

B<--debug>, integer

=item C<$O{'dir'}>

B<--dir>

=item C<$O{'dist'}>

B<--dist>

=item C<$O{'host'}>

B<--host>

=item C<$O{'install'}>

B<--install>, boolean

=item C<$O{'no'}>

B<--no>, boolean

=item C<$O{'no-config'}>

B<--no-config>, boolean

=item C<$O{'no-download-re'}>

B<--no-download-re>, array reference

=item C<$O{'no-download-tar'}>

B<--no-download-tar>, boolean

=item C<$O{'no-dscverify'}>

B<--no-dscverify>, boolean

=item C<$O{'no-user-config'}>

B<--no-user-config>, boolean

=item C<$O{'non-us-dir'}>

B<--non-us-dir>

=item C<$O{'non-us-host'}>

B<--non-us-host>

=item C<$O{'root-build'}>

B<--root-build>

=item C<$O{'root-install'}>

B<--root-install>

=item C<$O{'source'}>

B<--source>, boolean

=item C<$O{'unpack'}>

B<--unpack>, boolean

=item C<$O{'verbose'}>

B<--verbose>, boolean

=back

Here's an example configuration file:

    $O{'host'} = 'debian.terrabox.com';
    $O{'verbose'} = 1;

=head1 BUGS

If you specify B<--install> all produced binary packages will be
installed, even ones you didn't specify on the command line.  Eg, if
you run C<debget
--install ssh> it will install both F<ssh> and F<ssh-askpass>.

I'd like to add a B<--clean> switch which will make the program remove
intermediate files.

See F</usr/share/doc/debget/README.Debian> if your transfers are failing
because you need to use passive FTP or a proxy.

=head1 SEE ALSO

dselect(8), apt-get(8)

=head1 AVAILABILITY

The code is licensed under the GNU GPL and distributed as part of Debian.

=head1 AUTHOR

Roderick Schertler <roderick@argon.org>

=cut
