#!/bin/sh

set -e

error() {
  echo >&2 "$0: error: $@"
}

fatal_error() {
  error "$@"
  exit 1
}

is_set() {
  ( eval [ "\"\${$1+set}\"" = "set" ] )
}

show_usage() {
cat <<EOT
Usage: $0 [OPTION]... [VAR=VALUE]...

This script creates necessary configuration files to build/install.

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.

Main options:
  -h, --help             display this help and exit
  --prefix=[path]        base path [/usr/local]
  --bindir=DIR           user executables [PREFIX/bin]
  --sbindir=DIR          system admin executables [PREFIX/sbin]
  --libdir=DIR           object code libraries [PREFIX/lib]
  --scriptdir=DIR        script type plugins [LIBDIR/vlock/scripts]
  --moduledir=DIR        module type plugins [LIBDIR/vlock/modules]
  --mandir=DIR           man documentation [PREFIX/share/man]

Optional Features:
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-plugins        enable plugin support [enabled]
  --enable-pam            enable PAM authentication [enabled]
  --enable-shadow         enable shadow authentication [disabled]
  --enable-root-password  enable unlogging with root password [enabled]
  --enable-debug          enable debugging

Additional configuration:
  --with-scripts=SCRIPTS  enable the named scripts []
  --with-modules=MODULES  enable the named modules [<architecture depedent>]

Some influential environment variables:
  CC            C compiler command
  CFLAGS        C compiler flags
  EXTRA_CFLAGS  additional C compiler flags (extends default)
  LDFLAGS       linker flags, e.g. -L<lib dir> if you have libraries in a
                nonstandard directory <lib dir>
  EXTRA_LDFLAGS additional linker flags (extends default)
  VLOCK_GROUP   group for restricted modules (default: vlock)
  VLOCK_MODE    mode for restricted modules (default: 0750)

Use these variables to override the choices made by \`configure' or to help
it to find libraries and programs with nonstandard names/locations.

Report bugs to <frank-vlock@benkstein.net>.
EOT
}

set_variable() {
  eval "$1"='"$2"'
}

enable_feature() {
  case "$1" in
    plugins)
      ENABLE_PLUGINS="$2"
    ;;
    root-password)
      ENABLE_ROOT_PASSWORD="$2"
    ;;
    pam|shadow)
      if [ "$2" = "yes" ] ; then
        if [ -n "$auth_method" ] && [ "$auth_method" != "$1" ] ; then
          fatal_error "pam and shadow authentication are mutually exclusive"
        fi
        AUTH_METHOD="$1"
      else
        fatal_error "cannot disable authentication"
      fi
    ;;
    debug)
      if [ "$2" = "yes" ] ; then
        CFLAGS="${DEBUG_CFLAGS}"
      else
        CFLAGS="${DEFAULT_CFLAGS}"
      fi
    ;;
    *)
      fatal_error "invalid feature name: $1"
    ;;
  esac
}

parse_arguments() {
  local feature opt optarg

  while [ $# -gt 0 ] ; do
    if ! opt=`expr "x$1" : 'x\([^=]*\)=.*'` ; then
      opt="$1"
    fi

    if ! optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ; then
      optarg=""
    fi

    case "$1" in
      --disable-*)
        feature=`expr "x$1" : 'x--disable-\(.*\)'`
        enable_feature "$feature" no
        shift
      ;;
      --enable-*=no)
        feature=`expr "x$1" : 'x--enable-\(.*\)=no'`
        enable_feature "$feature" no
        shift
      ;;
      --enable-*=yes)
        feature=`expr "x$1" : 'x--enable-\(.*\)=yes'`
        enable_feature "$feature" yes
        shift
      ;;
      --enable-*)
        feature=`expr "x$1" : 'x--enable-\(.*\)'`
        enable_feature "$feature" yes
        shift
      ;;
      *=*)
        shift
        # unshift
        set -- "$opt" "$optarg" "$@"
      ;;
      --prefix)
        PREFIX="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --bindir)
        BINDIR="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --sbindir)
        SBINDIR="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --libdir)
        LIBDIR="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --moduledir)
        MODULEDIR="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --scriptdir)
        SCRIPTDIR="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --mandir)
        MANDIR="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --with-modules)
        MODULES="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      --with-scripts)
        SCRIPTS="$2"
        shift 2 || fatal_error "$1 argument missing"
      ;;
      EXTRA_CFLAGS)
        CFLAGS="${CFLAGS} $2"
        shift 2 || fatal_error "$1 value missing"
      ;;
      EXTRA_LDFLAGS)
        LDFLAGS="${LDFLAGS} $2"
        shift 2 || fatal_error "$1 value missing"
      ;;
      [A-Z]*)
        set_variable "$1" "$2"
        shift 2 || fatal_error "$1 value missing"
      ;;
      --quiet)
        verbose=0
        shift
      ;;
      --help|-h)
        show_usage
        exit
      ;;
      -*)
        error "unrecognized option: $1"
        echo >&2 "Try \`$0 --help' for more information."
        exit 1
      ;;
      *)
        error "invalid argument: $1"
        echo >&2 "Try \`$0 --help' for more information."
        exit 1
      ;;
    esac
  done
}

set_defaults() {
  # architecture independent defaults
  PREFIX="/usr/local"
  BINDIR="\$(PREFIX)/bin"
  SBINDIR="\$(PREFIX)/sbin"
  LIBDIR="\$(PREFIX)/lib"
  MANDIR="\$(PREFIX)/share/man"
  SCRIPTDIR="\$(LIBDIR)/vlock/scripts"
  MODULEDIR="\$(LIBDIR)/vlock/modules"

  CC=gcc
  DEFAULT_CFLAGS="-O2 -Wall -W -pedantic -std=gnu99"
  DEBUG_CFLAGS="-O0 -g -Wall -W -pedantic -std=gnu99"
  CFLAGS="${DEFAULT_CFLAGS}"
  LD=ld
  LDFLAGS=""
  AUTH_METHOD="pam"
  ENABLE_ROOT_PASSWORD="yes"
  ENABLE_PLUGINS="yes"
  SCRIPTS=""

  VLOCK_GROUP="vlock"
  VLOCK_MODULE_MODE="0750"

  BOURNE_SHELL="/bin/sh"

  # architecture dependent defaults
  OS=`uname`

  for make in make gmake ; do
    if $make -f /dev/null -q -v 2>/dev/null | head -n 1 | grep -q "GNU Make" ; then
      MAKE="$make"
      break
    fi
  done

  ROOT_GROUP=`getent group | awk -F: '$3 == 0 { print $1 ; exit }'`

  case "$OS" in
    Linux)
      PAM_LIBS='-ldl -lpam'
      DL_LIB='-ldl'
      CRYPT_LIB='-lcrypt'
      MODULES="all.so new.so nosysrq.so"
    ;;
    GNU/kFreeBSD)
      PAM_LIBS='-ldl -lpam'
      DL_LIB='-ldl'
      CRYPT_LIB='-lcrypt'
      MODULES="all.so new.so"
    ;;
    FreeBSD)
      PAM_LIBS='-lpam'
      DL_LIB=''
      CRYPT_LIB=''
      MODULES="all.so new.so"
    ;;
  esac
}

parse_config_mk() {
  local tmpdir

  if [ -z "$MAKE" ] ; then
    error "GNU make not found"
    echo >&2 "Set MAKE environment variable to specify alternative."
    exit 1
  fi

  tmpdir=`mktemp -d -t vlock-configure.XXXXXX`

  $MAKE -rR -f config.mk -p -q . 2>/dev/null | awk > "$tmpdir/config.mk" '
    /^# makefile/ { p=1; next }
    /^#/ { p=0; next }
    p==1 && $1 != "MAKEFILE_LIST" && /^[A-Za-z_]+ :?= .*/ { print }
  '

  while read line
  do
    variable_name=`expr "x${line}" : 'x\([[[:alpha:]_]\{1,\}\) :\{0,1\}='`
    if variable_value=`expr "x${line}" : 'x[[:alpha:]_]\{1,\} :\{0,1\}= \(.*\)'` ; then
      set_variable "$variable_name" "$variable_value"
    else
      set_variable "$variable_name" ""
    fi
  done < "$tmpdir/config.mk"

  rm -rf "$tmpdir"
}

show_summary() {
  cat <<EOF
vlock configuration

directories:
  prefix:     $PREFIX
  bindir:     $BINDIR
  sbindir:    $SBINDIR
  libdir:     $LIBDIR
  mandir:     $MANDIR
  scriptdir:  $SCRIPTDIR
  moduledir:  $MODULEDIR

features:
  enable plugins: $ENABLE_PLUGINS
  root-password:  $ENABLE_ROOT_PASSWORD
  auth-method:    $AUTH_METHOD
  modules:        $MODULES
  scripts:        $SCRIPTS

build configuration:

  operating system: $OS
  gnu make:         $MAKE
  c compiler:       $CC
  compiler flags:   $CFLAGS
  linker flags      $LDFLAGS
  pam libs:         $PAM_LIBS
  dl libs:          $DL_LIB
  crypt lib:        $CRYPT_LIB

installation configuration:
  root group:       $ROOT_GROUP
  vlock group:      $VLOCK_GROUP
EOF
}

create_config_mk() {
  cat > config.mk <<EOF
# automatically generated by $0 on $(date)

### configuration options ###

# authentification method (pam or shadow)
AUTH_METHOD = ${AUTH_METHOD}
# also prompt for the root password in adition to the user's
ENABLE_ROOT_PASSWORD = ${ENABLE_ROOT_PASSWORD}
# enable plugins for vlock-main
ENABLE_PLUGINS = ${ENABLE_PLUGINS}
# which plugins should be build
MODULES = ${MODULES}
# which scripts should be installed
SCRIPTS = ${SCRIPTS}

# root's group
ROOT_GROUP = ${ROOT_GROUP}

# group for privileged plugins
VLOCK_GROUP = ${VLOCK_GROUP}
# mode for privileged plugins
VLOCK_MODULE_MODE = ${VLOCK_MODULE_MODE}

### paths ###

# installation prefix
PREFIX = ${PREFIX}
BINDIR = ${BINDIR}
SBINDIR = ${SBINDIR}
LIBDIR = ${LIBDIR}
MANDIR = ${MANDIR}
# installation root
DESTDIR =
# path where modules will be located
MODULEDIR = ${MODULEDIR}
# path where scripts will be located
SCRIPTDIR = ${SCRIPTDIR}

### programs ###

# shell to run vlock.sh with (only bash is known to work)
BOURNE_SHELL = ${BOURNE_SHELL}
# C compiler
CC = ${CC}
# linker
LD = ${LD}
# mkdir
MKDIR_P = mkdir -p
# install
INSTALL = install

### compiler and linker settings ###

# C compiler flags
CFLAGS = ${CFLAGS}
# linker flags
LDFLAGS = ${LDFLAGS}
# linker flags needed for dlopen and friends
DL_LIB = ${DL_LIB}
# linker flags needed for crypt
CRYPT_LIB = ${CRYPT_LIB}
# linker flags needed for pam
PAM_LIBS = ${PAM_LIBS}
EOF
}

main() {
  verbose=1

  set_defaults
  parse_config_mk
  parse_arguments "$@"
  
  if [ "$verbose" -ge 1 ] ; then
    show_summary
  fi
  
  create_config_mk
}

main "$@"
