package SDCommon;

#  Copyright 2003, 2004, 2005, 2006 Eduard Bloch <blade@debian.org>
#
#  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

use base qw/ Exporter /;
our @EXPORT = qw(writeCfg withecho withechoNoPrompt needs_upsTagUrl
needs_upsCurrentUrl needs_tagsUrl url insvn long_path oldSvnDirsCheck
load_dirs set_statusref svnMkdirP start_ssh _g rev_number);
#### common ####
our @EXPORT_OK = qw ($tagVersion $upVersion $package $version
$opt_verbose $opt_noninteractive %c $opt_nosave $opt_quiet $epoch
$version);
use vars qw($package $epoch $version $opt_verbose);
use Cwd;
use SVN::Client;
use POSIX qw(locale_h);
use Locale::gettext;
use strict;

#my $SDCommon::opt_quiet;
#my $opt_verbose;
#my $opt_noninteractive;

#my $tagVersion;
#my $upVersion;
#my $package;
#my $epoch;
my $opt_nosave;
my @cfgRest;
my %cacheUrl;
my %inSvn;
my @junk;
my $force_debian;

# static fallback value
my $OURVERSION = '0.6.25';

sub common_version {
	my $query = `dpkg-query -W -f='\${Version}' svn-buildpackage`;
	(defined $query) ? return $query : return $OURVERSION;
}

$SDCommon::version=&common_version();

sub long_path {
   return Cwd::abs_path($_[0]);
}

my $cfgFile=long_path(".svn/deb-layout");
my $defCfgFile="debian/svn-deblayout";

sub exwerror {
   print STDERR $_[0]."\n";
   exit 1;
}

my $msgmax=150;

##
# Prints the command, executes it, on errors also jumps to a control prompt
# returns 1 on success
#
sub withecho {
   if ($SDCommon::opt_noninteractive) {
      return withechoNoPrompt(@_);
   }
   # weed undefs out
   for(my $i=0; $i<=$#_; $i++) {splice(@_, $i, 1) if(!defined($_[$i]));}
   my $cmd=join(' ',@_);
   if(length($cmd) > $msgmax && !$SDCommon::opt_verbose) {
      $cmd="";
      my $i=0;
      while(length($cmd) < $msgmax) {$cmd.=" ".$_[$i];$i++;}
      my $rest=$#_-$i+1;
      $cmd.=" <$rest more argument".($rest>1?"s>":">") if $rest;
   }
   retry:
   print STDERR "$cmd\n" if(!$SDCommon::opt_quiet);
   if(system(@_)) {
      my $cdir = Cwd::getcwd ;
      $cdir = "<unknown>" unless ($cdir);
      printf STDERR (_g("Command '%s' failed in '%s', how to continue now? [Qri?]: "), $cmd, $cdir);
      prompt:
      my $ans = <STDIN>;
      if($ans =~ /^q$/i or $ans =~ /^$/) {
         exwerror(_g("Aborting.\n"));
      }
      elsif($ans =~ /^i$/i) {
         return 1;
      }
      elsif($ans =~ /^r$/i) {
         goto retry;
      }
      else {
         print STDERR _g("Invalid selection! ") if $ans !~ /^\?/;
         # Translators: retain the letters q, r and i as untranslated.
         print STDERR _g("The choices are: Quit (q), Retry (r), Ignore & continue (i).\n");
         print STDERR "[Qri?]: ";
         goto prompt;
      }
   }
   return 1;
}

## 
# Same as withecho but does not show the control prompt on errors
# returns on success
sub withechoNoPrompt {
   # weed undefs out
   for(my $i=0; $i<=$#_; $i++) {splice(@_, $i, 1) if(!defined($_[$i]));}
   my $cmd=join(' ',@_);
   if(length($cmd) > $msgmax && !$SDCommon::opt_verbose) {
      $cmd="";
      my $i=0;
      while(length($cmd) < $msgmax) {$cmd.=" ".$_[$i];$i++;}
      my $rest=$#_-$i+1;
      $cmd.=" <$rest more argument".($rest>1?"s>":">") if $rest;
   }
   print STDERR "$cmd\n" if(!$SDCommon::opt_quiet);
   return ( (!system(@_)) or ($SDCommon::opt_ignoreerrors) );
}

sub svnMkdirP {
   my $url=shift;
   my $scriptname = shift;
   my $tempurl=$url;
   my $missingurl="";
   my $part;
   my $noexist=0;
   my $svnout;
   my $svntempdir;

   chomp ( $svnout = `env LC_ALL=C svn info $tempurl 2>/dev/null`) ;
   while ( ( $svnout =~ /^$/ ) and ( "$tempurl" =~ /^(svn(\+ssh)?|file|https?):\/\/.*/ ) ) {
      chomp ( $part= `basename $tempurl` ) ;
      $missingurl = $part . '/' . $missingurl ;
      chomp ( $tempurl = `dirname $tempurl` ) ;
      chomp ( $svnout = `env LC_ALL=C svn info $tempurl 2>/dev/null`) ;
   } ;

   # now we have to import $missingurl at $tempurl level
   return 0 if ($missingurl =~ /^$/ ) ;
   chomp ( $svntempdir=`mktemp -d` ) ;
   system ( "mkdir -p $svntempdir/$missingurl" ) ;
   my $msg = sprintf (_g("Creating %s directory."), $missingurl);
   withecho("svn", "import", "$svntempdir", "$tempurl", "-m", "$scriptname $msg" );
   system "rm -fr $svntempdir 2>/dev/null" ;
}

sub oldSvnDirsCheck {
   my @svndirs;
   my $dir = $_[0];
   @svndirs=`find $dir -name .svn`;
   if(@svndirs) {
      print _g("Found conflicting .svn directories in the upstream source:\n");
      if($SDCommon::opt_verbose) {
         print @svndirs;
      }
      else {
         print _g("use -v to display the files\n");
      }
      print _g("Hint: use the uclean program to fix upstream source tarball\n");
      exit 1;
   }
}

sub rev_number {
   my $Revision;
   my $testpath=long_path("./debian/");
   open(INFO, "env LC_ALL=C svnversion $testpath 2>/dev/null |");
   while(<INFO>) {
      chomp;
      s/.*:// if (/:/);
      $Revision=$1 if (/^([0-9]+)M?$/i);
   };
   return $Revision;
}

# gets the url of specified local (checkout) directory
sub url {
   my $info;
   return undef if !defined $_[0];
   # bloated with caching, maybe reduce to returning URL again
   my $URL;
   my $PATH;
   my $testpath=long_path($_[0]);
   return $cacheUrl{$testpath} if($cacheUrl{$testpath});
   open(INFO, "env LC_ALL=C svn info $testpath 2>/dev/null |");
   while(<INFO>) { 
      $URL=$1 if(/^Url\s*:\W*(.+)\n/i); 
      $PATH=$1 if(/^Path\s*:\s*(.+)\n/i);
   };
   close (INFO);
   if($URL) {
      $cacheUrl{$testpath}=$URL;
      $cacheUrl{$PATH}=$URL if $PATH;
      return $URL;
   }
   return undef;
}

sub insvn {
   my $testurl = $_[0];
   my $url=$testurl;
   $url =~ /(.*:\/\/)(.*)/;
   my $proto=$1;
   $url=$2;
   while( $url =~ /\.\./) { 
      $url =~ s/\/\//\//g;
      $url =~ s/[^\/]+\/\.\.//g;
      $url =~ s/\/\//\//g;
   }
   $url =~ s/\/$//g;
   $url=$proto.$url;
   return undef if !defined $testurl;
   return  $inSvn{$testurl} if $inSvn{$testurl};
   printf (_g("Repository lookup, probing '%s' ...\n"), $url);
   open(INFO, "env LC_ALL=C svn ls $url 2>/dev/null |") or
      die (sprintf (_g("Can't open svn ls '%s': %s"), $url, $!));
   @junk=<INFO>;
   if (close INFO) {
      $inSvn{$testurl}=$url;
      return $url;
   }
   return undef;
}

# helper to automate lookup for variables
# does not change defined var, but looks for useful path if not defined
sub search {

   my @testloc;
   my @testsvn;
   our($pre, $suf, $var, $basedir) = @_;
#   print "
#WOOT:
#   $pre
#   $suf
#   $var
#   $basedir
#";
   $suf = "" unless $suf;
   $basedir = "" unless $basedir;
   # if basedir contains :// -> svn lookup;
   # args: 
   # prefix: like branches
   # suffix: like upstream
   # var: name of the variable we work on in c config hash
   # basedir: starting directory. If ommited, `pwd` is used

   return if (defined($SDCommon::c{$var}));

   # FIXME: this should use a smarter way of detecting an url,
   # see 433532 for more details
   $basedir=Cwd::getcwd if(!$basedir);
   if($basedir=~/:\/\//) { # huch, URL was specified as $basedir?
      printf (_g("W: %s not specified anywhere, looking in the local repository...\n"), $var);
      @testsvn=("$basedir/../$pre/$suf",
      "$basedir/../../$pre/$package/$suf",
      "$basedir/../../$pre/$suf/$package");
      if($basedir =~ /(trunk|branches|tags)/ && $pre =~ /^(branches|tags|trunk)$/) {
        my $simple = "$basedir";
        $simple =~ s/(trunk|branches|tags)/$pre/;
        $simple .= "/$suf" if(length($suf));
        unshift @testsvn, $simple;
      }
   }
   else {
      if($SDCommon::opt_verbose) {
         print _g("I: Trying blind lookup for resource directories in SVN repository.\n");
         print "D: $pre, $suf, $var, $basedir.\n";
      }
      @testloc=(long_path("$basedir/../$pre/$suf"),
      long_path("$basedir/../../$pre/$package/$suf"));
   }
   foreach my $dir (@testloc) {
      if ($dir && -d $dir) { $SDCommon::c{$var}=$dir; return;}
   }
   for (@testsvn) {
     printf (_g("Looking in SVN for: %s\n"), $_);
     # print "($_)" if $SDCommon::opt_verbose;
      if ($_ && insvn($_)) { 
         printf (_g("I: adding the URLs to the '%s' to skip the check later.\n"), $cfgFile) if(!$SDCommon::opt_nosave);
         $SDCommon::c{$var}=insvn($_);
         return;
      }
      else {
         print _g("Failed, assuming non-existent directory...")."\n";
      }
   }
}

sub get_probe_set
{
   my @probeset=(url("."));
   push(@probeset, "$1/$package") if $probeset[0]=~/(.*)\/branches\//;
   return @probeset;
}

# if still undefined, try blind search in the repository
sub needs_upsTagUrl {
   return 1 if defined $SDCommon::c{"upsTagUrl"};
   foreach my $hereurl (get_probe_set())
   {
      search("branches","upstream", "upsTagUrl", $hereurl);
      return 1 if defined $SDCommon::c{"upsTagUrl"};
   }
   exwerror sprintf(_g("upsTagUrl setting is required, but could not be found in %s or repository tree."), $cfgFile);
}
sub needs_upsCurrentUrl {
   return 1 if defined $SDCommon::c{"upsCurrentUrl"};
   foreach my $hereurl (get_probe_set())
   {
      search("branches","upstream/current", "upsCurrentUrl", $hereurl);
      if( !defined $SDCommon::c{"upsCurrentUrl"} ) { # maybe layout 2?
         search("branches/upstream","current", "upsCurrentUrl", $hereurl);
      }
      return 1 if defined $SDCommon::c{"upsTagUrl"};
   }
   # Translators: %s is the name of the config file.
   exwerror sprintf (_g("upsCurrentUrl setting is required, but could not be found in %s or repository tree."), $cfgFile);
}
sub needs_tagsUrl {
   return 1 if defined $SDCommon::c{"tagsUrl"};
   foreach my $hereurl (get_probe_set())
   {
      search("tags", "", "tagsUrl", $hereurl);
      return 1 if defined $SDCommon::c{"tagsUrl"};
   }
    # Translators: %s is the name of the config file.
    exwerror sprintf (_g("tagsUrl setting is required, but could not be found in %s or repository tree."), $cfgFile);
}
sub writeCfg {
   my $cfgFile = shift;
    # Translators: %s is the name of the config file.
   printf (_g("I: Configuration saving was overriden (%s should be intact).\n"), $cfgFile) if ($SDCommon::opt_nosave==1 and (defined $SDCommon::opt_verbose) );
   return 1 if $SDCommon::opt_nosave==1;
   if($_[0]){$cfgFile = $_[0];}
    # Translators: %s is the name of the config file.
   printf (_g("I: Trying to save the configuration to %s.\n"), $cfgFile) if defined $SDCommon::opt_verbose;
   open(CFG, ">$cfgFile") || die sprintf(_g("Could not open %s for writing.\n"), $cfgFile);
   foreach (sort(keys %SDCommon::c)) {
      print CFG "$_=".$SDCommon::c{$_}."\n" if defined $SDCommon::c{$_};
   }
   print CFG @cfgRest;
   close(CFG);
}

sub sd_exit {
   my $ret = shift||0;
   if($SDCommon::opt_nosave==0) {
      printf (_g("Writing config: %s"), $cfgFile."\n\n\n") if $SDCommon::opt_verbose;
      &writeCfg(".svn/deb-layout");
   }
   # print STDERR "Return-Code: $ret";
   exit $ret;
}

sub init {


   open(INFOC, "env LC_ALL=C svn info debian/changelog |");  @junk=<INFOC>;
   if(!close(INFOC)) {
      exwerror _g("Not started from the Trunk directory or not a valid SVN repository. Aborting.\n");
   }

#   `head -n1 debian/changelog` =~ /^(\S+)\s*\((.+)\)/;
#   $package=$1 if(!defined($package));
#   $upVersion=$2;
#   $tagVersion=$upVersion;
#   if(!$force_debian) {
#      $upVersion=~s/^.*://;
#      $upVersion=~s/-.*//;
#   }

   if(  (-f "debian/changelog" &&
         `head -n1 debian/changelog` =~ /^(\S+)\s*\(((\d+):)?(.+)\)/
      )
      || 
      (-f "../debian/changelog" && chdir ".." &&
         `head -n1 debian/changelog` =~ /^(\S+)\s*\(((\d+):)?(.+)\)/
      )
   )
      {
      $package=$1;
      $SDCommon::upVersion=$4;
      $epoch=$3;
      $SDCommon::tagVersion="$SDCommon::upVersion";
      if(defined($epoch) && length($epoch)) {
        $SDCommon::tagVersion = "$epoch:".$SDCommon::tagVersion;
      }
      if(!$force_debian) {
         $SDCommon::upVersion=~s/^.*://;
         $SDCommon::upVersion=~s/(.*)-([^-]+)/$1/;
      }
      print STDERR _g("I: Got package name and version from debian/changelog.\n")
      	if $SDCommon::opt_verbose;
   }
   else { 
      exwerror _g("E: Not started from the trunk/PACKAGE directory (debian/changelog garbled?).\n");
   }
   printf (_g("
   Package name: %s
   Current upstream version: %s
   Debian tag: %s

   "), $SDCommon::package, $SDCommon::upVersion, $SDCommon::tagVersion)
     if $SDCommon::opt_verbose;

   $SDCommon::opt_nosave=1;

}

# File::Spec's catfile will break file:// and svn+ssh:// because it
# removes the double /
sub mycatfile {
  my $str = "";
  my $i = 0;
  foreach(@_) {
    if(!($i == 0 or substr($_, 0, 1) eq "/" or substr($str, -1) eq "/")) {
      $str .= "/";
    }
    $str .= $_;
    $i++;
  }
  return $str;
}

sub readLayoutProps {
   my $nonempty = 0 ;
   my $repo_root;
   foreach ( `svn proplist debian | grep 'svn-bp:'` ) {
   # import every svn-bp:* property as a cfg
   if(/\s*svn-bp:(\S+)\s*/) {
      my $prop = $1;
      my $val=`svn propget svn-bp:$prop debian | head -n1 | tr -d '\n'`;
      $val=~ s/\ ~/\ $ENV{"HOME"}/;
      if(($val !~ m%://%) and ($prop =~ /Url/)) {
        if(!defined($repo_root)) {
          $repo_root=`env LC_ALL=C svn info | grep "^Repository Root: " | cut -d ' ' -f 3-`;
          chomp $repo_root;
        }
        $val = mycatfile($repo_root, $val);
      }
      # make sure we don't override anything from the command line
      # or some other prioritary levels
      $SDCommon::c{$prop}=$val if (!defined $SDCommon::c{$prop});
      print _g("Importing layout information via Subversion properties... \n") if ( $nonempty == 0 );
      print "\t$prop: $val\n";
      $nonempty = 1 ;
      }
   }
}

sub printLayout {
   our ($msg) = @_;
   my $nonempty = 0 ;
   $msg = _g("Layout info") if ( !defined $msg ) ;
   print "$msg:\n" ;
   foreach (sort(keys %SDCommon::c)) {
      print "\t$_=".$SDCommon::c{$_}."\n" if (defined $SDCommon::c{$_}) ; #if($_=~/^\// && ! -e $_);
      $nonempty = 1;
      };
   printf ("\t(%s)\n", _g("no information")) if ( $nonempty == 0 ) ;
}

sub readLayoutFromFile {
   our ($layoutFile, $formalFileName) = @_;

   if(! -e $layoutFile) {
      printf (_g("No layout information imported through %s.\n%s does not exist.\n"),
        $formalFileName, $layoutFile) if ( defined $opt_verbose ) ;
   } else {
      printf (_g("Importing layout information from %s.\n"), $layoutFile) if ( defined $opt_verbose ) ;
      if(-r $layoutFile) {
         open(CFG, "<$layoutFile") or die ("Can't open $layoutFile: $!");
         while(<CFG>) {
            if(/(\S+)\s*=\s*(.+)(\n|\r)*/) {
               my $val=$2;
               $val=~ s/\ ~/\ $ENV{"HOME"}/;
               $SDCommon::c{$1}=$val;
               print "\t$1: $val\n";
            }
            else {
               push(@cfgRest,$_);
            }
         }
         close(CFG);
      }
   };
}

sub configure {
  my @opt_override = @_;

   &init if(!$SDCommon::tagVersion);
   # keep the list of known vars here
   # @cfgVars=("upsCurrentDir", "upsTagDir", "tagsDir", "origDir", "origUrl",
   # "upsCurrentUrl","upsTagUrl", "tagsUrl", "trunkUrl", "trunkDir");

   # import config options in the right order so that the
   # priority of configuration options is (later is higher):
   #  props, .svn/deb-layout, command line

   &readLayoutProps;
   &printLayout (_g("Versioned layout properties")) if (defined $SDCommon::opt_verbose);

   &readLayoutFromFile ( $defCfgFile, _g("the versioned file layout") ) ;
   # status of the versioned layout info
   &printLayout (_g("Versioned layout information")) if (defined $SDCommon::opt_verbose);

   &readLayoutFromFile ( $cfgFile, _g("the local override") ) ;

   # give a nice status of the currently set options
   &printLayout (_g("Layout information with local overrides")) if (defined $SDCommon::opt_verbose);

   # always redetect them; keep them in the same config base just for the
   # record
   $SDCommon::c{"trunkUrl"}=url(".");
   if($SDCommon::c{"trunkUrl"}) {
      $SDCommon::c{"trunkDir"}=getcwd;
   }
   else {
      exwerror _g("We are not in a working copy of SVN trunk directory");
   }

   foreach(values(%SDCommon::c)) {
      exwerror sprintf (_g("\nThe directory %s does not exist!

Create this directory or fix the setting in .svn/deb-layout or remove that
line and let svn-buildpackage redetect the value. Also check the associated
URL.

"), $_) if($_=~/^\// && ! -e $_);
   }
   # lookup for local locations and get URLs from them if needed
   search("build-area", "", "buildArea");
   search("tags", "", "tagsDir");
   $SDCommon::c{"tagsUrl"}=url($SDCommon::c{"tagsDir"}) if($SDCommon::c{"tagsDir"} && !defined $SDCommon::c{"tagsUrl"});
   if($SDCommon::upVersion ne $SDCommon::tagVersion) {
      search("branches","upstream", "upsTagDir");
      $SDCommon::c{"upsTagUrl"}=url($SDCommon::c{"upsTagDir"}) if($SDCommon::c{"upsTagDir"} && !defined $SDCommon::c{"upsTagUrl"});
      search("branches","upstream/current", "upsCurrentDir");
      $SDCommon::c{"upsCurrentUrl"}=url($SDCommon::c{"upsCurrentDir"}) if($SDCommon::c{"upsCurrentDir"} && !defined $SDCommon::c{"upsCurrentUrl"});
      search("tarballs", "", "origDir");
   }

  @opt_override = split(/,|\ |\r|\n/,join(',',@opt_override));
  for(@opt_override) {
    if(/(.*)=(.*)/) {
      printf _g("Overriding variable: %s with %s\n"), $1, $2 if (defined $SDCommon::opt_verbose);
      $SDCommon::c{$1}=$2;
    }
    else {
      printf (_g("Warning, unable to parse the override string: %s\n"), $_);
    }
  }

   &printLayout (_g("Complete layout information"));

   &writeCfg(".svn/deb-layout");

   #foreach("branches", "tarballs", "trunk", "tags") {
   #   if(length($pkgDir)) {
   #      exwerror "E: Weird directory structure. Where am I? Missing $startdir/$_/$package/\n"
   #      if(! -d $_."/$package");
   #   }
   #   else {
   #      mkdir $_;
   ##      withecho "svn $quiet add $_" if(`svn status -N $_`=~/^\?/);
   #   }
   #}
   #
   ## sanity check first
}

sub check_uncommited {
   my  @conflicts;
   open(SVN, "env LC_ALL=C svn status --ignore-externals |") or die ("Can't open `svn status`: $!");
   for(<SVN>) {
       next if /^\s*$/;
       next if /^\s*X+\s+(.*)/;
       next if /^\s*K+\s+(.*)/;
       if(/^\s*M+\s+(.*)/) {
           # FIXME: rewrite to run svn propget in one command with a list if
           # somebody complains about performance issues
           push(@conflicts,$_) if not `svn propget deb:ignoreM "$1"`;
       }
       else {
           push(@conflicts, $_);
       }
   }
   if (@conflicts) {
      printf (_g("E: Found unresolved issues: %s"), "\n\n@conflicts\n");
      exwerror sprintf (_g("E: Resolve them manually before continuing\n"));
   }
   close(SVN);
}

my $statusref;
sub set_statusref {
    $statusref=shift;
}

sub collect_name {
   (my $file, my $status) = @_;
   chomp($file);
   $$statusref{$file}=$status->text_status;
   #print "Status: $file -- ".$status->text_status."\n";
}

#sub get_listing {
#    my $dir = shift;
#    my $curdir=Cwd::getcwd;
#    chdir $dir;
#    my %tmp;
#    $statusref=\%tmp;
#    my $ctx = new SVN::Client;
#    $ctx->status("", "BASE", \&collect_name, 1, 1, 0, 1);
#    chdir $curdir;
#    return (keys %tmp);
#}

# embed new contents into an URL-referenced directory, using a specified
# work directory. The resulting directory is not commited; check and commit
# yourself with appropriate message.
sub load_dirs {
    my $url=shift;
    my $tmpdir=Cwd::abs_path(shift);
    my @src;
    while(@_) { push(@src, Cwd::abs_path(shift)); }

    # simple replacement for svn_load_dirs, just purge the files that
    # disappeared and add new stuff, then commit
    if( ! withechoNoPrompt("svn", "co", $url, $tmpdir) ) {
        # svn sucks here, import does create subdirs, but mkdir has no -p switch
        withecho "mkdir", "-p", $tmpdir;
        my $msg = _g("Creating trunk directory");
        withecho("svn", "-m", "$msg", "import", $tmpdir, $url);
        unlink $tmpdir;
        withecho("svn", "co", $url, $tmpdir);
    }
    my $curdir=Cwd::getcwd;

    chdir $tmpdir;
    
    my %tmp;
    my $ctx = new SVN::Client;
    $statusref=\%tmp;
    $ctx->status("", "BASE", \&collect_name, 1, 1, 0, 1);

    for(@src) {
        chdir $_;
        for(`find`) {
            chomp; 
            substr($_,0,2,""); # make it svn-like paths, just trailing / is missing
            delete $tmp{$_};
            delete $tmp{"$_/"};
        };
    }
    # remains in %tmp are not covered by the new stuff and shall be deleted
    chdir $tmpdir;
    @junk = keys %tmp;
    $ctx->delete(\@junk, 1);
    for(@src) {
        withecho("cp", "-a", "$_/.", "$tmpdir/.");
    }
    %tmp=();
    $statusref=\%tmp;
    $ctx->status("", "BASE", \&collect_name, 1, 1, 0, 1);
    for(keys %tmp) {
        if($tmp{$_} == 2) {
            $ctx->add($_, 1);
        } elsif($tmp{$_} == 11) {
           if ($SDCommon::opt_ignored_files_action) {
			   # Translators: 'import' and 'skip' are not to be translated
              exwerror _g("Action for ignored files must be either 'import' or 'skip'!\nAborting!")
                unless ($SDCommon::opt_ignored_files_action eq "import" or $SDCommon::opt_ignored_files_action eq "skip");
              $ctx->add($_, 1) if $SDCommon::opt_ignored_files_action eq "import";
           } else {
              unless ($SDCommon::opt_noninteractive) {
                 printf STDERR _g("One or more files were ignored due to (global) subversion svn:ignore properties,\n\thow to procede (quit, import, skip)? [Qis?]: ");
                 prompt:
                 my $ans = <STDIN>;
                 exwerror sprintf (_g("Aborting!\n")) if ($ans =~ /^$/ or $ans =~ /^q/i);
                 if ($ans =~ /^i/i) {
                    $ctx->add($_, 1);
                    $SDCommon::opt_ignored_files_action = "import";
                 } elsif ($ans =~ /^s/i) {
                    $SDCommon::opt_ignored_files_action = "skip";
                 } else {
                    printf STDERR _g("Your choices are:\n\tQuit (q), import (i) which means to add ignored files or skip (s)\n\twhich means to NOT include those files.\n");
                    goto prompt;
                 }
              }
           }
        }
    }
    chdir $curdir;
}

our @end_ssh_command;
our $socket;

use File::Temp qw/tempdir/;
use File::Spec;

sub start_ssh {
  my $url = shift;
  if(!($url)) {
    $url = @{[grep /^Repository Root: /, `env LC_ALL=C svn info`]}[0];
    $url =~ s/Repository Root: //;
    chomp $url;
  }
  if(!($url =~ /^svn\+ssh:\/\//)) {
    return;
  }
  $url =~ s/^svn\+ssh:\/\/([^\/]+)\/.*$/$1/;
  my($user, $host, $port) = ($url =~ /(?:([^@]*)@)?([^:]*)(?:\:(.*))?/);
  return if(!($host));
  my @ssh_command = qw/ssh/;
  push @ssh_command, "-l", $user if($user);
  push @ssh_command, "-p", $port if($port);
  push @ssh_command, $host;
  $socket = File::Spec->catfile(tempdir(CLEANUP => 1), "socket");
  push @ssh_command, "-S", $socket;
  my @start_ssh_command = @ssh_command;
  push @start_ssh_command, "-o", "ControlMaster=yes", "-f", "-N";
  print _g("Starting ssh connection..\n");
  system(@start_ssh_command);
  my $ret = $? >> 8;
  @end_ssh_command = @ssh_command;
  push @end_ssh_command, "-O", "exit";
  if($ret != 0) {
    exwerror(_g("Failed to make the ssh connection, ssh's return code was: ") . $ret);
  }
  my @svn_ssh = qw/ssh/;
  push @svn_ssh, "-S", $socket;
  $ENV{SVN_SSH} = join " ", @svn_ssh;
}

sub stop_ssh {
  return if(!@end_ssh_command);
  return if(! -e $socket);
  system(@end_ssh_command);
}

sub END {
  stop_ssh();
}

sub _g {
	return gettext(shift);
}

1;
##
##### /common ###
# vi: se sw=3 ts=3 et: #

