#!/usr/bin/env bash
#
# CONFIGURE, check requirements then set variables and create Makefiles
# Copyright (C) 2017-2021 Xavier Delaruelle
#
# 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, see <http://www.gnu.org/licenses/>.

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

progpath=$0
progdir=${progpath%/*}
prog=${progpath##*/}

# files to translate
targetlist="${progdir}/Makefile.inc ${progdir}/site.exp"

# argument list
arglist="TCLSH PYTHON SPHINXBUILD PS BASENAME RMDIR_IGN_NON_EMPTY SED_ERE \
VERSION RELEASE baseprefix prefix bindir libdir libexecdir etcdir initdir \
datarootdir mandir docdir vimdatadir modulefilesdir setmanpath appendmanpath \
setbinpath appendbinpath setdotmodulespath docinstall vimaddons \
examplemodulefiles builddoc gitworktree usemanpath compatversion EXEEXT \
libtclenvmodules SHLIB_SUFFIX multilibsupport libdir64 libdir32 versioning \
silentshdbgsupport setshellstartup quarantinesupport autohandling \
availindepth implicitdefault extendeddefault moduleshome initconfin pager \
pageropts verbosity color darkbgcolors lightbgcolors termbg lockedconfigs \
icase unloadmatchorder searchmatch modulepath loadedmodules quarantinevars \
wa277 advversspec ml windowssupport nearlyforbiddendays implicitrequirement \
tagabbrev tagcolorname mcookieversioncheck availoutput availterseoutput \
listoutput listterseoutput editor variantshortcut"
compatarglist=
libarglist=

# flags to know if argument has been specified on command-line
defdotmodulespath=1
defcompatversion=1
defpageropts=1

# flags to know a requirement has already been checked
checkedautoreconf=1

# set argument default values
prefix=/usr/local/Modules
setmanpath=y
appendmanpath=n
setbinpath=y
appendbinpath=n
setdotmodulespath=n
docinstall=y
vimaddons=y
examplemodulefiles=y
compatversion=n
EXEEXT=
libtclenvmodules=y
SHLIB_SUFFIX='.so'
multilibsupport=n
libdir64=undefined
libdir32=undefined
versioning=n
silentshdbgsupport=y
setshellstartup=y
quarantinesupport=y
autohandling=n
implicitrequirement=y
availindepth=y
implicitdefault=y
extendeddefault=n
advversspec=n
ml=y
wa277=n
loadedmodules=
quarantinevars=
binsearchpath=/usr/bin:/bin:/usr/local/bin
TCLSH=tclsh
PYTHON=python
SPHINXBUILD=sphinx-build
builddoc=y
gitworktree=y
usemanpath=y
PS=ps
BASENAME=basename
RMDIR_IGN_NON_EMPTY=rmdir
SED_ERE='sed -E'
VERSION=
RELEASE=
initconfin=initdir
pager=less
pageropts='-eFKRX'
verbosity=normal
color=n
darkbgcolors='hi=1:db=2:tr=2:se=2:er=91:wa=93:me=95:in=94:mp=1;94:di=94:al=96:va=93:sy=95:de=4:cm=92:aL=100:L=90;47:H=2:F=41:nF=43:S=46:sS=44'
lightbgcolors='hi=1:db=2:tr=2:se=2:er=31:wa=33:me=35:in=34:mp=1;34:di=34:al=36:va=33:sy=35:de=4:cm=32:aL=107:L=47:H=2:F=101:nF=103:S=106:sS=104'
termbg=dark
lockedconfigs=
unloadmatchorder=returnlast
searchmatch=starts_with
icase=never
# these args are initialized here but as they depend on other argument value
# they will get their default value later (after command-line parse)
bindir=
libdir=
libexecdir=
etcdir=
initdir=
datarootdir=
mandir=
docdir=
vimdatadir=
modulefilesdir=
moduleshome=
modulepath=
windowssupport=n
nearlyforbiddendays=14
tagabbrev='auto-loaded=aL:loaded=L:hidden=H:hidden-loaded=H:forbidden=F:nearly-forbidden=nF:sticky=S:super-sticky=sS'
tagcolorname=
mcookieversioncheck=y
availoutput='modulepath:alias:dirwsym:sym:tag:key'
availterseoutput='modulepath:alias:dirwsym:sym:tag'
listoutput='header:idx:variant:sym:tag:key'
listterseoutput='header'
editor='vi'
variantshortcut=

# git branch to use to build compat version
compatbranch='c-3.2'


# check if output/error channels are attached to terminal
for i in 1 2; do
   if [ -t $i ]; then
      fdisterm[$i]=1
   else
      fdisterm[$i]=0
   fi
done

# apply SGR code to message if output channel is attached to terminal
sgr() {
   local fd=$1
   local code=$2
   local msg=$3
   if [ ${fdisterm[$fd]} -eq 1 ]; then
      local out="\033[${code}m${msg}\033[0m"
   else
      local out=$msg
   fi
   echo $out
}

# print message on stderr then exit
echo_error() {
   local firstln=1
   while [ $# -gt 0 ]; do
      if [ $firstln -eq 1 ]; then
         local msg="$(sgr 2 31 ERROR): $1"
         firstln=0
      elif [ "$1" = 'tryhelp' ]; then
         msg="${msg}\n  Try \`$progpath --help' for more information"
      else
         msg="${msg}\n  $1"
      fi
      shift
   done
   echo -e "${msg}" >&2
   exit 1
}

# print message on stderr then exit
echo_warning() {
   echo -e "$(sgr 1 33 WARNING): $1"
}


# print usage message
echo_usage() {
   echo "Usage: $progpath [OPTION]...
Check requirements then set variables and create Makefiles.

Defaults for the options are specified in square brackets.

Configuration:
  -h, --help              display this help and exit

Installation directories:
  --prefix=PREFIX         install files in PREFIX [$prefix]

By default, \`make install' will install all the files in
\`$prefix/bin', \`$prefix/libexec', etc. You
can specify an installation prefix other than \`$prefix'
using \`--prefix', for instance \`--prefix=\$HOME'.

For better control, use the options below.

Fine tuning of the installation directories:
  --bindir=DIR            user executables [PREFIX/bin]
  --libdir=DIR            object code libraries [PREFIX/lib]
  --libexecdir=DIR        program executables [PREFIX/libexec]
  --etcdir=DIR            program configurations [PREFIX/etc]
  --initdir=DIR           environment initialization scripts [PREFIX/init]
  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
  --mandir=DIR            man documentation [DATAROOTDIR/man]
  --docdir=DIR            documentation root [DATAROOTDIR/doc]
  --vimdatadir=DIR        Vim addons directory [DATAROOTDIR/vim/vimfiles]
  --modulefilesdir=DIR    system modulefiles [PREFIX/modulefiles]

Optional Features:
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-set-manpath    set mandir to MANPATH in init scripts [yes]
  --enable-append-manpath append rather prepend mandir to MANPATH [no]
  --enable-set-binpath    set bindir to PATH in init scripts [yes]
  --enable-append-binpath append rather prepend bindir to PATH [no]
  --enable-dotmodulespath, --enable-modulespath
                          configure modules path in .modulespath file rather
                          than modulerc file (or respectively in modulespath
                          file rather initrc file if \`--with-initconf-in' is
                          set to 'etcdir') [no]
  --enable-doc-install    install documentation files in the documentation
                          directory defined with \'docdir' (no impact on
                          man pages installation) [yes]
  --enable-vim-addons     install Vim addon files in the directory defined
                          with \'vimdatadir' [yes]
  --enable-example-modulefiles
                          install in 'modulefilesdir' some modulefiles
                          provided as examples [yes]
  --enable-compat-version also build and install Modules compatibility (C)
                          version and enable switching capabilities between
                          the two versions [no]
  --enable-libtclenvmodules
                          build and install the Modules Tcl extension library
                          and enable its load in modulecmd.tcl [yes]
  --enable-multilib-support
                          support multilib systems by looking at an alternative
                          location where to find the Modules Tcl extension
                          library depending on current machine architecture.
  --enable-versioning     append modules version to installation prefix and
                          deploy a \`versions' modulepath, shared between all
                          versioning enabled Modules installation, containing
                          modulefiles that enable to switch from one Modules
                          version to another [no]
  --enable-silent-shell-debug-support
                          generate code in module function definition and
                          initialization scripts to add support for silencing
                          shell debugging properties [yes]
  --enable-set-shell-startup
                          set when module function is defined the shell
                          startup file to ensure that the module function is
                          still defined in sub-shells [yes]
  --enable-quarantine-support
                          generate code in module function definition and
                          initialization scripts to add support for the
                          environment variable quarantine mechanism [yes]
  --enable-auto-handling  set modulecmd.tcl to automatically apply automated
                          modulefiles handling actions, like loading the
                          pre-requisites of a modulefile when loading this
                          modulefile [no]
  --enable-implicit-requirement
                          implicitly define a requirement toward modules
                          specified on ``module load`` or ``module unload``
                          commands in modulefile [yes]
  --enable-avail-indepth  control whether \`avail' search results should
                          recursively include or not modulefiles from
                          directories matching search query [yes]
  --enable-implicit-default
                          implicitly set a default version for modules with
                          none defined [yes]
  --enable-extended-default
                          allow partial module version specification [no]
  --enable-advanced-version-spec
                          activate the advanced module version specificers to
                          specify finer constraints on module version. [no]
  --enable-ml             define \`ml' command when Modules initializes [yes]
  --enable-color          control colored output. \`yes' equals to the \`auto'
                          color mode. \`no' equals to the \`never' color mode
                          [no]
  --enable-wa-277         activate workaround for Tcsh history issue [no]
  --enable-windows-support
                          install all required files for Windows platform [no]
  --enable-new-features   enable all new features that are disabled by default
                          due to substantial behavior changes they imply on
                          Modules 4 [no]
  --enable-mcookie-version-check
                          enable check of the version specified right after
                          Modules magic cookie in modulefiles, which defines
                          the minimal version of the Modules tool to use to
                          evaluate the modulefile [yes]

Optional Packages:
  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
  --with-bin-search-path=PATHLIST
                          list of paths to look at when searching the location
                          of tools required to build and configure Modules
                          [$binsearchpath]
  --with-moduleshome      location of the main Modules package file
                          directory [$prefix]
  --with-initconf-in=VALUE
                          location where to install Modules initialization
                          configuration files. Either \`initdir' or \`etcdir'
                          [initdir]
  --with-tclsh=BIN        name or full path of Tcl interpreter shell [tclsh]
  --with-pager=BIN        name or full path of default pager program to use to
                          paginate informational message output (can be super-
                          seeded at run-time by environment variable) [$pager]
  --with-pager-opts=OPTLIST
                          settings to apply to default pager program [$pageropts]
  --with-verbosity=VALUE  specify default message verbosity. accepted values
                          are \`silent', \`concise', \`normal', \`verbose',
                          \`verbose2', \`trace', \`debug' and \`debug2'
                          [$verbosity]
  --with-dark-background-colors=SGRLIST
                          default color set to apply if terminal background
                          color is defined to \`dark'. Each element in SGRLIST
                          is an output item associated to a Select Graphic
                          Rendition (SGR) code (elements in SGRLIST are
                          separated by \`:')
                          [$darkbgcolors]
  --with-light-background-colors=SGRLIST
                          default color set to apply if terminal background
                          color is defined to \`light'. Expect the same syntax
                          than described for \`--with-dark-background-colors'
                          [$lightbgcolors]
  --with-terminal-background=VALUE
                          terminal background color which determines the color
                          set to apply by default between the \`dark' background
                          colors or the \`light' background colors [$termbg]
  --with-locked-configs=CONFIGLIST
                          ignore environment variable superseding value for
                          listed configuration options. accepted option names
                          in CONFIGLIST are \`extra_siteconfig' and
                          \`implicit_default' (each option name in CONFIGLIST
                          should be separated by \` ') []
  --with-unload-match-order=VALUE
                          when unloading a module if multiple loaded modules
                          match the request, unload module loaded first
                          (\`returnfirst') or module loaded last (\`returnlast')
                          [$unloadmatchorder]
  --with-search-match=VALUE
                          when searching for a module, query string should
                          match the begining of module name (\`starts_with')
                          or any part of module name (\`contains')
                          [$searchmatch]
  --with-icase=VALUE      apply a case insensitive match to module
                          specification on search-like sub-commands
                          (\`search'), on all sub-commands and modulefile Tcl
                          commands (\`always') or disable case insensitive
                          match (\`never') [$icase]
  --with-nearly-forbidden-days=VALUE
                          define the number of days a module is considered
                          nearly forbidden prior reaching its expiry date [$nearlyforbiddendays]
  --with-tag-abbrev=ABBRVLIST
                          define the abbreviation to use when reporting each
                          module tag. Each element in ABBRVLIST is a tag name
                          associated to an abbreviation string (elements in
                          ABBRVLIST are separated by \`:')
                          [$tagabbrev]
  --with-tag-color-name=TAGLIST
                          define the tags whose graphical rendering should be
                          applied over their name instead of over the name of
                          the module they are attached to. Each element in
                          TAGLIST is a tag name or abbreviation (elements in
                          TAGLIST are separated by \`:') [$tagcolorname]
  --with-avail-output=LIST
                          specify content to report on avail sub-command
                          regular output. elements accepted in LIST are:
                          \`modulepath', \`alias', \`dirwsym', \`sym', \`tag'
                          and \`key' (elements in LIST are separated by \`:')
                          [$availoutput]
  --with-avail-terse-output=LIST
                          specify content to report on avail sub-command
                          terse output. elements accepted in LIST are:
                          \`modulepath', \`alias', \`dirwsym', \`sym', \`tag'
                          and \`key' (elements in LIST are separated by \`:')
                          [$availterseoutput]
  --with-list-output=LIST
                          specify content to report on list sub-command
                          regular output. elements accepted in LIST are:
                          \`header', \`idx', \`variant', \`sym', \`tag' and
                          \`key' (elements in LIST are separated by \`:')
                          [$listoutput]
  --with-list-terse-output=LIST
                          specify content to report on list sub-command terse
                          output. elements accepted in LIST are: \`header',
                          \`idx', \`variant', \`sym', \`tag' and \`key'
                          (elements in LIST are separated by \`:')
                          [$listterseoutput]
  --with-variant-shortcut=SHORTCUTLIST
                          define shortcut characters to specify variant names.
                          Each element in SHORTCUTLIST is a variant name
                          associated to a shortcut character (elements in
                          SHORTCUTLIST are separated by \`:') [$variantshortcut]
  --with-editor=BIN       name or full path of default editor program to use to
                          edit modulefiles [$editor]
  --with-modulepath=PATHLIST
                          default modulepaths to set in default configuration
                          file to be enabled (each path in PATHLIST should
                          be separated by \`:') [PREFIX/modulefiles or
                          BASEPREFIX/\$MODULE_VERSION/modulefiles if versioning
                          installation mode enabled]
  --with-loadedmodules=MODLIST
                          default modulefiles to set in default configuration
                          file to be loaded (each modulefile name in MODLIST
                          should be separated by \`:') []
  --with-quarantine-vars='VARNAME[=VALUE] ...'
                          environment variables to put in quarantine when
                          running the module command to ensure it a sane
                          execution environment (each variable should be
                          separated by \` '). a value can eventually be set to
                          a quarantine variable instead of emptying it. []
  --with-tcl              directory containing the Tcl configuration script
                          tclConfig.sh.
  --with-tclinclude       directory containing the Tcl header files.
  --with-python=BIN       name or full path of Python interpreter command to
                          use as shebang for helper scripts [$PYTHON]

Depending on the above configuration options the files are approximately
placed in the following directory structure:
  PREFIX/
    bin/
    etc/
    init/
    lib/
    libexec/
    share/
      doc/
      man/
        man1/
        man4/
      vim/
        vimfiles/
    modulefiles/"
}

# check requirement availability
check_requirement() {
   typeset req=$1
   typeset optmesg=$2
   typeset cmdenv=$3

   if [ $# -gt 3 ]; then
      shift 3
   else
      shift $#
   fi

   for cmd in $req "${@}"; do
      if [ -n "$cmd" ]; then
         echo -n "checking for ${cmd}... "
         if [ -n "$cmdenv" ]; then
            cmdsearch="$cmdenv command -v $cmd"
         else
            cmdsearch="command -v $cmd"
         fi
         reqpath=$(eval $cmdsearch)
         if [ -n "$reqpath" ]; then
            echo $reqpath
            break
         else
            echo "not found"
         fi
      fi
   done

   if [ -z "$reqpath" ]; then
      if [ -z "$optmesg" ]; then
         echo_error "$req could not be found"
      # no message output if message set to '-'
      elif [ "$optmesg" != '-' ]; then
         echo_warning "$optmesg"
      fi
   fi
}

# parse optional argument to find if feature is enabled or disabled
get_feature_value() {
   typeset val="${1#*=}"

   if [ "${1//--enable/}" != "$1" ]; then
      if [ "$val" = 'no' ]; then
        echo 'n'
      else
        echo 'y'
      fi
   else
      echo 'n'
   fi
}

# parse optional argument to find package is set or disabled
get_package_value() {
   typeset val="${1#*=}"
   typeset valifunset="$2"

   # use fallback value 'valifunset' when no specific value given
   if [ "${1//--with-/}" != "$1" ]; then
      if [ "$val" = 'no' ] || [ "$val" = "$1" ]; then
        echo "$valifunset"
      else
        echo "$val"
      fi
   else
      echo "$valifunset"
   fi
}

# parse arguments
for arg in "$@"; do
   # set argument value defined with "--arg val" form
   if [ -n "${nextargisval+x}" ]; then
      declare $nextargisval=$arg
      unset nextargisval
      continue
   # set compat and lib argument value defined with "--arg val" form
   elif [ -n "${nextargiscompatlibarg+x}" ]; then
      compatarglist+="$arg "
      libarglist+="$arg " ;
      unset nextargiscompatlibarg
      continue
   # current arg should be skipped
   elif [ -n "${nextargisign+x}" ]; then
      unset nextargisign
      continue
   fi

   case "$arg" in
   --prefix=*)
      prefix="${arg#*=}" ;;
   --prefix)
      nextargisval=prefix ;;
   --bindir=*)
      bindir="${arg#*=}" ;;
   --bindir)
      nextargisval=bindir ;;
   --libdir=*)
      libdir="${arg#*=}" ;;
   --libdir)
      nextargisval=libdir ;;
   --libexecdir=*)
      libexecdir="${arg#*=}" ;;
   --libexecdir)
      nextargisval=libexecdir ;;
   --etcdir=*)
      etcdir="${arg#*=}" ;;
   --etcdir)
      nextargisval=etcdir ;;
   --initdir=*)
      initdir="${arg#*=}" ;;
   --initdir)
      nextargisval=initdir ;;
   --datarootdir=*)
      datarootdir="${arg#*=}" ;;
   --datarootdir)
      nextargisval=datarootdir ;;
   --mandir=*)
      mandir="${arg#*=}" ;;
   --mandir)
      nextargisval=mandir ;;
   --docdir=*)
      docdir="${arg#*=}" ;;
   --docdir)
      nextargisval=docdir ;;
   --vimdatadir=*)
      vimdatadir="${arg#*=}" ;;
   --vimdatadir)
      nextargisval=vimdatadir ;;
   --modulefilesdir=*)
      modulefilesdir="${arg#*=}" ;;
   --modulefilesdir)
      nextargisval=modulefilesdir ;;
   --enable-set-manpath*|--disable-set-manpath)
      setmanpath=$(get_feature_value "$arg") ;;
   --enable-append-manpath*|--disable-append-manpath)
      appendmanpath=$(get_feature_value "$arg") ;;
   --enable-set-binpath*|--disable-set-binpath)
      setbinpath=$(get_feature_value "$arg") ;;
   --enable-append-binpath*|--disable-append-binpath)
      appendbinpath=$(get_feature_value "$arg") ;;
   --enable-dotmodulespath*|--disable-dotmodulespath|--enable-modulespath*|--disable-modulespath)
      setdotmodulespath=$(get_feature_value "$arg")
      defdotmodulespath=0 ;;
   --enable-doc-install*|--disable-doc-install)
      docinstall=$(get_feature_value "$arg")
      defdocinstall=0 ;;
   --enable-vim-addons*|--disable-vim-addons)
      vimaddons=$(get_feature_value "$arg")
      defvimplugin=0 ;;
   --enable-example-modulefiles*|--disable-example-modulefiles)
      examplemodulefiles=$(get_feature_value "$arg")
      defexamplemodulefiles=0 ;;
   --enable-compat-version*|--disable-compat-version)
      compatversion=$(get_feature_value "$arg")
      defcompatversion=0 ;;
   --enable-libtclenvmodules*|--disable-libtclenvmodules)
      libtclenvmodules=$(get_feature_value "$arg") ;;
   --enable-multilib-support*|--disable-multilib-support)
      multilibsupport=$(get_feature_value "$arg") ;;
   --enable-versioning*|--disable-versioning)
      versioning=$(get_feature_value "$arg") ;;
   --enable-silent-shell-debug-support*|--disable-silent-shell-debug-support)
      silentshdbgsupport=$(get_feature_value "$arg") ;;
   --enable-set-shell-startup*|--disable-set-shell-startup)
      setshellstartup=$(get_feature_value "$arg") ;;
   --enable-quarantine-support*|--disable-quarantine-support)
      quarantinesupport=$(get_feature_value "$arg") ;;
   --enable-auto-handling*|--disable-auto-handling)
      autohandling=$(get_feature_value "$arg") ;;
   --enable-implicit-requirement*|--disable-implicit-requirement)
      implicitrequirement=$(get_feature_value "$arg") ;;
   --enable-avail-indepth*|--disable-avail-indepth)
      availindepth=$(get_feature_value "$arg") ;;
   --enable-implicit-default*|--disable-implicit-default)
      implicitdefault=$(get_feature_value "$arg") ;;
   --enable-extended-default*|--disable-extended-default)
      extendeddefault=$(get_feature_value "$arg") ;;
   --enable-advanced-version-spec*|--disable-advanced-version-spec)
      advversspec=$(get_feature_value "$arg") ;;
   --enable-ml*|--disable-ml)
      ml=$(get_feature_value "$arg") ;;
   --enable-wa-277*|--disable-wa-277)
      wa277=$(get_feature_value "$arg") ;;
   --enable-windows-support*|--disable-windows-support)
      windowssupport=$(get_feature_value "$arg") ;;
   --enable-mcookie-version-check*|--disable-mcookie-version-check)
      mcookieversioncheck=$(get_feature_value "$arg") ;;
   --with-bin-search-path=*|--without-bin-search-path)
      binsearchpath=$(get_package_value "$arg") ;;
   --with-moduleshome=*|--without-moduleshome)
      moduleshome=$(get_package_value "$arg") ;;
   --with-initconf-in=*|--without-initconf-in)
      initconfin=$(get_package_value "$arg") ;
      allowedval=" initdir etcdir " ;
      if [ "${allowedval// $initconfin /}" = "$allowedval" ]; then
         echo_error "Bad value for option \`--with-initconf-in'"\
            "Allowed values are:$allowedval"
      fi ;;
   --with-tclsh=*|--without-tclsh)
      tclshbin=$(get_package_value "$arg") ;;
   --with-pager=*|--without-pager)
      pager=$(get_package_value "$arg") ;;
   --with-pager-opts=*|--without-pager-opts)
      pageropts=$(get_package_value "$arg")
      defpageropts=0 ;;
   --with-verbosity=*|--without-verbosity)
      verbosity=$(get_package_value "$arg")
      allowedval=" silent concise normal verbose trace debug debug2 " ;
      if [ "${allowedval// $verbosity /}" = "$allowedval" ]; then
         echo_error "Bad value for option \`--with-verbosity'"\
            "Allowed values are:$allowedval"
      fi ;;
   --enable-color*|--disable-color)
      color=$(get_feature_value "$arg") ;;
   --enable-new-features*|--disable-new-features)
      val=$(get_feature_value "$arg") ;
      echo "TOTO: $val"
      if [ "$val" = 'y' ]; then
         autohandling=y ;
         color=auto ;
         icase=search ;
         extendeddefault=y ;
         advversspec=y ;
      fi ;;
   --with-dark-background-colors=*|--without-dark-background-colors)
      darkbgcolors=$(get_package_value "$arg") ;;
   --with-light-background-colors=*|--without-light-background-colors)
      lightbgcolors=$(get_package_value "$arg") ;;
   --with-terminal-background=*|--without-terminal-background)
      termbg=$(get_package_value "$arg") ;
      allowedval=" dark light " ;
      if [ "${allowedval// $termbg /}" = "$allowedval" ]; then
         echo_error "Bad value for option \`--with-terminal-background'"\
            "Allowed values are:$allowedval"
      fi ;;
   --with-locked-configs*|--disable-locked-configs)
      lockedconfigs=$(get_package_value "$arg") ;
      allowedval=" extra_siteconfig implicit_default " ;
      for val in $lockedconfigs; do
         if [ "${allowedval// $val /}" = "$allowedval" ]; then
            echo_error "Bad value for option \`--with-locked-configs'"\
               "Allowed values are:$allowedval"
         fi ;
      done ;;
   --with-unload-match-order*|--without-unload-match-order)
      unloadmatchorder=$(get_package_value "$arg") ;
      allowedval=" returnlast returnfirst " ;
      if [ "${allowedval// $unloadmatchorder /}" = "$allowedval" ]; then
         echo_error "Bad value for option \`--with-unload-match-order'"\
            "Allowed values are:$allowedval"
      fi ;;
   --with-search-match*|--without-search-match)
      searchmatch=$(get_package_value "$arg") ;
      allowedval=" starts_with contains " ;
      if [ "${allowedval// $searchmatch /}" = "$allowedval" ]; then
         echo_error "Bad value for option \`--with-search-match'"\
            "Allowed values are:$allowedval"
      fi ;;
   --with-icase*|--without-icase)
      icase=$(get_package_value "$arg") ;
      allowedval=" never search always " ;
      if [ "${allowedval// $icase /}" = "$allowedval" ]; then
         echo_error "Bad value for option \`--with-icase'"\
            "Allowed values are:$allowedval"
      fi ;;
   --with-nearly-forbidden-days*|--without-nearly-forbidden-days)
      nearlyforbiddendays=$(get_package_value "$arg") ;
      if [ -z "$nearlyforbiddendays" ] || ! [ "$nearlyforbiddendays" -eq "$nearlyforbiddendays" ] 2>/dev/null\
         || ! [ "$nearlyforbiddendays" -ge 0 ] || ! [ "$nearlyforbiddendays" -le 365 ]; then
         echo_error "Bad value for option \`--with-nearly-forbidden-days'"\
            "Value should be an integer comprised between 0 and 365"
      fi ;;
   --with-tag-abbrev*|--without-tag-abbrev)
      tagabbrev=$(get_package_value "$arg") ;;
   --with-tag-color-name*|--without-tag-color-name)
      tagcolorname=$(get_package_value "$arg") ;;
   --with-avail-output*|--without-avail-output)
      availoutput=$(get_package_value "$arg" "$availoutput") ;
      allowedval=" modulepath alias dirwsym sym tag key " ;
      ORIG_IFS=$IFS ;
      IFS=: ;
      for val in $availoutput; do
         if [ "${allowedval// $val /}" = "$allowedval" ]; then
            echo_error "Bad value '$val' set in option \`--with-avail-output'"\
               "Values allowed in list are:$allowedval"
         fi ;
      done ;
      IFS=$ORIG_IFS ;;
   --with-avail-terse-output*|--without-avail-terse-output)
      availterseoutput=$(get_package_value "$arg" "$availterseoutput") ;
      allowedval=" modulepath alias dirwsym sym tag key " ;
      ORIG_IFS=$IFS ;
      IFS=: ;
      for val in $availterseoutput; do
         if [ "${allowedval// $val /}" = "$allowedval" ]; then
            echo_error "Bad value '$val' set in option \`--with-avail-terse-output'"\
               "Values allowed in list are:$allowedval"
         fi ;
      done ;
      IFS=$ORIG_IFS ;;
   --with-list-output*|--without-list-output)
      listoutput=$(get_package_value "$arg" "$listoutput") ;
      allowedval=" header idx variant sym tag key " ;
      ORIG_IFS=$IFS ;
      IFS=: ;
      for val in $listoutput; do
         if [ "${allowedval// $val /}" = "$allowedval" ]; then
            echo_error "Bad value '$val' set in option \`--with-list-output'"\
               "Values allowed in list are:$allowedval"
         fi ;
      done ;
      IFS=$ORIG_IFS ;;
   --with-list-terse-output*|--without-list-terse-output)
      listterseoutput=$(get_package_value "$arg" "$listterseoutput") ;
      allowedval=" header idx variant sym tag key " ;
      ORIG_IFS=$IFS ;
      IFS=: ;
      for val in $listterseoutput; do
         if [ "${allowedval// $val /}" = "$allowedval" ]; then
            echo_error "Bad value '$val' set in option \`--with-list-terse-output'"\
               "Values allowed in list are:$allowedval"
         fi ;
      done ;
      IFS=$ORIG_IFS ;;
   --with-variant-shortcut=*|--without-variant-shortcut)
      variantshortcut=$(get_package_value "$arg") ;;
   --with-editor=*|--without-editor)
      editor=$(get_package_value "$arg") ;;
   --with-modulepath=*|--without-modulepath)
      modulepath=$(get_package_value "$arg") ;;
   --with-loadedmodules=*|--without-loadedmodules)
      loadedmodules=$(get_package_value "$arg") ;;
   --with-quarantine-vars=*|--without-quarantine-vars)
      quarantinevars=$(get_package_value "$arg") ;;
   --with-tcl=*|--without-tcl)
      compatarglist+="$arg " ;
      libarglist+="$arg " ;;
   --with-tclinclude=*|--with-tcl-inc=*)
      # apply Tcl header specific location to corresponding configuration
      # option of compat version and libtclenvmodules (not same option name)
      val="${arg#*=}" ;
      compatarglist+="--with-tcl-inc=$val " ;
      libarglist+="--with-tclinclude=$val " ;;
   --without-tclinclude|--without-tcl-inc)
      compatarglist+="--without-tcl-inc " ;
      libarglist+="--without-tclinclude " ;;
   --with-python=*|--without-python)
      pythonbin=$(get_package_value "$arg") ;;
   --with-module-path=*)
      echo_warning "Option \`--with-module-path' ignored, use \`--modulepath' instead" ;;
   -h|--help)
      echo_usage
      exit 0
      ;;
   --build|--host|--target)
      # pass argument supported by compat version anb lib to their ./configure
      # script
      compatarglist+="$arg " ;
      libarglist+="$arg " ;
      # and set next arg should also be send to compat version and lib scripts
      nextargiscompatlibarg=y ;;
   --build=*|--host=*|--target=*)
      # pass argument supported by compat version anb lib to their ./configure
      # script
      compatarglist+="$arg " ;
      libarglist+="$arg " ;;
   --with-tcl-lib=*|--without-tcl-lib|--with-tcl-ver=*|--without-tcl-ver|\
   --with-tclx=*|--without-tclx|--with-tclx-ver=*|--without-tclx-ver|\
   --with-tclx-lib=*|--without-tclx-lib|--with-tclx-inc=*|--without-tclx-inc|\
   --with-x|--without-x|--enable-dependency-tracking*|\
   --disable-dependency-tracking)
      # pass argument supported by compat version to its ./configure script
      compatarglist+="$arg " ;;
   --program-prefix|--program-suffix|--program-transform-name|--exec-prefix|\
   --sbindir|--sysconfdir|--datadir|--includedir|--localstatedir|\
   --sharedstatedir|--infodir|--runstatedir|--oldincludedir|--localedir|\
   --htmldir|--dvidir|--pdfdir|--psdir)
      # ignore standard configure option and value which has no meaning here
      echo_warning "Option \`$arg' ignored" ;
      nextargisign=y ;;
   --program-prefix=*|--program-suffix=*|--program-transform-name=*|\
   --exec-prefix=*|--sbindir=*|--sysconfdir=*|--datadir=*|--includedir=*|\
   --localstatedir=*|--sharedstatedir=*|--infodir=*|--runstatedir=*|\
   --oldincludedir=*|--localedir=*|--htmldir=*|--dvidir=*|--pdfdir=*|\
   --psdir=*)
      # ignore standard configure option which has no meaning here
      echo_warning "Option \`$arg' ignored" ;;
   --enable-*|--disable-*|--with-*|--without-*)
      # ignore --enable-*/--with-* options not supported here to comply with
      # GNU Configuration specification
      # (https://www.gnu.org/prep/standards/html_node/Configuration.html)
      echo_warning "Option \`$arg' ignored" ;;
   -*)
      echo_error "Unrecognized option \`$arg'" tryhelp;;
   *=*)
      echo -e "Export \`$arg' in environment" ;
      export $arg ;;
   *)
      # type of system to build the program for (not needed here but useful
      # for true autoconf configure scripts below
      compatarglist+="$arg " ;
      libarglist+="$arg " ;;
   esac
done

# disable compat version build if not explicitely enabled, compat source not
# available and not working from git repository
if [ ! -e '.git' -a ! -d 'compat' -a $defcompatversion -eq 1 ]; then
   echo_warning "\`compat' directory cannot be found, disabling compatibility \
version build"
   compatversion=n
fi

# test requirements availability
check_requirement uname
kernelname=$(uname -s)

# Makefile for this project are GNU Makefile so we must ensure correct make
# command is available, on Linux/Darwin systems the 'make' bin refers to GNU
# Make whereas on other system it is not the default make flavor, so 'gmake'
# should be checked
case "$kernelname" in
   Linux|Darwin|CYGWIN*|MINGW*|MSYS_NT*) make='make' ;;
   *) make='gmake'
esac
check_requirement $make
check_requirement sed
check_requirement runtest "Install \`dejagnu' if you want to run the \
testsuite"
check_requirement manpath 'Will rely on MANPATH to get enabled man directories'
[ -z "$reqpath" ] && usemanpath=n

# rmdir option may not be available, use || true in any case
if [ "$kernelname" = 'Linux' ]; then
    RMDIR_IGN_NON_EMPTY='rmdir --ignore-fail-on-non-empty'
else
    RMDIR_IGN_NON_EMPTY='rmdir'
fi

# sed -E option may not be availble, use -r option otherwise
if [ "$(echo foo | $SED_ERE 's|foo|bar|' 2>/dev/null)" != 'bar' ]; then
   SED_ERE='sed -r'
fi

# if we install from git repository, must have git to fetch current release number
if [ -e '.git' ]; then
   check_requirement git
fi

# if pre-built docs are available (but not from a previous make run), no doc
# build but install pre-built files
oldbuilddoc=$(sed -n '/^builddoc/{s/.*:= //p;q;}' Makefile.inc 2>/dev/null)
if [ -e 'doc/build/diff_v3_v4.txt' ] && [ -e 'doc/build/MIGRATING.txt' ] \
   && [ -e 'doc/build/INSTALL.txt' ] && [ -e 'doc/build/INSTALL-win.txt' ] \
   && [ -e 'doc/build/NEWS.txt' ] && [ -e 'doc/build/CONTRIBUTING.txt' ] \
   && [ -e 'doc/build/module.1.in' ] && [ -e 'doc/build/ml.1' ] \
   && [ -e 'doc/build/modulefile.4' ] && [ "$oldbuilddoc" != 'y' ]; then
   builddoc=p
# test sphinx availability otherwise
else
   # pass even if sphinx not there but no doc will be built
   check_requirement sphinx-build "Install \`sphinx-build' if you want to \
build the documentation" '' 'sphinx-1.0-build'
   SPHINXBUILD=$reqpath
   [ -z "$reqpath" ] && builddoc=n
fi

# get tclsh location from standard PATHs or /usr/local/bin
# or validate location passed as argument
if [ -n "$tclshbin" ]; then
   check_requirement $tclshbin '' "PATH=$binsearchpath"
else
   # also check version-specific names in case no generic tclsh binary exists
   check_requirement $TCLSH '' "PATH=$binsearchpath" 'tclsh8.6' 'tclsh8.5' 'tclsh8.4'
fi
TCLSH=$reqpath

# python is mandatory if installing from git repository to build ChangeLog
if [ -e '.git' ]; then
   missmsg=''
elif [ -n "$pythonbin" ]; then
   missmsg="Specified Python command \`$pythonbin' cannot be found"
else
   missmsg='-'
fi
# only look at --with-python specification if set
if [ -n "$pythonbin" ]; then
   check_requirement $pythonbin "$missmsg" "PATH=$binsearchpath"
else
   check_requirement $PYTHON "$missmsg" "PATH=$binsearchpath" 'python3' 'python2'
fi
# define a generic launching name if command not found at build time
if [ -z "$reqpath" ]; then
   # use --with-python specification if set
   if [ "${pythonbin:0:1}" = '/' ]; then
      PYTHON="$pythonbin"
   elif [ -n "$pythonbin" ]; then
      PYTHON="/usr/bin/env $pythonbin"
   else
      PYTHON="/usr/bin/env $PYTHON"
   fi
else
   PYTHON=$reqpath
fi

# get location of commands used 'fullpath' in init scripts
check_requirement 'ps' '' "PATH=$binsearchpath"
PS=$reqpath
check_requirement 'basename' '' "PATH=$binsearchpath"
BASENAME=$reqpath

# get pager program location from standard PATHs or /usr/local/bin
# or validate location passed as argument
if [ -n "$pager" ]; then
   check_requirement $pager '' "PATH=$binsearchpath"
   pager=$reqpath
fi
# adapt pager program settings dependings of specified args
if [ $defpageropts -eq 1 -a "${pager##*/}" != 'less' ]; then
    pageropts=''
    echo_warning "As chosen pager is not \`less', default pager options are cleared"
fi

# fetch modules version from version.inc.in file (which may be raw data to
# refine or exact values if dist has been built by `git archive`)
if [ -r ${progdir}/version.inc.in ]; then
  release=$(sed -n '/^MODULES_RELEASE/{s/.*= //p;q;}' ${progdir}/version.inc.in)
  build_hash=$(sed -n '/^MODULES_BUILD_HASH/{s/.*= //p;q;}' \
     ${progdir}/version.inc.in)
  build_refs=$(sed -n '/^MODULES_BUILD_REFS/{s/.*= //p;q;}' \
     ${progdir}/version.inc.in)
  build_refs=${build_refs//,}
  build_refs=${build_refs//origin\/}
else
   echo_error "${progdir}/version.inc.in is missing"
fi

# refine build number if working from git repository
if [ -e '.git' ]; then
  gitcurtag=$(git describe --tags --abbrev=0)
  gitcurdesc=$(git describe --tags)
  gitcurbranch=$(git rev-parse --abbrev-ref HEAD)
  if [ "$gitcurtag" = "$gitcurdesc" ]; then
     build=''
  elif [ "$gitcurbranch" = 'master' ]; then
     build="+${gitcurdesc#${gitcurtag}-}"
  else
     build="+${gitcurbranch}${gitcurdesc#${gitcurtag}}"
  fi
# set a recognizable build number if one found in version.inc.in is raw data
elif [ "$build_hash" = '$Format:%h' ]; then
   build='+XX-gffffffff'
# or compute it from these information as if working from git repository
elif [[ " $build_refs " =~ " v$release " ]]; then
   build=''
elif [[ " $build_refs " =~ " master " ]]; then
   build="+XX-g$build_hash"
# %D placeholder may not be known by old version of git
elif [ "$build_refs" = "%D" ]; then
   build="+XX-g$build_hash"
else
   build="+${build_refs##* }-XX-g$build_hash"
fi
VERSION=$release$build
RELEASE=$release

if [ -z "$VERSION" ]; then
   echo_error "Cannot fetch modules version"
fi

# refer to binary exes and libs to build with the appropriate extension
case "$kernelname" in
   CYGWIN*) EXEEXT='.exe'; SHLIB_SUFFIX='.dll' ;;
   MINGW*|MSYS_NT*) SHLIB_SUFFIX='.dll' ;;
   Darwin) SHLIB_SUFFIX='.dylib' ;;
esac

# adapt prefix if versioning enabled
baseprefix=$prefix
if [ "$versioning" = 'y' ]; then
   # append modules version to installation prefix
   prefix=$prefix/$VERSION
fi

# set argument default values for those depending of other argument
[ -z "$bindir" ] && bindir=$prefix/bin
[ -z "$libdir" ] && libdir=$prefix/lib
[ -z "$libexecdir" ] && libexecdir=$prefix/libexec
[ -z "$etcdir" ] && etcdir=$prefix/etc
[ -z "$initdir" ] && initdir=$prefix/init
[ -z "$datarootdir" ] && datarootdir=$prefix/share
[ -z "$mandir" ] && mandir=$datarootdir/man
[ -z "$docdir" ] && docdir=$datarootdir/doc
[ -z "$vimdatadir" ] && vimdatadir=$datarootdir/vim/vimfiles
[ -z "$modulefilesdir" ] && modulefilesdir=$prefix/modulefiles
[ -z "$moduleshome" ] && moduleshome=$prefix
# default modulepath based on MODULE_VERSION if versioning enabled
[ -z "$modulepath" -a "$versioning" = 'y' ] && modulepath=${modulefilesdir/#$prefix/$baseprefix\/\$MODULE_VERSION}
[ -z "$modulepath" ] && modulepath=$modulefilesdir

# define libdir64 and libdir32 based on libdir if multilib support enabled
if [ "$multilibsupport" = 'y' ]; then
   case "$libdir" in
      *64)
         libdir64=$libdir ;
         libdir32=${libdir%64} ;;
      *)
         libdir64=${libdir}64 ;
         libdir32=$libdir ;;
   esac
fi

# check feature requirements are met
if [ "$setdotmodulespath" = 'y' -a -n "$loadedmodules" ]; then
   featmesg="As \`setdotmodulespath' is enabled beware that \`loadedmodules' may be ignored by init scripts"
   echo_warning "$featmesg"
fi
if [ -n "$loadedmodules" -a -z "$modulepath" ]; then
   featmesg="A value is set for \`loadedmodules' whereas \`modulepath' is not defined"
   echo_warning "$featmesg"
fi

# prepare extension library sources if not yet done whether libtclenvmodules
# is enabled or not to create consistent dist in any cases.
if [ ! -e 'lib/configure' ]; then
   check_requirement autoreconf
   checkedautoreconf=0
   echo "--- preparing extension library sources ----------"
   pushd lib
   autoreconf -i || exit 1
   popd
   echo "--------------------------------------------------"
fi

# check compat version sources
if [ "$compatversion" = 'y' ]; then
   if [ ! -d 'compat' ]; then
      # extract sources from a git branch and prepare sources
      if [ -e '.git' ]; then
         if [ $checkedautoreconf -eq 1 ]; then
            check_requirement autoreconf
         fi
         # specific location to find autopoint on Darwin
         if [ "$kernelname" = 'Darwin' ]; then
            check_requirement autopoint '' "PATH=$binsearchpath:/usr/local/opt/gettext/bin"
         else
            check_requirement autopoint
         fi
         check_requirement automake
         check_requirement aclocal

         echo "--- preparing compatibility version sources ------"
         repofull=1
         # retrieve compat version branch if not found locally
         git rev-parse --verify $compatbranch >/dev/null 2>/dev/null
         if [ $? -ne 0 ]; then
            curbranch=$(git rev-parse --abbrev-ref HEAD)
            curremote=$(git remote show | head -n 1)
            curnbcomm=$(git rev-list $curbranch | wc -l)
            if [ $curnbcomm -lt 350 ]; then
               repofull=0
            else
               git fetch $curremote $compatbranch || exit 1
               git branch $compatbranch $curremote/$compatbranch || exit 1
            fi
         fi

         # repository is truncated, need to standalone clone compat branch
         if [ $repofull -eq 0 ]; then
            curremoteurl=$(git config --get remote.$curremote.url)
            git clone --branch=$compatbranch $curremoteurl compat || exit 1
         else
            # install compat git branch in a directory
            git worktree list >/dev/null 2>/dev/null
            if [ $? -eq 0 ]; then
               git worktree add compat $compatbranch || exit 1
            else
               gitworktree=n
               # clone branch if no worktree command supported
               gitdir=$(git rev-parse --git-dir)
               git clone --branch=$compatbranch $gitdir compat || exit 1
            fi
         fi

         pushd compat
         autoreconf -f -i || exit 1
         popd
         echo "--------------------------------------------------"
      else
         echo_error "Cannot build compatibility version, \`compat' directory cannot be found"
      fi
   fi
fi

# display configuration set
# and build Makefile translation expression
translation_regexp=()
for arg in ${arglist}; do
   echo "$arg = ${!arg}"
   translation_regexp+=('-e' "s|@${arg}@|${!arg}|g")
done

# configure extension library sources
if [ "$libtclenvmodules" = 'y' ]; then
   echo "--- configuring extension library sources --------"
   echo "libarglist = $libarglist"
   pushd lib
   ./configure $libarglist || exit 1
   popd
   echo "--------------------------------------------------"
fi

# configure compat version sources
if [ "$compatversion" = 'y' ]; then
   echo "--- configuring compatibility version sources ----"
   echo "compatarglist = $compatarglist"
   pushd compat
   # unknown arguments for this configure script are passed down to the
   # compatibility version configure script (which raise error if arg unknown)
   # also pass arguments relative to installation paths to get compatibility
   # man pages set with them
   # keep versioning disabled for compat version to get expected paths defined
   ./configure --prefix=$prefix --bindir=$bindir --libdir=$libdir \
      --libexecdir=$libexecdir --datarootdir=$datarootdir --mandir=$mandir \
      --docdir=$docdir --with-module-path=$modulepath --with-python="$PYTHON" \
      --disable-versioning $compatarglist || exit 1
   popd
   echo "--------------------------------------------------"
fi

# generate Makefiles
for target in ${targetlist}; do
   # escape any '$' in value for Makefile or testsuite interpretation
   if [ "${target/Makefile}" = "${target}" ]; then
      extra_regexp=()
   else
      extra_regexp=('-e' 's|\$|\$\$|g')
   fi
   echo "creating $target"
   sed "${translation_regexp[@]}" "${extra_regexp[@]}" ${target}.in >$target
done

exit 0

# vim:set tabstop=3 shiftwidth=3 expandtab autoindent:
