+our ($dscdata,$dscurl,$dsc,$skew_warning_vsn);
+
+sub printcmd {
+ my $fh = shift @_;
+ my $intro = shift @_;
+ print $fh $intro or die $!;
+ local $_;
+ foreach my $a (@_) {
+ $_ = $a;
+ if (s{['\\]}{\\$&}g || m{\s} || m{[^-_./0-9a-z]}i) {
+ print $fh " '$_'" or die $!;
+ } else {
+ print $fh " $_" or die $!;
+ }
+ }
+ print $fh "\n" or die $!;
+}
+
+sub failedcmd {
+ { local ($!); printcmd \*STDERR, "$_[0]: failed command:", @_ or die $!; };
+ if ($!) {
+ fail "failed to fork/exec: $!";
+ } elsif (!($? & 0xff)) {
+ fail "subprocess failed with error exit status ".($?>>8);
+ } elsif ($?) {
+ fail "subprocess crashed (wait status $?)";
+ } else {
+ fail "subprocess produced invalid output";
+ }
+}
+
+sub runcmd {
+ printcmd(\*DEBUG,"+",@_) if $debug>0;
+ $!=0; $?=0;
+ failedcmd @_ if system @_;
+}
+
+sub printdone {
+ if (!$dryrun) {
+ print "dgit ok: @_\n";
+ } else {
+ print "would be ok: @_ (but dry run only)\n";
+ }
+}
+
+sub cmdoutput_errok {
+ die Dumper(\@_)." ?" if grep { !defined } @_;
+ printcmd(\*DEBUG,"|",@_) if $debug>0;
+ open P, "-|", @_ or die $!;
+ my $d;
+ $!=0; $?=0;
+ { local $/ = undef; $d = <P>; }
+ die $! if P->error;
+ if (!close P) { print DEBUG "=>!$?\n" if $debug>0; return undef; }
+ chomp $d;
+ $d =~ m/^.*/;
+ print DEBUG "=> \`$&'",(length $' ? '...' : ''),"\n" if $debug>0; #';
+ return $d;
+}
+
+sub cmdoutput {
+ my $d = cmdoutput_errok @_;
+ defined $d or failedcmd @_;
+ return $d;
+}
+
+sub dryrun_report {
+ printcmd(\*STDOUT,"#",@_);
+}
+
+sub runcmd_ordryrun {
+ if (!$dryrun) {
+ runcmd @_;
+ } else {
+ dryrun_report @_;
+ }
+}
+
+sub runcmd_ordryrun_local {
+ if ($dryrun <= 1) {
+ runcmd @_;
+ } else {
+ dryrun_report @_;
+ }
+}
+
+sub shell_cmd {
+ my ($first_shell, @cmd) = @_;
+ return qw(sh -ec), $first_shell.'; exec "$@"', 'x', @cmd;
+}
+
+our $helpmsg = <<END;
+main usages:
+ dgit [dgit-opts] clone [dgit-opts] package [suite] [./dir|/dir]
+ dgit [dgit-opts] fetch|pull [dgit-opts] [suite]
+ dgit [dgit-opts] build [git-buildpackage-opts|dpkg-buildpackage-opts]
+ dgit [dgit-opts] push [dgit-opts] [suite]
+important dgit options:
+ -k<keyid> sign tag and package with <keyid> instead of default
+ --dry-run -n do not change anything, but go through the motions
+ --damp-run -L like --dry-run but make local changes, without signing
+ --new -N allow introducing a new package
+ --debug -D increase debug level
+ -c<name>=<value> set git config option (used directly by dgit too)
+END
+
+our $later_warning_msg = <<END;
+Perhaps the upload is stuck in incoming. Using the version from git.
+END
+
+sub badusage {
+ print STDERR "$us: @_\n", $helpmsg or die $!;
+ exit 8;
+}
+
+sub cmd_help () {
+ print $helpmsg or die $!;
+ exit 0;
+}
+
+our %defcfg = ('dgit.default.distro' => 'debian',
+ 'dgit.default.username' => '',
+ 'dgit.default.archive-query-default-component' => 'main',
+ 'dgit.default.ssh' => 'ssh',
+ 'dgit-distro.debian.git-host' => 'git.debian.org',
+ 'dgit-distro.debian.git-proto' => 'git+ssh://',
+ 'dgit-distro.debian.git-path' => '/git/dgit-repos/repos',
+ 'dgit-distro.debian.git-check' => 'ssh-cmd',
+ 'dgit-distro.debian.git-create' => 'ssh-cmd',
+ 'dgit-distro.debian.sshdakls-host' => 'coccia.debian.org',
+ 'dgit-distro.debian.sshdakls-dir' =>
+ '/srv/ftp-master.debian.org/ftp/dists',
+ 'dgit-distro.debian.upload-host' => 'ftp-master', # for dput
+ 'dgit-distro.debian.mirror' => 'http://ftp.debian.org/debian/');
+
+sub cfg {
+ foreach my $c (@_) {
+ return undef if $c =~ /RETURN-UNDEF/;
+ my @cmd = (@git, qw(config --), $c);
+ my $v;
+ {
+ local ($debug) = $debug-1;
+ $v = cmdoutput_errok @cmd;
+ };
+ if ($?==0) {
+ return $v;
+ } elsif ($?!=256) {
+ failedcmd @cmd;
+ }
+ my $dv = $defcfg{$c};
+ return $dv if defined $dv;
+ }
+ badcfg "need value for one of: @_";
+}
+
+sub access_distro () {
+ return cfg("dgit-suite.$isuite.distro",
+ "dgit.default.distro");
+}
+
+sub access_cfg (@) {
+ my (@keys) = @_;
+ my $distro = $idistro || access_distro();
+ my $value = cfg(map { ("dgit-distro.$distro.$_",
+ "dgit.default.$_") } @keys);
+ return $value;
+}
+
+sub access_someuserhost ($) {
+ my ($some) = @_;
+ my $user = access_cfg("$some-user",'username');
+ my $host = access_cfg("$some-host");
+ return length($user) ? "$user\@$host" : $host;
+}
+
+sub access_gituserhost () {
+ return access_someuserhost('git');
+}
+
+sub access_giturl () {
+ my $url = access_cfg('git-url','RETURN-UNDEF');
+ if (!defined $url) {
+ $url =
+ access_cfg('git-proto').
+ access_gituserhost().
+ access_cfg('git-path');
+ }
+ return "$url/$package.git";
+}
+
+sub parsecontrolfh ($$@) {
+ my ($fh, $desc, @opts) = @_;
+ my %opts = ('name' => $desc, @opts);
+ my $c = Dpkg::Control::Hash->new(%opts);
+ $c->parse($fh) or die "parsing of $desc failed";
+ return $c;
+}
+
+sub parsecontrol {
+ my ($file, $desc) = @_;
+ my $fh = new IO::Handle;
+ open $fh, '<', $file or die "$file: $!";
+ my $c = parsecontrolfh($fh,$desc);
+ $fh->error and die $!;
+ close $fh;
+ return $c;
+}
+
+sub getfield ($$) {
+ my ($dctrl,$field) = @_;
+ my $v = $dctrl->{$field};
+ return $v if defined $v;
+ fail "missing field $field in ".$v->get_option('name');
+}
+
+sub parsechangelog {
+ my $c = Dpkg::Control::Hash->new();
+ my $p = new IO::Handle;
+ my @cmd = (qw(dpkg-parsechangelog), @_);
+ open $p, '-|', @cmd or die $!;
+ $c->parse($p);
+ $?=0; $!=0; close $p or failedcmd @cmd;
+ return $c;
+}
+
+sub git_get_ref ($) {
+ my ($refname) = @_;
+ my $got = cmdoutput_errok @git, qw(show-ref --), $refname;
+ if (!defined $got) {
+ $?==256 or fail "git show-ref failed (status $?)";
+ print DEBUG "ref $refname= [show-ref exited 1]\n";
+ return '';
+ }
+ if ($got =~ m/^(\w+) \Q$refname\E$/m) {
+ print DEBUG "ref $refname=$1\n";
+ return $1;
+ } else {
+ print DEBUG "ref $refname= [no match]\n";
+ return '';
+ }
+}
+
+our %rmad;
+
+sub archive_query ($) {
+ my ($method) = @_;
+ my $query = access_cfg('archive-query','RETURN-UNDEF');
+ if (!defined $query) {
+ my $distro = access_distro();
+ if ($distro eq 'debian') {
+ $query = "sshdakls:".
+ access_someuserhost('sshdakls').':'.
+ access_cfg('sshdakls-dir');
+ } else {
+ $query = "madison:$distro";
+ }
+ }
+ $query =~ s/^(\w+):// or badcfg "invalid archive-query method \`$query'";
+ my $proto = $1;
+ my $data = $'; #';
+ { no strict qw(refs); &{"${method}_${proto}"}($proto,$data); }
+}
+
+sub archive_query_madison ($$) {
+ my ($proto,$data) = @_;
+ die unless $proto eq 'madison';
+ $rmad{$package} ||= cmdoutput
+ qw(rmadison -asource),"-s$isuite","-u$data",$package;
+ my $rmad = $rmad{$package};
+ return madison_parse($rmad);
+}
+
+sub archive_query_sshdakls ($$) {
+ my ($proto,$data) = @_;
+ $data =~ s/:.*// or badcfg "invalid sshdakls method string \`$data'";
+ my $dakls = cmdoutput
+ access_cfg('ssh'), $data, qw(dak ls -asource),"-s$isuite",$package;
+ return madison_parse($dakls);
+}
+
+sub canonicalise_suite_sshdakls ($$) {
+ my ($proto,$data) = @_;
+ $data =~ m/:/ or badcfg "invalid sshdakls method string \`$data'";
+ my @cmd =
+ (access_cfg('ssh'), $`,
+ "set -e; cd $';".
+ " if test -h $isuite; then readlink $isuite; exit 0; fi;".
+ " if test -d $isuite; then echo $isuite; exit 0; fi;".
+ " exit 1");
+ my $dakls = cmdoutput @cmd;
+ failedcmd @cmd unless $dakls =~ m/^\w/;
+ return $dakls;
+}
+
+sub madison_parse ($) {
+ my ($rmad) = @_;
+ my @out;
+ foreach my $l (split /\n/, $rmad) {
+ $l =~ m{^ \s*( [^ \t|]+ )\s* \|
+ \s*( [^ \t|]+ )\s* \|
+ \s*( [^ \t|/]+ )(?:/([^ \t|/]+))? \s* \|
+ \s*( [^ \t|]+ )\s* }x or die "$rmad $?";
+ $1 eq $package or die "$rmad $package ?";
+ my $vsn = $2;
+ my $newsuite = $3;
+ my $component;
+ if (defined $4) {
+ $component = $4;
+ } else {
+ $component = access_cfg('archive-query-default-component');
+ }
+ $5 eq 'source' or die "$rmad ?";
+ my $prefix = substr($package, 0, $package =~ m/^l/ ? 4 : 1);
+ my $subpath = "/pool/$component/$prefix/$package/".dscfn($vsn);
+ push @out, [$vsn,$subpath,$newsuite];
+ }
+ return sort { -version_compare_string($a->[0],$b->[0]); } @out;
+}
+
+sub canonicalise_suite_madison ($$) {
+ my @r = archive_query_madison($_[0],$_[1]);
+ @r or fail
+ "unable to canonicalise suite using package $package".
+ " which does not appear to exist in suite $isuite;".
+ " --existing-package may help";
+ return $r[0][2];
+}
+
+sub canonicalise_suite () {
+ return if defined $csuite;
+ fail "cannot operate on $isuite suite" if $isuite eq 'UNRELEASED';
+ $csuite = archive_query('canonicalise_suite');
+ if ($isuite ne $csuite) {
+ # madison canonicalises for us
+ print "canonical suite name for $isuite is $csuite\n";
+ }
+}