#!/usr/bin/perl

#  Copyright 2003-2006 Eduard Bloch <blade@debian.org>
#  Copyright 2009-2010 Neil Williams <codehelp@debian.org>
#  Copyright 2009 Ryan Niebur <ryanryan52@gmail.com>,
#  Copyright 2008-2009 Jan Hauke Rahm <info@jhr-online.de>
#
#  This package 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 3 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/>.

use Getopt::Long qw(:config no_ignore_case bundling pass_through);
use File::Basename;
use Cwd qw/getcwd chdir/;
use POSIX qw(locale_h);
use Locale::gettext;
use lib (split(/:/, $ENV{SVNBPPERLLIB} || "/usr/share/svn-buildpackage"));
use SDCommon;

setlocale(LC_MESSAGES, "");
textdomain("svn-buildpackage");

use strict;
use warnings;
#use diagnostics;

my $startdir=getcwd;
chomp(my $tmpfile=`mktemp`);
my $scriptname="[svn-buildpackage]";

sub mychdir {
    my $dir = shift;
    chdir $dir || die "Could not change directories to $dir...";
}

sub help {
printf _g("
Usage: svn-buildpackage [ OPTIONS... ] [ OPTIONS for dpkg-buildpackage ]
Builds Debian package within the SVN repository. The source code
repository must be in the format created by svn-inject, and this script
must be executed from the work directory (trunk/package).

Building and working directory management:
  --svn-builder CMD    Use CMD as build command instead of dpkg-buildpackage
  --svn-ignore-new     Don't stop on svn conflicts or new/changed files
  --svn-dont-clean     Don't run debian/rules clean (default: clean first)
  --svn-savecfg        Create a .svn/deb-layout file from the detected/imported
                       layout information. (replicates old behaviour)
Source copying behavior:
  --svn-no-links       Don't use file links (default: use links where possible)
  --svn-dont-purge     Don't wipe the build directory (default: purge after build)
  --svn-reuse          Reuse an existing build directory, copy trunk over it
  --svn-rm-prev-dir    Remove an existing build directory instead of making a
                       copy; if --svn-reuse is specified, this option is reset
  --svn-export         Just prepares the build directory and exits
Tagging and post-tagging:
  --svn-tag            Final build: Export && build && tag && dch -i
  --svn-retag          Replace an existing tag directory if found while tagging
  --svn-only-tag       Tags the current trunk directory without building
  --svn-noautodch      Don't add a new Debian changelog entry when done
Post-build processing:
  --svn-lintian        Run lintian after the build
  --svn-move           Move package files to .. after successful build
  --svn-move-to XYZ    Move package files to XYZ, implies --svn-move
Miscelaneous:
  --svn-pkg PACKAGE    Specifies the package name
  --svn-override a=b   Override some config variable (comma separated list)
  --svn-arch ARCH      Allows specifying the build architecture
  --svn-verbose        More verbose program output
  --svn-noninteractive Turn off interactive mode
  --svn-download-orig  Use apt and uscan to download the .orig.tar.gz
  -h, --help           Show this help message

If the debian directory has the mergeWithUpstream property, svn-buildpackage
will extract .orig.tar.gz file first and add the Debian files to it.

"); exit 1;
}
my $quiet="-q";
my $opt_verbose;
my $opt_dontclean;
my $opt_dontpurge;
my $opt_reuse;
my $opt_rmprevbuilddir;
my $opt_ignnew;
my $opt_tag;
my $opt_only_tag;
my $opt_lintian;
my $opt_nolinks;
my $opt_noninteractive;
my $opt_pretag;
my $opt_prebuild;
my $opt_posttag;
my $opt_postbuild;
my $opt_retag;
my $opt_buildcmd;
my $opt_export;
my $opt_pass_diff;
my @opt_override;
my $opt_move;
my $opt_move_to;
my $opt_moved_to;
my $opt_noautodch;
my $opt_download_tarball;
my $package;
my $opt_savecfg;
my $opt_architecture;

# deprecated; to be completely removed in next major release?
my $opt_linda;
my $linda_warning = <<EOLINDAW;

################################################################################

   !!!!!!!! WARNING: linda is deprecated and no longer developed !!!!!!!!
         The corresponding option is still accepted, but is ignored.

   If the option svn-linda is present in your ~/.svn-buildpackage.conf
   or any scripts you might use to call svn-buildpackage, please remove
   it! This option is scheduled for complete  removal in the near
   future, so using it would lead to broken builds with such future
   versions of svn-buildpackage.

   !!!!!!! PLEASE STOP USING THE OPTION --svn-linda / svn-linda  !!!!!!!

################################################################################

EOLINDAW

my %options = (
#   "h|help"                => \&help,
   "svn-savecfg"           => \$opt_savecfg,
   "svn-verbose"           => \$opt_verbose,
   "svn-ignore-new|svn-ignore"        => \$opt_ignnew,
   "svn-dont-clean"        => \$opt_dontclean,
   "svn-export"            => \$opt_export,
   "svn-dont-purge"        => \$opt_dontpurge,
   "svn-reuse"             => \$opt_reuse,
   "svn-rm-prev-dir"       => \$opt_rmprevbuilddir,
   "svn-only-tag"          => \$opt_only_tag,
   "svn-tag-only"          => \$opt_only_tag,
   "svn-tag"               => \$opt_tag,
   "svn-retag"             => \$opt_retag,
   "svn-lintian"           => \$opt_lintian,
   "svn-linda"             => \$opt_linda,
   "svn-no-links"          => \$opt_nolinks,
   "svn-noninteractive"    => \$opt_noninteractive,
   "svn-pass-diff"         => \$opt_pass_diff,
   "svn-prebuild=s"        => \$opt_prebuild,
   "svn-postbuild=s"       => \$opt_postbuild,
   "svn-pretag=s"          => \$opt_pretag,
   "svn-posttag=s"         => \$opt_posttag,
   "svn-arch:s"            => \$opt_architecture,
   "svn-download-orig"     => \$opt_download_tarball,
   # and for compatibility with old config directives
   "pre-tag-action=s"      => \$opt_pretag,
   "post-tag-action=s"     => \$opt_posttag,
   "pre-build-action=s"    => \$opt_prebuild,
   "post-build-action=s"   => \$opt_postbuild,
   "svn-move"              => \$opt_move,
   "svn-moved-to=s"             => \$opt_moved_to,
   "svn-move-to=s"         => \$opt_move_to,
   "svn-builder=s"         => \$opt_buildcmd,
   "svn-override=s"        => \@opt_override,
   "svn-pkg=s"             => \$package,
   "svn-noautodch"         => \$opt_noautodch,
);

my $tagVersion;
my $upVersion;
my $upRNumber;
my $tagVersionNonEpoch;
my @builder;
my $origExpect;
my $origfile;
my @dirs;
my @files;
my $retval=0;
my $orig_target;

# get only --help here, do before init because init needs to run after the main Getopt call, but may fail and ignore --help
#
{
   my $gethelp;
   &help unless GetOptions('h|help' => \$gethelp);
   &help if $gethelp;
}

SDCommon::init();

sub setenv {
   my ($key, $val) = @_;
   return 0 if(!defined($val));
   print "ENV: $key=$val\n" if $opt_verbose;
   $ENV{$key}=$val;
}

sub setallenv {
   $tagVersion=$SDCommon::tagVersion;
   $upVersion=$SDCommon::upVersion;
   $tagVersionNonEpoch = $tagVersion;
   $tagVersionNonEpoch =~ s/^[^:]*://;

   #this sucks but the config file needs to be processed before the options and there should be reasonable default
   $package = $SDCommon::package?$SDCommon::package:'' unless $package;
   setenv("PACKAGE", $package);
   setenv("package", $package);
   setenv "TAG_VERSION", $tagVersion;
   setenv "debian_version", $tagVersion;
   setenv "non_epoch_version", $tagVersionNonEpoch;
   setenv "upstream_version", $upVersion;
   setenv "upstream_rev", &rev_number;
   setenv "SVN_BUILDPACKAGE", $SDCommon::version;
   setenv "guess_loc", ( ($package=~/^(lib.)/)?$1:substr($package,0,1))."/$package"."_$upVersion.orig.tar.gz";
}

&setallenv;

my @CONFARGS;
for my $file ($ENV{"HOME"}."/.svn-buildpackage.conf", ".svn/svn-buildpackage.conf") {

    if(open(RC, $file)) {
        SKIP: while(<RC>) {
            chomp;
            next SKIP if /^#/;
            # drop leading spaces
            s/^\s+//;
            if(/^\w/) {
                # remove spaces between
                s/^(\S+)\s*=\s*/$1=/;
                # convert to options and push to args
                s/^/--/;
                $_=`echo -n $_` if(/[\$`~]/);
                push(@CONFARGS, $_);
            }
        }
        close(RC);
    }
}

if($#CONFARGS>=0) {
   @ARGV=(@CONFARGS, @ARGV);
   print _g("Imported config directives:")."\n\t".join("\n\t", @CONFARGS)."\n";
}

&help unless ( GetOptions(%options));
$quiet="" if ($opt_verbose);
# if opt_only_tag is used, set opt_tag too. Should not hurt because the
# real function of opt_tag at the end of the script is never reached
$opt_tag = 1 if($opt_only_tag);
$opt_move=1 if $opt_move_to;
$opt_dontpurge=1 if $opt_reuse;
$opt_rmprevbuilddir=0 if $opt_reuse;
my $destdir=long_path($opt_move_to ? $opt_move_to : "$startdir/..");
$SDCommon::opt_verbose=$opt_verbose;
$package = $SDCommon::package if(!$package);
$SDCommon::opt_noninteractive = 1 if$opt_noninteractive;
$SDCommon::opt_noninteractive = 1 if exists($ENV{DEBIAN_FRONTEND}) && $ENV{DEBIAN_FRONTEND} =~ /^noninteractive$/;

$SDCommon::opt_nosave = 0 if (defined $opt_savecfg) ;

print _g("D: Configuration will not be saved.\n") if (defined $opt_verbose && ($SDCommon::opt_nosave==1));

SDCommon::configure(@opt_override);
my $c=\%SDCommon::c;

sub check_oldBASE {
    open(SVN, "env LC_ALL=C svn diff -r BASE:HEAD |") or die ("Can't open `svn diff`: $!");
    # we just need one line to know that there is a problem
    my $oldBASE = 0;
    for (<SVN>) {
	$oldBASE = 1;
    }
    close(SVN);
    print _g("W: You are tagging while your working copy is not up to date with the repository!\n") if $oldBASE;
}

if($opt_tag){
    start_ssh();
    needs_tagsUrl();
    check_oldBASE();
}

# linda is deprecated - big fat warning since we found it
print $linda_warning if (defined $opt_linda);

@ARGV=grep {!/^--svn-/} @ARGV;

#some things may have been overriden by user options
&setallenv;
if($opt_buildcmd) { # pass @ARGV but carefully
   foreach(@ARGV) { s/"/\\"/g ; s/^/"/; s/$/"/;}
   @builder = ("/bin/sh", "-c", $opt_buildcmd." ".join(" ", @ARGV));
}
else {
   my $arch = ($opt_architecture)?`echo -n "-a${opt_architecture}"`:undef;
   chomp($arch) if (defined $arch);
   push(@builder, "dpkg-buildpackage",@ARGV);
   push(@builder, $arch) if (defined $arch);
   # a simple "helper". Only executed if:
   #  neither --svn-tag-only nor --evn-export is used and
   #  no custom command is choosen
   #  and no -d switch is there
   #  and no prebuild hook is set
   if(! $opt_only_tag && ! $opt_export && (!grep {$_ eq "-d"} @ARGV)
      && (! withechoNoPrompt("dpkg-checkbuilddeps")) 
      && ! $opt_prebuild )
   {
         die _g("Insufficient Build-Deps, stop!\n");
   }
}

my $e=`LC_ALL=C printenv`;
my $fkr = ($e !~ /\nFAKEROOTKEY=[0-9]+\n/) ? "fakeroot" : "";

withecho "$fkr debian/rules clean || die" if ! ($opt_dontclean || (`svn proplist debian` =~ /mergeWithUpstream/i));
SDCommon::check_uncommited if(!$opt_ignnew);

my $parsechangelogret = `dpkg-parsechangelog`;
if (($? >> 8)!=0) {
  die _g("Failed to parse changelog");
}
if($parsechangelogret =~ /(\*\s+NOT.RELEASED.YET)|(UNRELEASED.*urgency)/m) {
   print STDERR _g("UNRELEASED tag found - you don't want to release with it, do you?\n");
   # Translators: retain $FORCETAG untranslated.
   die _g("Aborting now, set \$FORCETAG to ignore it.\n") if($opt_tag && !$ENV{"FORCETAG"});
}

sub checktag {
   SDCommon::svnMkdirP ( $$c{"tagsUrl"}, $scriptname ) ;
   if(insvn($$c{"tagsUrl"}."/$tagVersion")) {
      if($opt_retag) {
		  my $msg = "Removing old tag";
         withecho ("svn", "-m", "$scriptname $msg $package-$tagVersion", "rm", $$c{"tagsUrl"}."/$tagVersion");
      }
      else {
         die sprintf (_g("Could not create tag copy\n%s
It does already exist. Add the --svn-retag option to replace that tag.\n"),
$$c{"tagsUrl"}."/$tagVersion");
      }
   }
}

sub dchIfNeeded {
      if ($opt_noautodch) {
          print _g("\nI: Done! No pending changelog entry was created since it was not requested.\n");
      }
      else {
          withecho "dch", "-D", "UNRELEASED", "-i", "NOT RELEASED YET";
          print _g("\nI: Done! Created the next changelog entry, please commit later or revert.\n");
      }
}

for(keys %{$c}) {
   setenv $_, $$c{$_};
}

if($opt_only_tag) {
   checktag;
   mychdir $$c{"trunkDir"};
   system "$opt_pretag" if($opt_pretag);
   printf (_g("Tagging %s (%s)\n"), $package, $tagVersion);
   # change from 'withecho' to output the translated string to user only
   withecho ("svn", "-m", "$scriptname Tagging $package $tagVersion", "cp", ".", $$c{"tagsUrl"}."/$tagVersion");
   system "$opt_posttag" if($opt_posttag);
   dchIfNeeded;
   SDCommon::sd_exit 0;
}

print "D: ",$opt_prebuild if $opt_verbose;

system "$opt_prebuild" if($opt_prebuild);

$$c{"buildArea"}=long_path($startdir."/..")."/build-area" if(!$$c{"buildArea"});

mkdir $$c{"buildArea"} if (! -d $$c{"buildArea"});

my $orig = $package."_".$upVersion.".orig.tar.gz";
my $bzorig = $package."_".$upVersion.".orig.tar.bz2";
my $xzorig = $package."_".$upVersion.".orig.tar.xz";

my $origExpectDir;
if ($$c{"origDir"}) {
   $origExpectDir = $$c{"origDir"};
} else {
    $origExpectDir = $startdir . "../tarballs";
};

my $bzorigExpect = $origExpectDir."/$bzorig";
my $xzorigExpect = $origExpectDir."/$xzorig";

# See #560391 for why we can't use dpkg-source --print-format .
my $check;
$check = `grep \"3.0\" debian/source/format`
  if (-f "debian/source/format");
if ((-f $bzorigExpect) and (defined $check))
{
  $origExpect = $bzorigExpect; # just for the messages
  $orig = $bzorig;
}
elsif ((-f $xzorigExpect) and (defined $check))
{
  $origExpect = $xzorigExpect; # just for the messages
  $orig = $xzorig;
}
else
{
  $origExpect = $origExpectDir."/$orig"; # just for the messages
}
if (-f $origExpect) {
   $origfile = long_path($origExpect); # for the actual operation
}

my $origDir = $$c{"origDir"} || "$startdir/../tarballs";
my %otherOrigFiles = map { /\.orig-([\w-]+)\.tar\.(gz|bz2|lzma|xz)$/ ? ($1 => $_) : () } grep {
    /(?:^|\/)\Q${package}_${upVersion}\E\.orig-[\w-]+\.tar\.(gz|bz2|lzma|xz)$/
} <$origDir/*>;

if(!defined($origfile)) {
    $$c{"origDir"} = $startdir . "/../tarballs/" if(!defined($$c{"origDir"}));
    system("mkdir", "-p", $$c{"origDir"}) if (! -d $$c{"origDir"});
    printf (_g("Orig tarball not found (expected %s)\n"), $origExpect);
    if ($$c{"origUrl"}) {
        my $oUrl = $$c{"origUrl"};
        printf (_g("fetching tarball from %s...\n"), $oUrl);
        system "wget -O $origExpect $oUrl" ;
        $origfile = long_path($origExpect); # now the file should exist
    }
}
if($opt_download_tarball) {
    if(!defined($origfile)) {
        print _g("Trying to download tarball using apt\n");
        my $olddir = getcwd();
        mychdir $$c{"origDir"};
        my @archive_versions = split("\n", `apt-cache policy $package`);
        @archive_versions = grep /^(( \*\*\* |     )[0-9])/, @archive_versions;
        map {$_ =~ s/^ \*\*\* /     /; $_ =~ s/^     ([^ ]+) .*$/$1/; $_} @archive_versions;
        foreach(@archive_versions) {
            my $upstream_version = "$_";
            $upstream_version =~ s/^.*://;
            $upstream_version =~ s/(.*)-([^-]+)/$1/;
            if($upstream_version eq $upVersion) {
                system("apt-get source --tar-only $package=$_");
                if(($? >> 8) == 0 && -f $orig) {
                    $origfile = long_path($orig);
                    last;
                }
            }
        }
        mychdir $olddir;
    }
    if(-f "debian/watch" && !defined($origfile)) {
        print _g("Trying to download tarball using uscan\n");
        system("uscan", "--destdir", $$c{"origDir"}, "--repack", "--download-current-version");
        if(-f $$c{"origDir"} . "/$orig") {
            $origfile = long_path($$c{"origDir"} . "/$orig");
        }
    };
    if($opt_download_tarball && !defined($origfile)) {
        print STDERR (_g("Couldn't find a tarball\n"));
    }
}

my $ba=$$c{"buildArea"};
my $ra=$ba;
if ($opt_moved_to) { $ra = $opt_moved_to; }
my $bdir="$ba/$package-$upVersion";

if($opt_rmprevbuilddir && -e "$bdir") {
   printf STDERR (_g("%s exists, removing it, as requested\n"), $bdir);
   system "rm -fr $bdir" ;
}

if(!$opt_reuse && -e "$bdir") {
   my $backupNr=rand;
   printf STDERR (_g("%s exists, renaming to %s\n"),
     $bdir, "$bdir.obsolete.$backupNr");
   rename("$bdir","$bdir.obsolete.$backupNr");
}

mkdir "$ba" if(! -d "$ba");

if(`svn proplist debian` =~ /mergeWithUpstream/i) {
   # Translators, leave 'mergeWithUpstream' untranslated.
   printf _g("mergeWithUpstream mode detected, looking for %s\n"), $origExpect;
}

# gets the upstream branch out of svn into .orig directory
sub exportToOrigDir {
   # no upstream source export by default and never in mergeWithUpstream mode
   if((!$ENV{"FORCEEXPORT"}) || `svn proplist debian` =~ /mergeWithUpstream/i) {
      return 0;
   }
   needs_upsCurrentUrl;
   defined($$c{"upsCurrentUrl"}) || print STDERR _g("upsCurrentUrl not set and not located, expect problems...\n");
   withecho("rm", "-rf", "$bdir.orig");
   withecho "svn", "export",$$c{"upsCurrentUrl"},"$bdir.orig";
}

# bold-ifies the output
sub boldify {
   system ( "tput", "smso" ) if ( (defined $ENV{"TERM"}) && ($ENV{"TERM"}) ) ;
}
# unbold-ifies the output
sub unboldify {
   system ( "tput", "rmso" ) if ( (defined $ENV{"TERM"}) && ($ENV{"TERM"}) ) ;
}

# non-Debian-native package detected, needing some kind of upstream source for
# dpkg-buildpackage (most likely, spew error messages but continue on native
# packages with dashes)
if($tagVersion =~ /-/) {
   my $abs_origfile=long_path($origfile);
   $orig_target="$ba/".$orig;
   if($opt_verbose) {
      print _g("Trying different methods to export the upstream source:\n");
      printf (_g(" - making hard or symbolic link from %s\n"), $origExpect) if (!$opt_nolinks);
      print _g(" - copying the tarball to the expected destination file\n");
   }
   else {
      printf (_g("W: %s not found, expect problems...\n"), $abs_origfile) if(! -e $abs_origfile);
   }
   foreach my $file ($origfile, values %otherOrigFiles) {
      if (not defined $file) {
         # no orig at all, try exporting
	 exportToOrigDir;
	 last;
      }
      my $absfile=long_path($file);
      my $fname = basename($file);
      my $target="$ba/$fname";
      if (-e $absfile) {
         if(-e $target) {
            if(((stat($absfile))[7]) != ((stat($target))[7]))
            {
               die sprintf(_g("%s exists but differs from %s!\nAborting, fix this manually..."),
                 $target, $absfile);
            }
         }
         else {
            # orig in tarball-dir but not in build-area
            if($opt_nolinks) {
               withechoNoPrompt("cp", long_path($file), "$ba/$fname")
               ||
               (($file eq $origfile) && exportToOrigDir);
            }
            else {
               link(long_path($file),"$ba/$fname")
               ||
               symlink(long_path($file),"$ba/$fname")
               ||
               withechoNoPrompt("cp",long_path($file),"$ba/$fname")
               ||
               (($file eq $origfile) && exportToOrigDir);
            }
         }
      }
   }
}

# contents examination for "cp -l" emulation
print STDERR _g("Creating file list...\n") if $opt_verbose;
my %tmp;
set_statusref(\%tmp);
my $ctx = new SVN::Client;
$ctx->status("", "BASE", \&SDCommon::collect_name, 1, 1, 0, 1);


# contents examination for "cp -l" emulation
print STDERR _g("Creating file list...\n") if $opt_verbose;
$ctx->status("", "BASE", \&SDCommon::collect_name, 1, 1, 0, 1);

for(keys %tmp) {
    s#/$##;
    next if !length($_);
    next if ($tmp{$_} eq 2 || $tmp{$_} eq 5 || $tmp{$_} eq 11); # skip junk and deleted files
    next if /^\s*$/;
    if(-d $_ and not -l $_ ) {
        push(@dirs,$_);
        print STDERR "DIR: $_\n" if $opt_verbose;
    }
    else {
        push(@files,$_);
        print STDERR "FILE: $_\n" if $opt_verbose;
    }
}

if(`svn proplist debian` =~ /mergeWithUpstream/i) {
   # Translators, leave 'mergeWithUpstream' untranslated.
   print STDERR _g("I: mergeWithUpstream property set, looking for upstream source tarball...\n");
   if(!(defined($$c{"origDir"}) && -e $$c{"origDir"}))
   {
       my $msg = _g("E: Could not find the origDir directory, please check the settings!");
       $msg .= ": '".$$c{"origDir"}."'\n" if (defined $$c{"origDir"});
       die ($msg);
   }
   die sprintf(_g("E: Could not find the upstream source file! (should be %s)\n"), $origExpect) if(! ($origfile && -e $origfile));
   my $check=basename($origExpect);
   my $tardest=dirname($bdir);
   system ("cp $origExpect $tardest/$check")
      if ((not -f "$tardest/$check") and (-f $origExpect));
   my $mod=rand;
   mkdir "$ba/tmp-$mod";
   if($opt_reuse && -d $bdir) {
      print _g("Reusing old build directory\n") if $opt_verbose;
   }
   else {
      my $compress = "--gzip";
      $compress = "--bzip2" if ($orig =~ /\.tar\.bz2/);
      $compress = "--xz" if ($orig =~ /\.tar\.xz/);
      withecho "tar", "--no-same-owner", "--no-same-permissions", "--extract", $compress, "--file", $origfile, "--directory", "$ba/tmp-$mod";
      my @entries = (<$ba/tmp-$mod/*>);
      if (@entries == 1) {
         # The files are stored in the archive under a top directory, we
         # presume
         withecho "mv", (<$ba/tmp-$mod/*>), $bdir;
      }
      else {
         # Otherwise, we put them into a new directory
         withecho "mv", "$ba/tmp-$mod", $bdir;
      }
      foreach my $component (keys %otherOrigFiles) {
	  withecho "rm", "-rf", "$bdir/$component";
	  withecho "tar", "--no-same-owner", "--no-same-permissions", "--extract", "--file", $otherOrigFiles{$component}, "--directory", "$ba/tmp-$mod";
	  my @entries = (<$ba/tmp-$mod/*>);
	  if (@entries == 1) {
	     withecho "mv", (<$ba/tmp-$mod/*>), "$bdir/$component";
	     rmdir "$ba/tmp-$mod";
	  } else {
	     withecho "mv", "$ba/tmp-$mod", "$bdir/$component";
	  }
      }
   }
   if($opt_nolinks || $opt_ignnew) {
      withecho ("svn", "--force", "export", $$c{"trunkDir"},"$bdir");
   }
   else {
      mkdir $bdir;
      #fixme maybe rewrite to withecho
      if( !withechoNoPrompt("mkdir","-p", map { "$bdir/$_" } @dirs) || !withechoNoPrompt("cp", "--parents", "-laf", @files, $bdir))
      { # cp failed...
         withecho "svn", "--force", "export", $$c{"trunkDir"},"$bdir";
      }
   }
   withecho "rm", "-rf", "$ba/tmp-$mod";
}
else {
   if($opt_nolinks) {
      withecho "svn","--force", "export",$$c{"trunkDir"},"$bdir";
   }
   else {
      mkdir $bdir;
      # stupid autodevtools are confused but... why?
      #if(system("mkdir", map { "$bdir/$_" } sort(@dirs)) + system ("cp", "--parents", "-laf", @files, $bdir) )
      if( !withechoNoPrompt("mkdir","-p", map { "$bdir/$_" } @dirs) || !withechoNoPrompt("cp", "--parents", "-laf", @files, $bdir))
      #open(tpipe, "| tar c --no-recursion | tar --atime-preserve -x -f- -C $bdir");
      #for(@dirs) {print tpipe "$_\n"}
      #close(tpipe);
      #if(system ("cp", "--parents", "-laf", @files, $bdir))
      { # cp failed...
         system "rm", "-rf", $bdir;
         withecho "svn", "--force", "export",$$c{"trunkDir"},$bdir;
      }
   }
}

# svn propset useNativeDist 1 debian
if(`svn proplist debian` =~ /useNativeDist/i) {
	print STDERR _g("I: useNativeDist property set, running make native-dist ...\n");
	mychdir ($bdir);
	if (!withecho("make native-dist")) {
		printf STDERR (_g("useNativeDist command failed in %s\nAborting.\n"), $bdir);
		print STDERR _g("W: build directory not purged!\n");
		print STDERR _g("W: no lintian checks done!\n") if($opt_lintian);
		print STDERR _g("W: package not tagged!\n") if($opt_tag);
		SDCommon::sd_exit 1;
	}
	mychdir ($startdir);
}

# clean up file permissions in bdir
# "stolen" from dpkg-source, see #390915
my ($mode, $modes_set, $i, $j);
$mode = 0777 & ~umask;
for ($i=0; $i<9; $i+=3) {
    $modes_set .= ',' if $i;
    $modes_set .= qw(u g o)[$i/3];
    for ($j=0; $j<3; $j++) {
	$modes_set .= $mode & (0400 >> ($i+$j)) ? '+' : '-';
	$modes_set .= qw(r w X)[$j];
    }
}
withecho 'chmod', '-R', $modes_set, '--', $bdir;

# a cludge...
my $afile;
my $bfile;
my $cfile;
if($opt_pass_diff) {
   my $dirname="$package-$upVersion";
   needs_upsCurrentUrl;

   if(`env LC_ALL=C svn status $$c{"trunkDir"}` =~ /(^|\n)(A|M|D)/m) {
      print STDERR _g("Warning, uncommited changes found, using combinediff to merge them...\n");
      chomp($afile=`mktemp`);
      chomp($bfile=`mktemp`);
      chomp($cfile=`mktemp`);
      withecho "svn diff ".$$c{"upsCurrentUrl"}." ".$$c{"trunkUrl"}." > $afile";
      withecho "cd ".$$c{"trunkDir"}." ; svn diff > $bfile";
      withecho "combinediff $afile $bfile > $cfile";
      open(DIFFIN, "cat $cfile |");
   }
   else {
      open(DIFFIN, "svn diff ".$$c{"upsCurrentUrl"}." ".$$c{"trunkUrl"}." |");
   }
   open(DIFFOUT,">$tmpfile");
   # fix some diff junk
   my $invalid=1;
   while(<DIFFIN>) {
      s!^--- (\S+).*!--- $dirname.orig/$1!;
      s!^\+\+\+ (\S+).*!+++ $dirname/$1!;
      $invalid=0 if(/^---/);
      $invalid=1 if( (!$invalid) && /^[^+\-\t\ @]/);
      $invalid || print DIFFOUT $_;
   }
   close(DIFFIN);
   close(DIFFOUT);
   $ENV{"DIFFSRC"}=$tmpfile;
}

mychdir($bdir);

if($opt_export) { printf (_g("Build directory exported to %s\n"), $bdir); exit 0;}

if (!withecho(@builder)) {
   system "$opt_postbuild" if($opt_postbuild);
   printf STDERR (_g("build command failed in %s\nAborting.\n"), $bdir);
   print STDERR _g("W: build directory not purged!\n");
   print STDERR _g("W: no lintian checks done!\n") if($opt_lintian);
   print STDERR _g("W: package not tagged!\n") if($opt_tag);
   SDCommon::sd_exit 1;
}
else {

    system "$opt_postbuild" if($opt_postbuild);

    mychdir "..";

    if( ! $opt_postbuild) {
        my $chfile='';
        my @newfiles=();
        my $determined_arch;
        if(grep {$_ eq "-S" or $_ eq "\"-S\""} @ARGV) {
            $determined_arch = 'source';
        } else {
            $determined_arch = `dpkg --print-architecture`;
        }
        for my $arch ($determined_arch, $opt_architecture) {
            next if ! $arch;
            my $file="$package"."_$tagVersionNonEpoch"."_$arch.changes";
            $file=~s/\r|\n//g; # something like chomp before does not work on constant values, 'source'
            push(@newfiles, "$ra/$file") if -e "$ra/$file";
            $chfile=$file if(-r "$ra/$file");
        }

        if(-f "$ra/$chfile" && open(CH, "<$ra/$chfile")) {
            while(<CH>) { push(@newfiles, $1) if(/^\s\w+\s\d+\s\S+\s\w+\s(.+)\n/); }
            close(CH);

            if($opt_move) {
                $retval=!withechoNoPrompt("mv", @newfiles, $destdir);
                if (-e $orig_target) {
                    $retval=!withechoNoPrompt("mv", $orig_target, $destdir);
                    push(@newfiles, $orig_target);
                }
            }
            else { $destdir=$ra; }

            # expand the paths in the list and kick non-binary packages
	          my $multi=0;
            map { if(/\.deb$/){ $_=" $destdir/$_"; $multi++}else{$_=""}} @newfiles;

            &boldify;
            print STDERR _g("build command was successful");
            # cannot know the build-area if pdebuild is in use.
            if (!$opt_buildcmd or $opt_buildcmd !~ m/pdebuild/) {
               printf STDERR (_g("; binaries are in %s"), "$destdir/") if ($multi > 0);
               print STDERR ". "._g("The changes file is:\n ")."$destdir/$chfile\n";
               &unboldify;
               print STDERR _g("Binary package"), ($multi > 1 ? "s:\n" : ":\n"), @newfiles, "\n" if ($multi > 0);
               &boldify;
            } else {
               print STDERR "\n";
            }
            printf STDERR
               (_g("Warning: %s should have an orig tarball but it does not!\nExpected filename: %s\n"), $package, $origExpect)
               if(($upVersion ne $tagVersionNonEpoch) && ($tagVersionNonEpoch =~/-1$/) && !-e "$destdir/$orig");
            &unboldify;

            if($opt_lintian) {
                withecho "lintian", "$destdir/$chfile";
            }
        }
        else
        {
            &boldify if ($opt_move or $opt_lintian);
            print STDERR _g("Could not read the .changes file: "). "$ra/$chfile";
            # Translators: this line is optional, hence may appear out-of-place in the POT
            print STDERR _g(" and thus failed to move the resulting files") if ($opt_move);
            if($opt_lintian) {
                if ($opt_move) {
                    print STDERR _g(" and run lintian");
                } else {
                    print STDERR _g(" and thus failed to run lintian");
                }
            }
            print STDERR ".\n";
            &unboldify if ($opt_move or $opt_lintian);
        }

        print $linda_warning if($opt_linda);
    }

   if($opt_tag) {
      checktag;
      mychdir $$c{"trunkDir"};
      system "$opt_pretag" if($opt_pretag);
      # Translators: relates to the use of --svn-tag
      printf (_g("Tagging %s (%s)\n"), $package, $tagVersion);
      # change from 'withecho' to output the translated string to user only
      withecho ("svn", "-m", "$scriptname Tagging $package $tagVersion", "cp", ".", $$c{"tagsUrl"}."/$tagVersion");
      system "$opt_posttag" if($opt_posttag);
      dchIfNeeded;
   }

   # cleanup
   if(!$opt_dontpurge) {
      withecho "rm", "-rf", $bdir if(length($tagVersion));
      for($tmpfile, $afile, $bfile, $cfile) {
         unlink $_ if(defined $_ and -e $_);
      }
   }
}
SDCommon::sd_exit 0+$retval;


