#!/bin/sh

exec perl -lwx $0 ${1+"$@"}

#!/usr/bin/perl -lw

# The above tries to ensure support for non-standard locations of perl

#
# This file is part of the SDD/DFS/Pipeline Team tools
# Copyright (C) 2005,2007,2009 European Southern Observatory
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
#

# 
#  $Author: llundin $
#  $Date: 2013-10-09 14:10:22 $
#  $Revision: 1.100 $
#  $Name: not supported by cvs2svn $
# 


$developers_help = <<'REQ';
1) Now (as before) the user provides interactively as input a
   $prefixdir, where all common packages are installed: CPL (6.3), esorex
   (3.10), CIFTSIO (3.31), WCS (4.16), GSL (1.15) and FFTW (3.3.3).
2) The pipeline themselves will no longer be installed in $prefixdir,
   but instead in $prefixdir/$instrume-$version.
3) Consequently, the user _must_ update $LD_LIBRARY_PATH to the value
   that ./install_pipeline suggests.

REQ

$developers_help = <<'HELP';

This script assumes the following:
0) In the below description gz can be substituted with bz2, or dropped
1) In the directory where it resides, it expects to find a number of gzipped
   tar-files, with names <basename>.tar.gz, that unpack into the directory
   <basename>/ :
   cpl-<version>.tar.gz
   esorex-<version>.tar.gz
   gasgano-<version>.tar.gz
   Optionally:
   One or more pipelines, <instrume>-<version>.tar.gz
   One or more pipeline static calibration files, <instrume>-calib-<version>.tar.gz
   External open-source, like fftw-3.3.3.tar.gz
1A) In the directory where it resides, it expects to find a cfitsio3310.tar.gz
   that unpacks into the directory cfitsio/ :-((((((((((((((((((((
2) With exception of gasgano-<version>.tar.gz and
   <instrume>-calib-<version>.tar.gz, these gzipped tar-files must contain an
   executable script, configure, that is compatible with configure-scripts
   generated by autoconf and that generates a Makefile - or they must contain a
   Makefile. The Makefile must support the targets 'all' and 'install',
   i.e. `make` and `make install` must work.
   Specifically, the configure-script must understand the --prefix option and
   either understand or ignore the --with-extra-includes and --with-extra-libs
   options.
3) Files of type <instrume>-calib-<version>.tar.gz are unpacked and installed
   into calib/<instrume>-<version>/ . The optional gasgano configuration files,
   i.e.
   <instrume>-calib-<version>/gasgano/config/<INSTRUME>.prefs
   <instrume>-calib-<version>/gasgano/config/<INSTRUME>.rul
   <instrume>-calib-<version>/gasgano/config/<INSTRUME>.oca
   and any gasgano scripts in
   <instrume>-calib-<version>/gasgano/scripts/
   are installed differently: If a gasgano package is delivered, the gasgano
   files in the calib-file are installed on top.
   If there is no gasgano delivery they will be ignored.

4) Files of type <instrume>-calib-<version>.tar.gz should contain:
   A) The static calibration (FITS-) files also found in the
      <instrume>-calibdb-<version>.tar.gz - using the same directory structure
      (typically a directory for each major observing mode).
   B) If <instrume>-calibdb-<version>.tar.gz contains a QC dictionary (in directory
      dic/), then this should also be present.
   C) If <instrume>-calibdb-<version>.tar.gz contains a gasgano/config-directory,
      then this should also be present.
   Example:
     <instrume>-calib-<version>/gasgano/
     <instrume>-calib-<version>/gasgano/config/
     <instrume>-calib-<version>/gasgano/config/<INSTRUME>.prefs
     <instrume>-calib-<version>/gasgano/config/<INSTRUME>.rul
     <instrume>-calib-<version>/gasgano/config/<INSTRUME>.oca
     <instrume>-calib-<version>/spec/cal/calibration_1.fits
     <instrume>-calib-<version>/spec/cal/calibration_2.fits
     <instrume>-calib-<version>/spec/cal/calibration_3.fits
     <instrume>-calib-<version>/dic/
     <instrume>-calib-<version>/dic/ESO-DFS-DIC.<INSTRUME>_QC
5) .sof files for example runs must be placed in sof/ and name <recipename>.sof.
6) The filenames in these .sof files shall be relative to the directory where the
   install-script resides.
7) The name of this directory should be either:
   <instrume>-kit-<version>/  - for delivery of one pipeline), or
   <instrume1>+<instrume2>-kit-<version>/ for delivery of f.ex. two
   pipelines. The gzipped tar-file should thus be named
   <instrume>-<version>-kit.tar.gz respectively
   <instrume1>-<version1>+<instrume2>-<version2>-kit.tar.gz
8) Files with the word 'config' in them are silently ignored.

Apart from perl this script assumes the PATH to point the following
executables:
gzip
tar
make
find
cp
mv
tee

HELP

use File::Copy "cp";

$supported = 1;

# Directory with files to be installed
$0 =~ /kit/ or ($srcdir) = $0 =~ /(.+\/)/;
$srcdir ||= './';

chdir($srcdir) || die "Could not chdir to $srcdir".$developers_help;

$fullpwd = `pwd`;
chomp($fullpwd);
$pwd=$fullpwd;
$pwd =~ s/.+\///;

$mso = '(?:s[ol]|dylib)'; # .so or .sl (for HP-UX) or .dylib (for Mac OSX).

$mtar = '\.tar(?:\.gz|\.bz2|\.xz)?'; # Support .tar, .tar.gz .tar.bz2 and .tar.xz

# FIXME: seems overly complicated ...
open(STDOUT, '|tee install.log') || die "Cannot redirect stdout to install.log\n";
open(STDERR, '>&STDOUT') || &mydie("Cannot dup stdout (install-$dir.log)\n");

select(STDERR); $| = 1; # make unbuffered
select(STDOUT); $| = 1; # make unbuffered

&init_install();

$ignore_esorex_rc = 0;
$make = 'make';
$sudoinstall = '';
$sudocalib = '';

for (;@ARGV;) {
  $ARGV[0] eq '-ignore_gasgano' and shift(@ARGV), $gasgano::doinstall = 0, next;
  $ARGV[0] eq '-ignore_esorex_rc' and shift(@ARGV), $ignore_esorex_rc = 1, next;
  $ARGV[0] =~ /^-makeopts=(.+)/ and shift(@ARGV), $make .= " $1", next;

  last;

}

if ($0 =~ /kit/) {

  # If the script is invoked under a name that contains the string 'kit'
  # then it expects some *-kit-*.tar.gz as arguments. These are unpacked,
  # checked for consistency, and installed when OK.

  local($inikit);
  local(@kit)=();
  local(@newdir)=();

  # Check argument names
  for (;@ARGV;) {
    if ($ARGV[0] =~ /(.+\-kit\-.+)$mtar/) {
       push(@kit, shift());
    } elsif (-d $ARGV[0] || $ARGV[0] =~ /^\// and $#newdir < 1) {
       push(@newdir, shift());
    } else {
       die "Need *-kit-*.tar.gz, not: $_";
    }
  }
  @newdir and push(@ARGV, @newdir);

  @kit or die "I need one or more pipeline kits.\n".$developers_help;

  local($tmpnam) = "pipeline-kit-$$.tmp";
  mkdir($tmpnam, 0755) || die "Could not create directory $tmpnam";
  chdir($tmpnam) || die "Could not chdir into directory $tmpnam";

  $builddir = 'tmp';
  mkdir($builddir, 0755) || die "Could not create directory $builddir";

  local(%dsize)=();
  local(%dname)=();

  # Unpack and check kits
  for (@kit) {
    local($kitname) = /(.+\-kit\-.+)$mtar/;
    local($kitbase) = $kitname;
    local($gz) = /\.gz/ ? 'z' : (/\.bz2/ ? 'j' : '');
    $kitbase =~ s/.+\///;
    &do_or_die("tar x${gz}f ". (/^\// ? $_ : "../$_"));
    opendir(IN, $kitbase) or die "Did not find $kitbase after unpacking $_";
    local(@kitfiles)=readdir(IN);
    closedir(IN);
    if ($inikit) {
       for $targz (@kitfiles) {
         $targz =~ /(.+)$mtar/ or next;
         local($base) = $1;
         if ($base =~ /(.+)-(.+)/) {
           defined($dname{$1}) && $dname{$1} ne $2 and die
             "Cannot install $kitbase and $inikit with both $base and $1-$dname{$1}";
           $dname{$1}=$2;
         }
         defined($dsize{$base}) && -s $targz != $dsize{$base} and die
             "Cannot install $kitbase/$targz and $inikit/$targz with "
            ."different sizes";
         $dsize{$base} = -s $targz;
         symlink("../$kitbase/$targz", "$builddir/$targz");
       }
    } else {
       $inikit = $kitbase;
       for $targz (@kitfiles) {
         $targz =~ /(.+)$mtar/ or next;
         local($base) = $1;
         if ($base =~ /(.+)-(.+)/) {
           defined($dname{$1}) and die
             "Cannot install $kitbase and $inikit with both $base and $1-$dname{$1}";
           $dname{$1}=$2;
         }
         $dsize{$base} = -s $targz;
         symlink("../$kitbase/$targz", "$builddir/$targz");
       }
    }
  }
  chdir($builddir);
}

# List of software to install
@targz = glob('*.tar*');
local($_);
for (@targz) {
  /\b(?:calib|config)\b/ and next;
  if (/^gasgano/) {
     defined($gasgano::doinstall)
       or $gasgano::doinstall = &gasgano::check_java_home($_);
     $gasgano::doinstall || next;
  } elsif (/^wcslib/) {
    $wcslib::doinstall = 1;
  } elsif (/^fftw/) {
    $fftw::doinstall = 1;
  }

  push(@dist, $_);

}
$gasgano::doinstall ||= 0;

# List of pipeline calibration files to install
grep($dist{$_}++, @dist);
@calib = grep(/(.+)\-calib(.+)/ && defined($dist{$1.$2}), @targz);
%dist=();

@must = ('cfitsio(?:2510|3\d\d\d)', 'cpl', 'esorex');
$gasgano::doinstall && push(@must, 'gasgano');

for $dist (@must) {
  local($n) = 0+grep(/\b$dist\b/,@dist);
  $n == 0 and die "I am missing a gzipped tar file with $dist in $srcdir ($0)\n".$developers_help;
  $n == 1  or die "I cannot handle more than one gzipped tar file with $dist in $srcdir (not $n)\n".$developers_help;
}

&check4pipeline(@dist) or die "
You have sucessfully downloaded and unpacked ESOs pipeline infrastructure kit!

Please follow these instructions to install one or more pipelines:
1) Point your browser to http://www.eso.org/pipelines
2) Find the pipeline(s) that you wish to install.
3) Download the source code of the pipeline, <instrume>-<version>.tar.gz,
   e.g. fors-4.3.5.tar.gz and save the gzipped tar file into the directory
   where this script resides.
4) Download the static calibration data, if any, of the pipeline,
   <instrume>-calib-<version>.tar.gz, e.g. fors-calib-4.3.5.tar.gz and save
   the gzipped tar file into the directory where this script resides.
5) Redo steps 3) and 4) for any additional pipeline that you wish to install.
6) Rerun the install script and verify that your pipeline(s) are listed among
   the packages to install.
";

grep(!/\bcpl\-(\d+)\.(\d+)/ || $1 > 4 || ($1 == 4 && $2 >= 0) ||
   die("The CPL version must be at least 4.0, I cannnot use $_\n".$developers_help), @dist);

print 'I am about to install the following software packages:';
grep(print("  $_"), sort dist_sort @dist);

$pipedirlist = join('/, ', map(/(.+)\.tar.*/,grep(!/^($msort)/o, @dist))).'/';

print '';
if (@calib) {
  print ' - with the following pipeline calibration file(s):';
  grep(print("  $_"), sort dist_sort @calib);

  print "
The software installation will be organised in bin/, lib/, include/ and
$pipedirlist directories,
while the calibration files will be installed in a calib/ directory.
";
} else {

  print "
The software installation will be organised in bin/, lib/, include/ and
$pipedirlist directories.
";
}

# Warn about reruns of $0
if (grep(-e &distdir($_), @targz)) {
  local($fullpwdback)=$fullpwd;
  $fullpwdback =~ s/[^\/]+\/*$//;
  opendir(IN, $fullpwdback);
  local($kit);
  grep(/\b$pwd$mtar/o && ($kit=$_), readdir(IN));

  warn("

Oops, you are trying to rerun the installation.

This is not supported and may cause unexpected behaviour or errors.

To ensure a correct pipeline installation, you should remove the
directory created by untarring the pipeline-kit, untar the kit again
and then run the installation script:

cd $fullpwdback
rm -r $fullpwd
tar xzf $kit
cd $fullpwd
./install_pipeline

");

  &confirm("Should I abort the installation") and exit(1);

  $supported = 0;

}

$gasgano::doinstall && &gasgano::lookahead();

@plugindirs=();

$prefixdir = '';
# An existing prefixdir may be supplied on the command line
$#ARGV >= 0 and $prefixdir = $prefixdir_default = shift(@ARGV) or
  # Otherwise: If user is root use /usr/local - otherwise use home directory
  $prefixdir_default =  $> ? $ENV{HOME} : '/usr/local';

$makeinstall = "$make install";
while (!-d $prefixdir) {

  printf("Where should I install the software packages ? [$prefixdir_default] ");
  $prefixdir = <STDIN>;
  chomp($prefixdir);
  $prefixdir ||= $prefixdir_default;
  if ($prefixdir !~ /^\//) {
    warn "I need an absolute path\n";
    $prefixdir = '';
  } elsif ($prefixdir =~ /^$ENV{HOME}\/+gasgano/) {
    warn("Oops, $prefixdir is reserved for gasgano, "
         ."I cannot install there\n");
    $prefixdir = '';
  } elsif (!-d $prefixdir &&
           &confirm("Directory $prefixdir does not exist, create") &&
           !mkdir($prefixdir, 0755) && system("mkdir -p '$prefixdir'") != 0) {
    warn("Oops, I could not create $prefixdir\n");

    if (&is_exe('sudo')) {
      warn("You can either let me perform the installation in
$prefixdir using the command sudo, or you can enter a new location for
the installation.

");

      if (&confirm("Should I use sudo to create '$prefixdir' and "
          ."for '$makeinstall'")) {
          if (&sudomkdir($prefixdir, 1)) {
            $sudoinstall = 'sudo ';
          } else {
            warn("Failed to create $prefixdir with sudo");
          }
      }
    }
    $sudoinstall or $prefixdir = '';

  } elsif (!-w $prefixdir) {
    warn("Oops, I do not have write access to $prefixdir\n");
    if (&is_exe('sudo')) {
      warn("You can either let me perform the installation in
$prefixdir using the command sudo, or you can enter a new location for
the installation.

");

      if (&confirm("Should I use sudo for '$makeinstall'")) {
        $sudoinstall = 'sudo ';
      }
    }
    $sudoinstall or $prefixdir = '';
  }
};

$makeinstall =  $sudoinstall . $makeinstall;

$prefixdir =~ s/\/+$//;

$calibdir = '';

if (@calib) {

  local($calibdir_default);

  # An existing calibdir may be supplied on the command line
  $#ARGV >= 0 and $calibdir = shift(@ARGV) or
    # Otherwise: If user is root use /usr/local - otherwise use home directory
    $calibdir_default =  $prefixdir;

  while (!-d $calibdir) {

    printf("Where should I install the pipeline calibration files ? [$calibdir_default] ");
    $calibdir = <STDIN>;
    chomp($calibdir);
    $calibdir ||= $calibdir_default;
    if ($calibdir !~ /^\//) {
      warn "I need an absolute path\n";
      $calibdir = '';
    } elsif ($calibdir =~ /^$ENV{HOME}\/+gasgano/) {
      warn("Oops, $calibdir is reserved for gasgano, "
	  ."I cannot install there\n");
      $calibdir = '';
    } elsif (-e $calibdir && !-d $calibdir) {
      warn "I need a directory\n";
      $calibdir = '';
    } elsif (!-d $calibdir &&
       &confirm("Directory $calibdir does not exist, create") &&
       !mkdir($calibdir, 0755) && system("mkdir -p '$calibdir'") != 0) {

      if ($sudoinstall && &sudomkdir($calibdir, $sudoinstall)) {
        $sudocalib = $sudoinstall;
      } else {
        warn("Oops, I cannot create the directory $calibdir\n");
        if (&is_exe('sudo')) {
          warn("You can either let me install the pipeline calibration files in
$calibdir using the command sudo, or you can enter a new location for
the installation of the pipeline calibration files.

");

          if (&confirm("Should I use sudo for 'mkdir -p '$calibdir'")) {
            local($cmd) = "sudo mkdir -p '$calibdir'";
            if (system($cmd) == 0) {
              $sudocalib = 'sudo ';
            } else {
              warn("The command \"$cmd\" failed");
            }
          }
        }
      }
      $sudocalib or $calibdir = '';
    } elsif (!-w $calibdir) {
      if ($sudoinstall) {
        $sudocalib = $sudoinstall;
      } else {
        warn("Oops, I cannot modify the directory $calibdir\n");
        if (&is_exe('sudo')) {
          warn("You can either let me install the pipeline calibration files in
$calibdir using the command sudo, or you can enter a new location for
the installation of the pipeline calibration files.

");

          if (&confirm('Should I use sudo for the installation of the '
                       .'pipeline calibration files')) {
            $sudocalib = 'sudo ';
          }
        }
      }
    }
  };

  &sudomkdir("$calibdir/calib", $sudocalib) ||
    die "Could not create directory $calibdir/calib".$developers_help;
}

$ENV{FFTWDIR} = $ENV{GSLDIR} = $ENV{CFITSIODIR} = $ENV{QFITSDIR} = $ENV{CPLDIR} = $prefixdir;

@libdir = ('lib', 'lib64', 'lib32'); # SUSE 12 uses lib64, not lib. lib32 ?

# Prepend prefixdir to LD_LIBRARY_PATH - Needed _before_ building,
# so configure can link and run test-executables
$ldlibdir = join(':', map("$prefixdir/$_", @libdir));
$ldlibpathname = $^O eq 'darwin' ? 'DYLD_LIBRARY_PATH' : 'LD_LIBRARY_PATH';

if (defined($ENV{$ldlibpathname})) {
  $ENV{$ldlibpathname} =~ /^$ldlibdir(?:$|\:)/o or
    $ENV{$ldlibpathname} = "$ldlibdir:$ENV{$ldlibpathname}",
    $^O eq 'darwin' and $dodyldmsg = 1;
} else {
  $ENV{$ldlibpathname} = $ldlibdir;
  $^O eq 'darwin' and $dodyldmsg = 1;
}

for $dist (sort dist_sort @dist) {

  if ($dist =~ /gasgano/){
    # Handled separately
    $gasgano::home = &gasgano::install($dist);
    next;
  }

  local($dir) = &distdir($dist) or die $dir.$developers_help;

  if ($dir !~ /^($msort)/o && $dist =~ /(.+)$mtar$/) {
    -e $prefixdir || mkdir($prefixdir, 0755) || system("mkdir -p '$prefixdir'")==0 ||
      die "Could not create directory $prefixdir\n";
    print "Installing $dist in $prefixdir (with plugins in ",
          "lib*/esopipes-plugins/$1)";
    push(@plugindirs, map("$prefixdir/$_/esopipes-plugins/$1", @libdir));
  } else {
    print "Installing $dist";
  }

  # next; # FIXME: used for development
  local($gz) = $dist =~ /\.gz/ ? 'z' : ($dist =~ /\.bz2/ ? 'j' : '');
  &do_or_die("tar x${gz}f $dist");

  chdir($dir) || die "Could not chdir to $dir (directory created by $dist)\n".$developers_help;

  # FFTW is built twice to support both single and double precision
  local($ninstalls) = $dist =~ /fftw/ ? 2 : 1;

  for $installs (1..$ninstalls) {

    if ($installs > 1) {
        &do_or_die("$make clean");
    }

  if (-x 'install_lapack') {
    # FIXME: Ugly, ugly, ugly kludge for non-standard build procedure of (c)lapack
    #        :-(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((

    &do_or_die("./install_lapack $prefixdir");

  } elsif (-x 'configure') { # Old fashioned distributions may not use ./configure

      local($distbase) = $dist =~ /^(\w+)/;
      # FIXME: Ugly, ugly, ugly kludge for non-standard tar-ball name of cfitsio
      #        :-(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
      $distbase =~ s/(cfitsio)\d+/$1/;

      # Default configure options
      local($config_opt) = "--with-extra-includes=$prefixdir/include";
      $config_opt       .= join('', map(" --with-extra-libs=$prefixdir/$_",
                                        @libdir));

      # Check for package dependent configure options
      if (defined($distbase) && defined(&{$distbase.'::config'})) {
         $config_opt = &{$distbase.'::config'}($prefixdir, $installs);
      } elsif (!defined($sort{$distbase})) {
         $config_opt = &pipeline_config($prefixdir, $installs);
      }

      local($config_cmd) = "./configure --prefix=$prefixdir $config_opt";

      # FIXME: Ugly, ugly, ugly kludge for non-standard clapack-3.2.1 configure 
      #        :-((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
      $distbase =~ /^clapack/ and
        $config_cmd =~ s/ --prefix=/ /;

      # Let the user know how the packages are built
      print "$config_cmd\n";

      &do_or_die($config_cmd);

    }

    &do_or_die($make);

    # FIXME: Ugly, ugly, ugly kludge for non-standard build procedure of cfitsio
    #        :-(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
    $dist =~ /cfitsio\d+\.tar\.gz/ and
      &do_or_die("$make shared");

    &do_or_die($makeinstall);

  }

  chdir('..');

}

#$ENV{LD_LIBRARY_PATH} = defined($ENV{LD_LIBRARY_PATH})
#   ? "$prefixdir/lib:$ENV{LD_LIBRARY_PATH}" : "$prefixdir/lib";
#$ENV{PATH} = defined($ENV{PATH}) ? "$prefixdir/bin:$ENV{PATH}" : "$prefixdir/bin";

if ($gasgano::doinstall && !gasgano::gotcpl($prefixdir)) {

  warn("

Oops, the CPL installation unexpectedly failed to produce the gasgano interface
library.

You can therefore not use gasgano for recipe execution.

You can either let me stop, so you can investigate why CPL failed to produce the
gasgano interface library, or you can proceed in which case you should be able to
use esorex for the recipe execution.

");

  &confirm("Should I abort the installation") and exit(1);

  $gasgano::doinstall = 0;

} else {
  print '';
  print "Pipeline installation complete!";
  print '';
}

local($esorex) = "$prefixdir/bin/esorex";

$ignore_esorex_rc or &check_n_upd_esorexrc(@plugindirs);

$cmd = "$esorex --recipes";
@stdout = `$cmd`;
if ($?) {
  $esorex::failed = $?;

  local($event) = &crashtxt($?);

  $gasgano::doinstall ? warn("
Oops, I cannot complete the esorex installation:

I tried to execute the command
$cmd
which $event.

This indicates that esorex is not supported on this platform.

You can either let me stop, so you can investigate why esorex failed, or you
can proceed in which case you should be able to use gasgano for the recipe
execution.

")
              : die("
Oops, I cannot complete the esorex installation:

I tried to execute the command
$cmd
which $event.

This indicates that esorex is not supported on this platform.

Since neither esorex nor gasgano could be installed, I have to stop the installation.

");

    &confirm("Should I abort the installation") and exit(1);

} else {
  @recipes = ();
  $found = 0;
  for (@stdout) {
    $found and /(\w+)\s*:/ && push(@recipes, $1), next;
    /List of Available Recipes/ and $found = 1, next;
  }
  if ($found) {
    print "Available recipes:";
    grep(print("  $_"), @recipes);
  } else {
    $esorex::failed = -1;
    $gasgano::doinstall ? warn("
Oops, I could not find any recipes:", @stdout,
"
You can therefore not use esorex for recipe execution.

You can either let me stop, so you can investigate why esorex failed, or you
can proceed in which case you should be able to use gasgano for the recipe
execution.

")

                        : die("
Oops, I could not find any recipes:", @stdout,
"
You can therefore not use esorex for recipe execution.

Since neither esorex nor gasgano could be installed, I have to stop the installation.

");

    &confirm("Should I abort the installation") and exit(1);
  }
}

&install_calib($gasgano::home, @calib);

$gasgano::doinstall and
   &gasgano::config($gasgano::home, \@plugindirs, \@calib),
   print <<EOF;

In order to use the recipes from gasgano you need to set the environment
variable CPLDIR to $ENV{CPLDIR} .

Depending on your type of shell a possible command for doing this may be:
export CPLDIR=$ENV{CPLDIR}

EOF

defined($dodyldmsg) and
   print <<EOF;

In order to use the recipes you need to set the environment
variable DYLD_LIBRARY_PATH to $ENV{DYLD_LIBRARY_PATH} .

Depending on your type of shell a possible command for doing this may be:
export DYLD_LIBRARY_PATH=$ENV{DYLD_LIBRARY_PATH}

EOF

@paths = ();
$esorex::failed or @paths = ($esorex);
$gasgano::doinstall && push(@paths, "$gasgano::home/bin/gasgano");
&check_path(@paths);

$esorex::failed or
  &offer();

&offer_esoflow();


sub sudomkdir {
  local($dir, $sudo) = @_;

  -d $dir or ($sudo ? system("sudo mkdir -p '$dir'") == 0 : mkdir($dir, 0755));
}

sub offer_esoflow {
  -d 'esoflow' && -x 'esoflow/esoflow' || return;

  opendir(IN, 'esoflow') || return;

  local(@flowdata) = grep($_ ne '.' && $_ ne '..' && -d "esoflow/$_", readdir(IN));

  closedir(IN);
  
  @flowdata or return;

  local($is_first) = 1;
  for $flowdir (sort @flowdata) {
    scalar glob("esoflow/$flowdir/*.fits") or next;

    if ($is_first) {
       $is_first = 0;
       print 'You can also try out a pre-defined sequence of esorex runs:';
       print 'cd esoflow';
    }

    local($flow) = $flowdir;
    $flow =~ s/\b\.\d+//;

    print "./esoflow $flow $flowdir/*.fits";

  }

}

sub init_install {

    $ENV{HOME} ||= (getpwuid($<))[7];
    delete($ENV{ESOREX_PLUGIN_DIR}); # Would otherwise confuse the installation

  # List the order of installation here.
  # The listed packages are those that the pipelines may depend on.
  # Any unmentioned distribution is done afterwards
  # and is assumed to be a CPL-based pipeline.
  $norder = 0;
  grep($sort{$_}=++$norder, split(/\s*\n\s*/,<<SORT));
gasgano
cfitsio
wcslib
gsl
fftw
qfits
cpl
esorex
eclipse
config
kmclipm
clapack
lapack
swarp
sextractor
scamp
SORT

  $msort = join('|', keys %sort);


  # Non-standard distributions here :-((((

  # FIXME: Ugly, ugly kludge for non-standard build procedure of cfitsio
  #        :-(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((

  %distdir = ('cfitsio2510', 'cfitsio',
              'cfitsio3060', 'cfitsio',
              'cfitsio3090', 'cfitsio',
              'cfitsio3290', 'cfitsio',
              'cfitsio3310', 'cfitsio',
              'cfitsio3330', 'cfitsio',
              'cfitsio3350', 'cfitsio',
              'cfitsio3360', 'cfitsio',
              'cfitsio3370', 'cfitsio');
}


sub dist_sort {

  local($asort) = $a =~ /^($msort)/o;
  local($bsort) = $b =~ /^($msort)/o;

  # First sort according to the predefined sorting order
  defined($asort) && defined($bsort) && return $sort{$asort} <=> $sort{$bsort};
  !defined($asort) && defined($bsort) && return 1;
  !defined($bsort) && defined($asort) && return -1;

  # Otherwise sort alphabetically (since these two should be pipelines)
  $a cmp $b;

}

sub check4pipeline {
  local(@dist)=@_;

  local($ok) = 0;

  grep(/^($msort)/o || $ok++, @dist);

  $ok;
}

sub confirm {
  local($ask)=@_;

  local($_);
  do {
    printf("$ask ? [Y/n] ");

    -t STDIN ||
      die "I cannot get your answer because my standard input is not opened to a tty\n";

    $_ = <STDIN>;
  } until (/^\s*[yn]?\s*$/i);

  !/n/i;

}



sub do_or_die {
  local($cmd)=@_;

  local($exec) = $cmd =~ /^\s*exec\s/ ? $cmd : "exec $cmd";

  system($exec) == 0 and return;

  warn "The command\n  $cmd\nfailed with code $?";

  $supported and
  die "This should not have happened. Please send an error report with
the file ./install.log to usd-help\@eso.org using subject: 
Installation of pipeline $pwd failed
";

}

sub cp_or_die {
  local(@args) = @_;

  local($dest) = pop(@args);

  # cp() returns 1 on OK
  if (grep(!cp($_, $dest) && warn("Copy of $_ to $dest failed: $!"), @args)) {
    $supported and
    die "This should not have happened. Please send an error report with
the file ./install.log to usd-help\@eso.org using subject: 
Installation of pipeline $pwd failed
";
  }
}

sub check_path {
  local(@fullpath)=@_;

  local(@actual_path) = split(/:/,$ENV{PATH});

  local(%missingpath, @missingpath);
  local(%precede, %fullpath, %fixprecede);

  for $fullpath (@fullpath) {

    -x $fullpath || die $fullpath.$developers_help;

    local($mypath,$exe) = $fullpath =~/(.+)\/(.+)/;
    local($myinode) = (stat($fullpath))[1];

    local($missingpath) = 1;

    for $path (@actual_path) {
      -d "$path/$exe" and next;
      -x "$path/$exe" or  next;

      $myinode == (stat("$path/$exe"))[1] and $missingpath = 0;

      # Path points to another instance of the executable

      $missingpath and $fullpath{$exe}=$fullpath,
        $fixprecede{$mypath}++,
        $precede{$exe} ||= $path;
    }
    $missingpath && !$fixprecede{$mypath} and
      push(@missingpath, $fullpath), $missingpath{$mypath}++;

  }

  if (%precede || %missingpath) {
    warn "Warning:\n\n";

    if (%precede) {

      warn "Your PATH points to\n"
      .join(" and\n", map("$precede{$_}/$_", sort keys %precede))
      ."\ninstead of your newly installed\n"
      .join(" and\n", map($fullpath{$_}, sort keys %precede)).".\n";

      if (%missingpath) {
        warn "\nSecondly, your PATH does not point to your newly installed\n"
        .join(" and\n",@missingpath).".\n";
      }

    } else {
      warn "Your PATH does not point to your newly installed\n"
      .join(" and\n",@missingpath).".\n";
    }

    warn "\nTo ensure execution of the newly installed software you should "
    ."modify your PATH.\n\n"
    ."Depending on your type of shell a possible command for doing this may be:\n"
    ."export PATH="
    .join(':', keys %fixprecede, @actual_path, keys %missingpath)."\n\n";
  }
}
  

sub check_n_upd_esorexrc {
  local(@plugindirs)=@_;

  # The recipes in each of the directories must be available and
  # they may not be available from other directories.

  # Get the current recipe-dir
  local($path) = join("",`$esorex --params 2>/dev/null`) =~ /^\s*recipe\-dir\s*\:\s+(\S+)/m;

  @paths = ();
  $path and @paths = split(':',$path);

  # Find those dirs in the recipe-path that contain the plugins with the same
  # name in other directories
  local(@remove)=();
  for $plugindir (@plugindirs) {
    opendir(IN, $plugindir) or
      print("Skipping $plugindir, could not open it for reading"), next;
    local(%inodes);
    grep(-f "$plugindir/$_" && ($inodes{$_} = (stat("$plugindir/$_"))[1]), readdir(IN));
    closedir(IN);

    for $path (@paths) {
      local($found) = 0;
      for $file (keys %inodes) {
        -e $path or last;
        local(@found) = `find $path -name $file`;
        chomp(@found);
        grep((stat($_))[1] != $inodes{$file}, @found) and $found++, last;
      }
      $found and push(@remove, $path),
        print "The esorex recipe-dir path needs a removal of the path $path";
    }
  }

  local($doit) = 0;
  if (@remove) {
    $doit = 1;
    local(@new) = ();
    for $path (@paths) {
      grep($path eq $_, @remove) || push(@new, $path);
    }
    @paths = @new;
  }

  # Find those dirs that are not already available in the recipe-dir path
  local(@append)=();
  for $plugindir (@plugindirs) {
    -e $plugindir or next;

    local($inode) = (stat($plugindir))[1];
    local($found) = 0;
    for $path (grep(-e, @paths)) {
      local(@find) = `find $path -type d`;
      chomp(@find);
      grep((stat($_))[1] == $inode, @find) and $found++, last;
    }
    $found or push(@append, $plugindir),
      print "The esorex recipe-dir path needs to be extended with $plugindir";
  }

  $doit ||= @append;
  $doit || return;

  @append and push(@paths, @append);
  # Need to remove (original) non-existing dirs when updating
  local($newpath) = join(':',grep(-e, @paths));
  local($cmd) = "$esorex --create-config --recipe-dir=$newpath 2>/dev/null";

  print "Updating the esorex recipe-dir configuration";

  local($r) = system("exec $cmd");

  if ($r) {

    $esorex .= " --recipe-dir=$newpath";

    local($event) = &crashtxt($r);

    if (system("exec $esorex --recipes > /dev/null 2>&1") == 0) {
      warn "
Oops, I cannot complete the esorex installation:

I tried to execute the command
$cmd
which $event.

This means that the esorex configuration file could not be created.

You can either let me stop, so you can investigate why esorex failed, or you
can proceed in which case you will need to invoke esorex with a specification
of the recipe location on the Command Line Interface, i.e.
$esorex

";
      &confirm("Should I abort the installation") and exit(1);
    }
  }
}


sub gasgano::lookahead {

  # gasgano must be installed in $HOME :-(
  local($gasganohome) = "$ENV{HOME}/gasgano";

  -e $gasganohome or return 1;
  -e "$gasganohome.old" or return 1;

  warn "
Oops, I cannot proceed with the gasgano installation:

gasgano must be installed in $gasganohome.
You appear to already have gasgano installed there.
I intended to rename your existing gasgano installation to
$gasganohome.old but
$gasganohome.old already exists.

You can either let me
D) Delete the current $gasganohome before I install gasgano there,
R) Replace $gasganohome.old with your current $gasganohome, or
S) Stop, so you can backup both $gasganohome and $gasganohome.old,
   and then restart the installation.

";

  local($_);
  do {
    printf("D) Delete, R) Replace, S) Stop ? [d/R/s] ");

    -t STDIN ||
      die "I cannot get your answer because my standard input is not opened to a tty\n";

    $_ = <STDIN>;
  } until (/^\s*[drs]?\s*$/i);

  /s/i and exit(-1);

  local($delete) = /d/i ? $gasganohome : "$gasganohome.old";

  # Try to prevent failure in the cleanup (but ignore a failure here)
  local(@chmod) = `find $delete -type d`;
  @chmod and chomp(@chmod), chmod(0700, @chmod);
  &do_or_die("/bin/rm -rf $delete");

}

sub gasgano::gotcpl {
  local($prefixdir)=@_;

  scalar glob(join(' ', map("$prefixdir/$_/libcplgasgano.*", @libdir)));

}

sub gasgano::expand_link {
  local($fullpath, $peel) = @_;

  defined($fullpath) or return;

  while (!&gasgano::check_jdk($fullpath, $peel) && -l $fullpath) {
    local($_) = readlink($fullpath);
    if (/^\//) {
      $fullpath = $_;
    } else {
      $fullpath =~ s/\/+[^\/+]+$// while s/^\.\.\///;
      $_ and $fullpath =~ s/[^\/]+$/$_/;
    }
  }

  $fullpath;

}

sub gasgano::check_jdk {
  local($jhome, $peel) = @_;

  # Verify if $JAVA_HOME is set to the JDK

  defined($jhome) or return;

  # Remove $peel entries from end
  $jhome =~ s/\/+[^\/]+$// while ($peel--);

  -e $jhome or return;

  local($bindir, $incdir);

  # Check for presence of executable $JAVA_HOME/{bin,Commands}/javah
  for $dir ('bin', 'Commands') {
    -x "$jhome/$dir/javah" and $bindir = $dir, last;
  }

   # Check for presence of $JAVA_HOME/{include,Headers}/jni.h
  for $dir ('include', 'Headers') {
    -e "$jhome/$dir/jni.h" and $incdir = $dir, last;
  }

  if (!defined($bindir) || !defined($incdir) and opendir(IN, $jhome)) {
     # Last, desperate try ...
     # If the javah and jni.h are found in too exotic directories,
     # then the configure --with-java of CPL may need an upgrade
     # too work with them.
     for $dir (readdir(IN)) {
        $dir eq '.' || $dir eq '..' and next;
        defined($bindir) or -x "$jhome/$dir/javah" and $bindir = $dir;
        defined($incdir) or -e "$jhome/$dir/jni.h" and $incdir = $dir;
     }
     closedir(IN);
  }

  defined($bindir) and defined($incdir) or return;

  -x "$jhome/$bindir/javah" or return;

  -e "$jhome/$incdir/jni.h" or return;

  if ($jhome =~ /\/+Versions\/+Current\/*$/) {
    local($jjhome) = &gasgano::check_jdk($jhome, 2);
    $jjhome and $jhome = $jjhome;
  }

  $jhome;

}

sub gasgano::find_java_home {
  local($exe)=@_;

  # use $exe to guess $ENV{JAVA_HOME}

  local($_, $fullpath, $jhome);

  $fullpath = &gasgano::expand_link(&is_exe($exe), 2) or return;

  # Peel off executable name
  $fullpath =~ s#/+[^/]+$## or return;

  $fullpath = &gasgano::expand_link($fullpath, 1);

  # Peel off executable directory
  ($jhome = $fullpath) =~ s#/+[^/]+$## or return;

  $jhome = &gasgano::expand_link($jhome, 0);

  &gasgano::check_jdk($jhome) and return $jhome;

  # If the above fails, try all subdirs (Needed on MacOS)
  for $dir ('/System/Library/Frameworks/JavaVM.framework', glob("$jhome/*")) {
    &gasgano::check_jdk($dir) and return $dir;
  }

  if (-e '/System') {
    # Even more desperate
    for $dir (`find /System -name javah`) {
      &gasgano::check_jdk($dir) and return $dir;
    }
  }

  $jhome;
}

sub gasgano::check_java_home {
  local($dist)=@_;

  # Check if $JAVA_HOME is already set to the JDK

  &gasgano::check_jdk($ENV{JAVA_HOME}) and return 1;

  # It was not.
  # On Mac OS it is so difficult to predict this location,
  # that there is a tool for finding it. Try it regardless of OS.
  if (-x '/usr/libexec/java_home') {
    local($java_home) = `/usr/libexec/java_home`; # Drop all but 1st line
    if (!$?) {
       chop($java_home);
       local($jjhome) = &gasgano::check_jdk($java_home);
       defined($jjhome) and $ENV{JAVA_HOME} = $jjhome, return 1;
    }
  }

  for $jexe ('java', 'javah') { 
    local($jhome) = &gasgano::find_java_home($jexe);

    defined($jhome) || next;
    local($jjhome) = &gasgano::check_jdk($jhome);
    defined($jjhome) and $ENV{JAVA_HOME} = $jjhome, return 1;
  }

  warn "
Oops, I cannot install gasgano, because I cannot find the
Java Development Kit (JDK).

You can either let me proceed with the installation, in which case
gasgano will _not_ be installed, or you can abort the installation,
install the JDK, and then restart the installation.

To install the JDK you need to:

1) Perform the actual installation of the JDK.
   On modern systems, such as Ubuntu, this can be done simply
   by opening the software installation manager and selecting the JDK.
2) In case the JDK installation has already been done, make sure that
   \$JAVA_HOME is export'ed

Please note that you will _not_ be able to use any existing gasgano
installation on your machine as a front-end for running the pipeline
recipes and that you therefore _must_ install the JDK if you wish to
use gasgano as a front-end for running the pipeline recipes.

";

  &confirm("Should I abort the installation, and let you install the JDK") and
    exit(1);

  0;

}

sub gasgano::install {
  local($dist)=@_;

  -s $dist || die "Invalid gasgano dist, $dist\n". $developers_help;

  print "Installing gasgano\n";

  local($dir) = $dist;
  $dir =~ s/\.tar\.gz$// || die "Invalid gasgano dist name $dir".$developers_help;

  $dir eq 'gasgano' and
    die "Will not unpack a gasgano distribution with no version number\n".$developers_help;

  # gasgano must be installed in $HOME :-(
  local($gasganohome) = "$ENV{HOME}/gasgano";
  if (-e $gasganohome) {
     -e "$gasganohome.old" &&
      die "Will not rename $gasganohome to existing $gasganohome.old\n";
     rename($gasganohome, "$gasganohome.old") or
      die "Could not rename $gasganohome to $gasganohome.old\n";
  }

  local($gz) = $dist =~ /\.gz/ ? 'z' : ($dist =~ /\.bz2/ ? 'j' : '');

  &do_or_die("tar -x${gz}f $dist");

  print "Moving $dir to $gasganohome";
  print '';
  &do_or_die("mv $dir $gasganohome");

  print "Gasgano installation complete";
  print '';

  $gasganohome;


}

sub gasgano::config {
  local($gasganohome, *plugindirs, *calib)=@_;

  # Make hash of plugins with their location
  local(%allplugins);
  local($gasganorcdefault) = '';
  local(@gasganorcdefaultinstrume) = ();
  for $plugindir (@plugindirs) {
      $plugindir =~ /esopipes-plugins\/(\w+)/ || $plugindir =~ /(\w+)\/plugins/;
      $instrume = $1;
      $instrume || next;
      opendir(IN, $plugindir) or
        print("Skipping $plugindir, could not open it for reading"), next;

      %{$plugins{$instrume}}=();

      # FIXME: Assuming that the recipename is the basename of the .so
      grep(/(.+)\.$mso$/o && ($plugins{$instrume}{$1}="$plugindir/$_"), readdir(IN));
      closedir(IN);

      %allplugins = (%allplugins, %{$plugins{$instrume}});
  }

  if (opendir(IN,"$gasganohome/config/")) {
    local(@prefs) = grep(/\.prefs$/,readdir(IN));
    closedir(IN);

    # Try to match a .prefs file with the pipeline
    for $plugindir (@plugindirs) {
      $plugindir =~ /esopipes-plugins\/(\w+)/ || $plugindir =~ /(\w+)\/plugins/;
      $instrume = $1;
      local($nprefs) = 0;
      for $prefs (grep(/$instrume/i, @prefs)) {
        $nprefs++;
        local(@calib) = grep(/\b$instrume\-calib/,@calib);
        local($rul) = $prefs;
        $rul =~ s/prefs$/rul/;
        local($oca) = $prefs;
        $oca =~ s/prefs$/oca/;
        $oca =~ tr/A-Z/a-z/; # .oca file is lower case
        # Last one is selected:
        @gasganorcdefaultinstrume = map("$gasganohome/config/$_", $oca, $rul);
        &gasgano::update_prefs("$gasganohome/config/$prefs",
                               \@gasganorcdefaultinstrume,
                               \%{$plugins{$instrume}}, \@calib);
        $gasganorcdefault = "$gasganohome/config/$prefs"; # Last one is selected
      }
      $nprefs or
         print("No gasgano preference file to be updated for pipeline $instrume\n"),
        next;
      $nprefs > 1 and
        warn("Updated preferences for pipeline $instrume with $nprefs preferences\n");
    }
  }

  local($gasganorc) = "$ENV{HOME}/.gasganorc";
  if (-f $gasganorc) {
    &cp_or_die("$ENV{HOME}/.gasganorc", "$ENV{HOME}/.gasganorc.old");
    &gasgano::update_prefs($gasganorc, \@gasganorcdefaultinstrume, \%allplugins,
                           \@calib);
  } elsif (-f $gasganorcdefault) {
    print "Copying $gasganorcdefault to $ENV{HOME}/.gasganorc\n";
    &cp_or_die($gasganorcdefault, "$ENV{HOME}/.gasganorc");
  }

  -f $gasganorc or warn "Could not create/update $gasganorc\n";

}

sub gasgano::update_prefs {
  local($prefs, *rul, *plugins, *calib)=@_;

  open(IN, $prefs) || die $prefs.$developers_help;
  local(@lines)=<IN>;
  close(IN);

  grep(/Gasgano Application Properties/, @lines) || return; # Obsolete format

  print "Updating $prefs\n";

  local(%recipeset);
  local($recipeset) = grep(/^RECIPE_SET=/, @lines);
  if (defined($recipeset)) {
    $recipeset =~ s/^RECIPE_SET=//;
    for $recipedef (split(/\s*;+\s*/, $recipeset)) {
       local($a,$b) = $recipedef =~ /(.+)=(.+)/;
       $a =~ /\// and ($a,$b) = ($b,$a); # Support old format
       -e $b || next; # Drop non-existing files

       # FIXME(DFS03146): Assume that the recipename is listed correctly in the .prefs
       $recipeset{$a}=$b;
     }
  }

  local(%soset)=();
  # Create the .so set from the new plugins
  grep($plugins{$_} =~ /([^\/]+)\.$mso$/o && ($soset{$1}=$_), keys %plugins);
  # Overwrite the plugin name of the .so with the one from the .prefs
  grep($recipeset{$_} =~ /([^\/]+)\.$mso$/o && ($soset{$1}=$_), keys %recipeset);

  # For each .so we now have the correct plugin name

  # For each .so we need to update the location
  local(%soloc)=();
  grep($recipeset{$_} =~ /([^\/]+)\.$mso$/o && ($soloc{$1}=$recipeset{$_}), keys %recipeset);
  # Overwrite with new locations
  grep($plugins{$_} =~ /([^\/]+)\.$mso$/o && ($soloc{$1}=$plugins{$_}), keys %plugins);

  # Update the recipe set with the recipename and its location for each .so
  $recipeset = "RECIPE_SET=". join(';', map("$soset{$_}=$soloc{$_}", sort keys %soset));

  local($rul, $rulbase);
  grep(-s $_ && ($rul ||= $_), @rul) and # Choose 1st
    $rul =~ /\.(rul|oca)$/, $rulbase = $1;
  local($didrecipeset) = 0;
  for (@lines) {
    # Update the location of the .rul files
    /^CLASSRULE_FILE=/ && defined($rulbase) && 
    $rul =~ /(\/config\/.+\.$rulbase)$/ and
      $_ = "CLASSRULE_FILE=$ENV{HOME}/gasgano$1\n";

    # Update the recipe locations
    /^RECIPE_SET=/ and $didrecipeset = 1, $_ = "$recipeset\n";

    # Update the location of data files with those of the calibration data
    if (/^DATA_FILES=/) {
      local(%dir);
      for $calib (@calib) {
        $calib =~ s/(.+)\-calib(.+)$mtar/$calibdir\/calib\/$1$2/ || die $calib;
        grep(/^(.+)\// && $dir{$1}++, `find $calib -name \\*.fits`);
      }
      $_ = 'DATA_FILES=' . join(';', sort keys %dir) . "\n";
    }
  }

  -w $prefs || chmod(0644, $prefs);
  open(OUT,">$prefs") || die $prefs.$developers_help;
  print OUT @lines;
  $didrecipeset or print OUT "$recipeset\n";

  close(OUT);
}

sub install_calib {
  local($gasganohome, @calib) = @_;

  print '';

  for $calib (@calib) {

    print "Installing calibration files from $calib";

    local($dir) = $calib;
    $dir =~ s/$mtar// || die $dir.$developers_help;

    local($gz) = $calib =~ /\.gz/ ? 'z' : ($calib =~ /\.bz2/ ? 'j' : '');

    &do_or_die("tar -x${gz}f $calib");
    chdir($dir) || die "Could not chdir to $dir (directory should be created by $calib)\n"
      .$developers_help;

    if (defined($gasganohome) && -e $gasganohome && chdir('gasgano')) {
      if (chdir('config')) {
        # Move supplied gasgano configuration files, if present
        if (opendir(IN, '.')) {
          local(@copy) = grep(/\.(?:rul|prefs|oca)$/, readdir(IN));
          closedir(IN);
          if (@copy) {
            local($dest) = "$gasganohome/config/";
            -e $dest || mkdir($dest, 0755);
            print "  Copying gasgano files to $dest";
            &cp_or_die(@copy, "$gasganohome/config/");
          }
        }
        chdir('..') || die;
      }
      if (-d 'scripts') {
        &do_or_die("cp -rf scripts $gasganohome/");
      }
      chdir('..') || die;
    }

    local($targetdir) = $dir;
    $targetdir =~ s/\-calib//;

    &sudomkdir("$calibdir/calib/$targetdir", $sudocalib) ||
      die "Could not create directory $calibdir/calib/$targetdir".$developers_help;

    # Temporarily mv gasgano to avoid installing it in the calibdir
    local($didmvgasgano) = rename('gasgano', '.gasgano');
    # Copy without preserving (the unknown) permissions
    &do_or_die("$sudocalib cp -fR * $calibdir/calib/$targetdir");
    $didmvgasgano and rename('.gasgano', 'gasgano');

    print '';
    chdir('..');
  }
}

sub offer {
  opendir(IN,'sof') || return;

  local(@try) = grep(/(\w+)\.sof/, readdir(IN));
  closedir(IN);

  @try || return;

  print "You are welcome to try out a recipe:";
  $srcdir =~ /^\.(\/+\.)*\/*$/ || print "cd $srcdir";
  print "$esorex --help";
  grep(/(\w+)\.sof/ && print("$esorex --suppress-prefix $1 sof/$_"), @try);

}

sub is_exe {
  local($exe)=@_;

  local($_);

  for (split(/:/,$ENV{PATH})) {
    -x "$_/$exe" && !-d "$_/$exe" && return("$_/$exe");
  }

  undef;

}

sub crashtxt {
  local($r) = @_;

  local($txt) = ($r & 127) ? "was killed by signal ".($r & 127)
                           : "failed with error code ".($r>>8);

  ($r & 128) and $txt .= " causing a core-dump";

  $txt;

}

sub distdir {
  local($targz) = @_;

  $targz =~ /(.+)$mtar/ or return;

  defined($distdir{$1}) ? $distdir{$1} : $1;

}


sub get_cpu_features {

    local(%features)=();

    # linux
    if (open(IN,'/proc/cpuinfo')) {
        while (<IN>) {
            chomp;
            if( /^flags\s*:\s*(.*$)/) {
                %features = map { $_ => 1 } split(/ /, $1);
                last;
            }
        }
        close(IN);
    # darwin
    } elsif (open(IN, 'sysctl -n machdep.cpu.features |')) {
        while (<IN>) {
            %features = map { $_ => 1 } split(/ /, lc($_));
            last;
        }
        close(IN);
    } else {

        eval { use Config; };

        if (%Config) {

# Some actual pairs of values
# - with Model name and flags from /proc/cpuinfo when present
# archname                       myarchname
# x86_64-linux-thread-multi      x86_64-linux
#   Intel(R) Xeon(R) CPU           E7540
#   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm syscall nx rdtscp lm constant_tsc ida nonstop_tsc pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr sse4_1 sse4_2 popcnt lahf_lm

# x86_64-linux-gnu-thread-multi  x86_64-linux
#   Intel(R) Core(TM)2 Duo CPU     E6750
#   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good aperfmperf pni dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm lahf_lm dts tpr_shadow vnmi flexpriority

# i386-linux-thread-multi        i686-linux
#   Intel(R) Xeon(R) CPU           E5345
#   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc pni monitor ds_cpl vmx est tm2 cx16 xtpr lahf_lm

# i386-linux-thread-multi        i686-linux
#   Pentium III (Katmai)
#   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse

# darwin-thread-multi-2level     i386-darwin
# darwin-thread-multi-2level     ppc-darwin

           if (grep(defined($Config{$_}) && $Config{$_} =~ /x86.64/,
                   'archname', 'myarchname')) {
#             amd64 always has sse2
              $features{'sse2'}=$features{'sse'}=1;
           } elsif (grep(defined($Config{$_}) && $Config{$_} =~ /i386-darwin/,
                   'archname', 'myarchname')) {
#            Intel-based Macs always has sse2 - which is always supported
#            by the one compiler (gcc) that currently works with CPL
             $features{'sse2'}=$features{'sse'}=1;
           }
        }
    }

    local (@simd)=('sse', 'sse2', 'sse3', 'ssse3',
                   'sse4', 'sse4.1', 'sse4.2', 'sse4a', 'sse5',
                   'avx', 'avx2');
    foreach (@simd) {
        $features{$_} && !system((defined($ENV{'CC'}) ? '$CC' : 'gcc')
               ." -m$_ -E - </dev/null >/dev/null 2>/dev/null")
        || delete($features{$_});

    }

    %features;
}


# Pipeline specific configure options here

sub pipeline_config {
  local($prefixdir)=@_;

  # This function is called for an pipeline which does not have its own
  # config-function, e.g. &detmon::config()

  '';

}

# Non-standard configure options here

sub qfits::config {
  local($prefixdir)=@_;

  # xmemory causes spurious warnings under darwin
  $^O eq 'darwin' ? ' --enable-memory-mode=1' : '';

}

sub cpl::config {
  local($prefixdir)=@_;

  local($_) = defined($wcslib::doinstall) ? "--with-wcs=$prefixdir "
                                          : '--without-wcs ';

  $gasgano::doinstall or $_ .= '--enable-gasgano=no ';

  defined($fftw::doinstall) or $_ .= '--without-fftw ';

  $^O eq 'darwin' and $_ .= '--enable-threads=no ';

  $_

  # Disable this very expensive debugging feature
   # - and disable a buggy cache-optimization
  . "CPPFLAGS='-DCX_DISABLE_ASSERT -DL2_CACHE_BYTES=0'";

}

sub fftw::config {
  local($prefixdir, $installs)=@_;

  local(%features) = &get_cpu_features();
  # fftw has runtime feature detection but we need to check for compiler support
  local($simd) = "";
  if ($features{'avx'}) {
      $simd .= " --enable-avx"; # ignored by fftw < 3.3
  }
  if ($features{'sse2'}) {
      # fftw < 3.3 does not accept sse2 for floats, fixed in later versions
      $simd .= $installs > 1 ? " --enable-sse" : " --enable-sse2";
  }

  #  --disable-fortran: Disabled not-needed Fortran-callable wrappers
  #                     for improved portability
  $installs > 1
   ? "$simd --disable-fortran --enable-shared --enable-float"
   : "$simd --disable-fortran --enable-shared";

}

sub detmon::config {
  local($prefixdir)=@_;

  " --with-fftw=$prefixdir";

}


sub wcslib::config {
  local($prefixdir)=@_;

  # Supported by wcslib-4.4.4, seems to be ignored by 4.3
  # Needed to work around hardcoded -Wno-globals, which is not
  # supported by g77 v. 4.2.4.
  ' --disable-fortran';

}

sub cfitsio::config {
  local($prefixdir)=@_;

  local(%features) = &get_cpu_features();
  local($simd) = $features{'sse2'} ? "--enable-sse2" : "";
  $features{'ssse3'} and $simd .=  " --enable-ssse3";

  # Has an effect from CFITSIO 3.18
  # Needed from CPL 6.0 and by CPL 5.3 when CFLAGS include -fopenmp
  " $simd --enable-reentrant";

}

sub visir::config {
  local($prefixdir)=@_;

  # cpl not built with openmp on mac (DFS11331)
  # FIXME: removal pending DFS11331
  $^O eq 'darwin' ? ' --disable-openmp' : ' --enable-openmp';
}

1;
