#!/bin/sh
#***************************************************************************
#
# $Source: /sc/CvsTree/sc/gen/src/prg/scripts/service-lib.sh,v $
#
# Copyright (c) 2014
# BRUKER BIOSPIN GMBH
# D-76287 Rheinstetten, Germany
#
# All Rights Reserved
#
# $Id: service-lib.sh,v 1.20 2017/10/24 17:00:59 jgo Exp $
#
#***************************************************************************
#			Description:
#
# Shell library for handling Linux services.
# Can be run from TopSpin installer, uninstaller, or from command line.
# POSIX compliant (test with POSIXLY_CORRECT=1 or --posix or set -o posix)
#***************************************************************************
# Used by:
#	sc/gen/src/prg/blade/post/installnetwork
#	sc/gen/src/prg/blade/post/setfirewall.sh
#	sc/gen/src/prg/CryoControlServer/crco-install
#	sc/gen/src/prg/IPSO_OS/install-services
#	sc/gen/src/prg/hostacq/setfirewall.sh
#	sc/gen/src/prg/hostacq/unittest.sh
#	sc/gen/src/prg/scripts/firewall-codemeter.sh
#	sc/gen/src/prg/scripts/firewall-mics.sh
#	sc/gen/src/prg/scripts/firewall-lib.sh
#	pr/gen/src/prg/xwinstall/xwinstall.d/modscripts/flexlm.post
#	pr/gen/src/prg/install.net/libinst/install.net


Usage ()
{
    $ECHO >&2 "$(MainUsage)"
    ExitHandler 2
}


SvcLibMain ()
{
    SvcLibSetDefaults
    SvcLibScanArguments "$@"
    SvcLibRunActions
    : $FUNCNAME calling standard termination
    ExitHandler $NExitValue
}


SvcLibSetDefaults ()
{
    trap "ExitHandler 99" 0
    exec 3>&1

    OLDPATH=$PATH
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:$PATH
    BSH=${BSH:-/bin/sh}
    export BSH
    BSHOPT=${BSHOPT:-$(GetBshOpt)}
    BrukerRpmInstallCmd="rpm -i --nosignature"
    export LC_ALL=C	# traditional sort order
    NExitValue=0
    Sysconfig=/etc/sysconfig
    SysFsDir=/sys
    EtcDir=/etc
    OptRemove=0
    OptDryRun=0
    OptTest=0
    OptHasForce=0
    OptForce=0
    OptHasTest=0
    OptTest=0

    SetEcho
    FindInstDir
    FindUnInstDir
    FindOsDistro
    FindOsVersion
    FindRpm
    FindEtcInitD
    FindServiceControl
    SetQueryYumInstall
    SetSwimEnvironment
    SetDefaults
}


SetDefaults ()
{
    :
}


SvcLibScanArguments ()
{
    while getopts "fintuh" szOption
    do
	case "$szOption" in
	    f)
		test ${OptHasForce:-0} || Fatal "option $szOption not implemented"
		OptForce=1
		;;
	    i)
		OptRemove=0
		;;
	    n)
		OptDryRun=1
		;;
	    t)
		test ${OptHasTest:-0} || Fatal "option $szOption not implemented"
		OptTest=1
		EtcDir=$SzInstDir/etc
		SysFsDir=$SzInstDir/sys
		Sysconfig=$EtcDir/sysconfig
		;;
	    u)
		OptRemove=1
		;;
	    h)
		Usage
		;;
	esac
    done

    ScanArguments "$@"
    AssertIdIsRoot
}


AssertIdIsRoot ()
{
    test $OptTest = 1 ||
    test $(id -u) = 0 ||
    Fatal "You must be super-user (root) to run $0"
}


ScanArguments ()
{
    shift $(expr $OPTIND - 1)
    test $# = 0 || Fatal "Invalid option ($1)"
}


SvcLibRunActions ()
{
    Output "$0: $(GetActionMsg) service$(Plural $MainServices): $MainServices$(GetDryRunMsg)"

    if [ $OptRemove = 0 ]
    then
	RunInstall
    else
	RunRemove
    fi
}


RunInstall ()
{
    local service
    for service in $MainServices
    do
	RunInstallService $service
    done
}


RunRemove ()
{
    local service
    for service in $MainServices
    do
	RunRemoveService $service
    done
}


RunRemoveService ()
{
    local service=$1 && shift
    ServiceControl $service stop
    SvcLibRestoreBackupFiles $service
}


SetEcho ()
{
    if test "$(echo -e)" = -e
    then
	ECHO=echo
    else
	ECHO="echo -e"
    fi
}


FindInstDir ()
{
    if test -d "${SzInstDir:-}"
    then
	return
    fi

    SzInstDir=$(cd $(dirname $0) >/dev/null && pwd)

    local p
    for p in .. ../.. ../../.. ../../../..
    do
	if test -d $SzInstDir/$p/uninstall
	then
	    SzInstDir=$(cd $SzInstDir/$p >/dev/null && pwd)
	    return
	fi
    done

    SzInstDir=/tmp
}


FindUnInstDir ()
{
    if test -d "${SzUnInstDir:-}"
    then
	return
    fi
    SzUnInstDir=$SzInstDir/uninstall
}


FindOsDistro ()
{
    if test -f /etc/redhat-release
    then
	LinuxDistro=redhat
    elif grep -i redhat /etc/os-release
    then
	LinuxDistro=redhat
    elif grep -i redhat /etc/os-release
    then
	LinuxDistro=redhat
    else
	LinuxDistro=other
    fi

    export LinuxDistro
}


FindOsVersion ()
{
    if test $LinuxDistro != redhat
    then
	OsVersion=0
	return
    fi

    local OsReleaseString="$(rpm -qf --queryformat %{version} /etc/redhat-release)"

    # Convert release string (e.g. '5' or '5Server') to number.
    OsVersion=`expr substr "$OsReleaseString" 1 1`

    if ! IsNumber "$OsVersion"
    then
	Warning "$OsVersion is not a number"
	OsVersion=0
    fi
}


FindRpm ()
{
    if which rpm >/dev/null 2>/dev/null
    then
	BHasRPM=1
	Output "Note: Using 'rpm' for package investigation."
    else
	BHasRPM=0
    fi
}


FindEtcInitD ()
{
    if [ $BHasRPM = 1 ]
    then
	# Get location of init.d directory.
	local szRPMInitDir=`rpm -ql initscripts | grep '^/etc.*/init.d$'`
    fi

    # Try the output of 'rpm' first, then other possible defaults.
    for szDir in "$szRPMInitDir" /etc/rc.d/init.d /etc/init.d
    do
	if [ -d "$szDir" ]
	then
	    EtcInitD="$szDir"
	    Output "Note: Using init directory $EtcInitD"
	    return
	fi
    done

    Warning "directory init.d not found"
}


# @purpose:	Find network interface and config file to use for spectrometer connection.
# @global	BRUKER_SPECT_NETIF	network interface name to use for commands
#					if unset: set to eth1
# @global	BRUKER_SPECT_NETCFG	network config file to read/write (full path)
#					if unset: find file per name, DEVICE=, NAME=, HWADDR=
FindNetIf ()
{
    if FindNetIfSub
    then
	Output "Note: Using network interface $BRUKER_SPECT_NETIF"
	Output "Note: Using network config $BRUKER_SPECT_NETCFG"
    elif test $OptForce = 1 -o $OptTest = 1
    then
	Output "Note: Using network interface $BRUKER_SPECT_NETIF (not existent)"
    elif test $OptDryRun = 1
    then
	Warning "network interface $BRUKER_SPECT_NETIF not found"
    elif test "$BRUKER_SPECT_NETIF" = "eth1"
    then
	Warning "network interface $BRUKER_SPECT_NETIF not found"
    else
	Warning "network interface $BRUKER_SPECT_NETIF not found"
    fi
}


FindNetIfSub ()
{
    local file hwaddr

    # Environment variables are given priority.
    BrukerSpectNetifSet=1

    if test -n "${BRUKER_SPECT_NETCFG:-}"
    then
	if test -z "${BRUKER_SPECT_NETIF:-}"
	then
	    BRUKER_SPECT_NETIF=$(echo "$BRUKER_SPECT_NETCFG" | sed -e 's|.*/||' -e 's|^ifcfg-||')
	fi
	return
    fi

    if test -n "${BRUKER_SPECT_NETIF:-}"
    then
	BRUKER_SPECT_NETCFG=$(GetNetifCfg $BRUKER_SPECT_NETIF)
	return
    fi

    # Next comes the previously saved name.
    if test -e "$SzUnInstDir"/netifname.txt
    then
	BRUKER_SPECT_NETIF=$(cat "$SzUnInstDir"/netifname.txt)
	BRUKER_SPECT_NETCFG=$(GetNetifCfg $BRUKER_SPECT_NETIF)
	return
    fi

    # Next try the default.
    BrukerSpectNetifSet=0
    BRUKER_SPECT_NETIF=eth1

    file=$(GetNetifCfg $BRUKER_SPECT_NETIF)
    if test -e "$file"
    then
	BRUKER_SPECT_NETCFG=$file
	return
    fi

    # Next scan some system files.
    file=$(get_config_by_device $BRUKER_SPECT_NETIF)
    if test "$file"
    then
	BRUKER_SPECT_NETCFG=$file
	return
    fi

    file=$(get_config_by_name $BRUKER_SPECT_NETIF)
    if test "$file"
    then
	BRUKER_SPECT_NETCFG=$file
	return
    fi

    hwaddr=$(get_hwaddr $BRUKER_SPECT_NETIF)
    if test "$hwaddr"
    then
	file=$(get_config_by_hwaddr $hwaddr)
	if test "$file"
	then
	    BRUKER_SPECT_NETCFG=$file
	    return
	fi
    fi

    BRUKER_SPECT_NETCFG=$(GetNetifCfg $BRUKER_SPECT_NETIF)
    return 1
}


get_config_by_device ()
{
    LANG=C grep -l "^[[:space:]]*DEVICE=\"\?${1}\"\?\([[:space:]#]\|$\)" $(get_ifcfg_files) 2>/dev/null
}


get_config_by_name ()
{
    LANG=C grep -E -il "^[[:space:]]*NAME=\"(Auto |System )?${1}\"" $(get_ifcfg_files) 2>/dev/null
}


get_config_by_hwaddr ()
{
    LANG=C grep -il "^[[:space:]]*HWADDR=\"\?${1}\"\?\([[:space:]#]\|$\)" $(get_ifcfg_files) 2>/dev/null
}


get_hwaddr ()
{
    if test -f $SysFsDir/class/net/${1}/address
    then
	awk '{ print toupper($0) }' < $SysFsDir/class/net/${1}/address
    elif test -d "$SysFsDir/class/net/${1}"
    then
	LC_ALL= LANG= ip -o link show ${1} 2>/dev/null | \
      	    awk '{ print toupper(gensub(/.*link\/[^ ]* ([[:alnum:]:]*).*/,"\\1", 1)); }'
    fi
}


get_ifcfg_files ()
{
    local file
    local found=0

    for file in $Sysconfig/network-scripts/ifcfg-*
    do
	case "$file" in
	    *.bak|*~|*.orig|*.rpmnew|*.rpmsave)
		continue
		;;
	    *)
		if test -e "$file"
		then
		    echo "$file"
		    found=1
		fi
	esac
    done

    if test $found = 0
    then
	echo /dev/null
    fi
}


GetNetifCfg()
{
    echo $Sysconfig/network-scripts/ifcfg-${1:-}
}


SetNetIFOptionsFile()
{
    BRUKER_SPECT_NETCFG=$1
}


FindServiceControl ()
{
    if which systemctl >/dev/null 2>/dev/null
    then
	ServiceControlCmd=Systemd
    elif which service >/dev/null 2>/dev/null
    then
	ServiceControlCmd=SystemV
    else
	ServiceControlCmd=Initd
    fi
    Output "Note: Using $ServiceControlCmd for service control"
}


#---------------------------------------------------------------------------
# SystemV service install involves
#	- copy of script to /etc/init.d (SvcConf install)
#	- granting read/execute permissions on the script (SvcConf install/fixperm)
#	- linking of script to /etc/rcN.d via chkconfig --add (SvcCtrl add)
#	-> call SvcCtrl install
# Systemd service install involves
#	- copy of description to /etc/systemd/system (SvcConf install)
#	-> call SvcCtrl install
#---------------------------------------------------------------------------


# param	name: service name as used by systemctl/service
# param cmd:  enable/disable/start/stop/status/
#             is-existing/is-enabled/is-running/
#             reload-conf/restart/install/remove/add/del
ServiceControl ()
{
    local name="$1" && shift
    local cmd=$1 && shift

    case $cmd in
	restart)
	    ServiceControl "$name" reload-conf
	    ServiceControl "$name" stop
	    ServiceControl "$name" start
	    ServiceControl "$name" status
	    ;;
	stop)
	    if XinetdControlsService $name; then return; fi
	    if ServiceControl "$name" is-running; then
		ServiceControl_$ServiceControlCmd "$name" $cmd
	    fi
	    ;;
	install)
	    ServiceConf $name $cmd
	    ServiceConf $name fixperm
	    ServiceControl $name reload-conf
	    ServiceControl $name add
	    ;;
	remove)
	    ServiceControl $name del
	    ServiceConf $name $cmd
	    ;;
	*)
	    ServiceControl_$ServiceControlCmd "$name" $cmd
    esac
}


ServiceControl_Systemd ()
{
    local name=$1 && shift
    local cmd=$1 && shift

    case $name in
	nfs) name=nfs-server;;
    esac

    if XinetdControlsService "$name"
    then
	case $cmd in
	    enable)
		XinetdEnableService $name
		;;
	esac
	name=xinetd
    fi

    case $cmd in
	reload-conf)
	    $FUNCNAME $name daemon-reload
	    ;;
	is-existing)
	    systemctl list-unit-files | grep "^$name.service" >/dev/null
	    ;;
	is-running)
	    systemctl is-active "$name" >/dev/null
	    ;;
	daemon-reload)
	    # Suppress printing of 'rm' and 'ln' lines to stderr
	    VerboseOk systemctl -q $cmd 2>&1
	    ;;
	enable)
	    systemctl is-enabled $name.service >/dev/null ||
	    VerboseOk systemctl $cmd $name.service
	    ;;
	disable)
	    systemctl is-enabled $name.service >/dev/null ||
	    VerboseOk systemctl $cmd $name.service
	    ;;
	add)
	    $FUNCNAME $name enable
	    ;;
	del)
	    $FUNCNAME $name disable
	    ;;
	*)
	    VerboseOk systemctl $cmd $name.service
    esac
}


ServiceControl_SystemV ()
{
    local name=$1 && shift
    local cmd=$1 && shift
    case $cmd in
	reload-conf)	:;;
	is-existing)	ServiceControl_Initd $name $cmd;;
	is-enabled)	chkconfig --list $name | grep "$name.*on";;
	is-running)	ServiceControl_Initd $name $cmd;;
	enable)		VerboseOk chkconfig --level 2345 $name on;;
	disable)	VerboseOk chkconfig --level 2345 $name off;;
	add)		VerboseOk chkconfig --add $name;;
	del)		VerboseOk chkconfig --del $name;;
	*)		VerboseOk service $name $cmd
    esac
}


ServiceControl_Initd ()
{
    local name=$1 && shift
    local cmd=$1 && shift
    case $cmd in
	reload-conf)	:;;
	is-existing)	test -e $EtcInitD/$name;;
	is-enabled)	test -e /etc/rc3.d/S*$name;;
	is-running)	test "$(pidof -x -o %PPID $name)";;
	enable)		VerboseOk chkconfig --level 2345 $name on;;
	disable)	VerboseOk chkconfig --level 2345 $name off;;
	add)		VerboseOk chkconfig --add $name;;
	del)		VerboseOk chkconfig --del $name;;
	*)		ShellCmd $EtcInitD/$name $cmd
    esac
}


# param file	save for use in 'ServiceConf * install' on $ServiceControlCmd
ServiceConfSetSrcFile ()
{
    ServiceConfSrcFile=$1
}


# param file1	save for use in 'ServiceConf * install' on Systemd
# param file2	save for use in 'ServiceConf * install' on SystemV/Initd
ServiceConfSetSrcFiles ()
{
    if test $ServiceControlCmd = Systemd
    then
	ServiceConfSrcFile=$1
    else
	ServiceConfSrcFile=$2
    fi
}


# Called as:
# param	name: service name as used by systemctl/service
# param cmd:  remove: remove SystemV service start script
#             install: install SystemV service start script
#             fixperm: set std perms on SystemV service start script
#             getfile: echo service start script/service conf file
#             getcmd: echo cmd to start/stop service
ServiceConf ()
{
    local name=$1 && shift
    local cmd=$1 && shift
    case $cmd in
	remove|install|fixperm|getfile|getcmd)
	    ServiceConf_$ServiceControlCmd "$name" $cmd "$@"
	    ;;
	*)
	    Fatal "$FUNCNAME $name $cmd $@: unknown cmd $cmd"
    esac
}


ServiceConf_Systemd ()
{
    local name=$1 && shift
    local cmd=$1 && shift
    local file=/etc/systemd/system/$name.service

    case $cmd in
	fixperm) :;;
	getfile) echo $file;;
	getcmd)  echo "systemctl [start|stop] $name";;
	*)       ServiceConfSub_Common $name $cmd $file "$@"
    esac
}


ServiceConf_SystemV ()
{
    ServiceConf_Initd "$@"
}


ServiceConf_Initd ()
{
    local name=$1 && shift
    local cmd=$1 && shift
    local file=$EtcInitD/$name
    ServiceConfSub_Common $name $cmd $file "$@"
}


ServiceConfSub_Common ()
{
    local name=$1 && shift
    local cmd=$1 && shift
    local file=$1 && shift

    case $cmd in
	install)
	    InstallFileFromSrc $ServiceConfSrcFile $file "$@"
	    ;;
	remove)
	    rm -f $file
	    ;;
	fixperm)
	    (umask=0; chmod 0755 $file)
	    ;;
	getfile)
	    echo $file
	    ;;
	getcmd)
	    echo "sh /etc/init.d/$name [start|stop]"
	    ;;
    esac
}


XinetdControlsService ()
{
    local name=$1 && shift
    case $name in
	tftp) return 0;;
	*) return 1;;
    esac
}


XinetdEnableService ()
{
    XinetdDone=0
    local file
    for file in /etc/xinetd.d/$name /etc/xinetd.conf
    do
	if test -e $file
	then
	    XinetdEnableServiceInConfFile $file $name
	    if test $XinetdDone = 1; then return; fi
	fi
    done
    ErrorMsg "Could not enable service $name: not in xinetd.conf"
}


XinetdEnableServiceInConfFile ()
{
    local file=$1 && shift
    local name=$1 && shift

    if XinetdHasService $file $name
    then
	XinetdDone=1
	XinetdEnableServiceInRegFile $file $name
    else
	local ifile
	for ifile in $(XinetdGetIncludeFiles $file)
	do
	    XinetdEnableServiceInConfFile $ifile $name
	    if test $XinetdDone = 1; then return; fi
	done
	local idir
	for idir in $(XinetdGetIncludeDirs $file)
	do
	    for ifile in $(ls $idir)
	    do
		XinetdEnableServiceInConfFile $idir/$ifile $name
		if test $XinetdDone = 1; then return; fi
	    done
	done
    fi
}


XinetdHasService ()
{
    local file=$1 && shift
    local name=$1 && shift

    grep "service[ 	]*$name" $file >/dev/null
}


XinetdGetIncludeFiles ()
{
    local file=$1 && shift
    awk '{if ($1 == "include") print $2; }' $file
}


XinetdGetIncludeDirs ()
{
    local file=$1 && shift
    awk '{if ($1 == "includedir") print $2; }' $file
}


XinetdEnableServiceInRegFile ()
{
    local file=$1 && shift
    local name=$1 && shift

    local textOrig
    textOrig=$(cat $file)
    local textMod
    textMod=$(    
	IGNORECASE=1 awk '{
		result=$0
		if ($1 == "service")
		{
			my_section = ($2 == "'$name'")
		}
		else if ($1 == "}")
		{
			my_section = 0
		}
		if (my_section && (($1 == "disabled") || ($1 == "disable")))
		{
			if ($2 == "=" || $2 == "+=")
			{
				if ($3 == "yes")
				{
					sub("yes", "no", result)
				}
			}
			status = 1
		}
		print result
		}' $file)

    if test "$textOrig" != "$textMod"
    then
	echo "$file: service $name disabled line changed from yes to no"
	InstallFileFromText $file "$textMod"
    else
	Output "$file: service $name already enabled"
    fi
}


InstallFileFromText ()
{
    local dst=$1 && shift
    local text=$1 && shift

    local src=$SzInstDir/$(basename $dst)
    Verbose -o $src echo "$text"
    InstallFileFromSrc "$src" "$dst"
    rm -f $src
}


InstallFileFromInstDir ()
{
    local dst=$1 && shift
    local src=$SzInstDir/$(basename $dst)
    InstallFileFromSrc "$src" "$dst"
}


CompareFiles ()
{
    cmp -s "$1" "$2" || diff -w -I'^#' "$1" "$2" >/dev/null 2>/dev/null
}


InstallFileFromSrc ()
{
    local src=$1 && shift
    local dst=$1 && shift

    if test -e $dst
    then
	if CompareFiles $src $dst
	then
	    return
	elif CheckSumIsCorrect "$dst"
	then
	    rm -f "$dst"
	else
	    BackupDstFile "$src" "$dst"
	fi
    fi

    Verbose cp -af $src $dst
    Verbose restorecon -v $dst
}


BackupDstFile()
{
    local src=$1 && shift
    local dst=$1 && shift

    local save
    save=$(GetBackupFileName $dst)

    if test -e $save && ! CompareFiles $dst $save
    then
	if test $OptForce = 0
	then
	    Fatal "Cannot save $dst: $save already exists

Please remove $dst or $save, then
- rerun the installation. or
- open a command line and run $0 $@"
	fi
	save=$(GetBackupFileName $dst)-pid$$
    fi

    MakeDir $SzUnInstDir
    Verbose mv -f $dst $save

    echo > $save.restore \
	"#!/bin/sh
# created by $0 on $(date +%Y%m%dT%H%M%S)
# purpose: restore backup of $dst 
#          provided that $dst is still equal to
#          previously installed $src
CompareFiles () { cmp -s \"\$1\" \"\$2\" || diff -w -I'^#' \"\$1\" \"\$2\" >/dev/null 2>/dev/null; }
PATH=/bin:/sbin:/usr/bin:/usr/sbin:\$PATH
if CompareFiles $dst $src; then
	echo \$0: mv -f $save $dst
	set -e
	mv -f $save $dst
	restorecon -v $dst || :
	rm -f \$0
else
	echo \$0: cannot restore: files are different:
	echo \"  $src\"
	echo \"  $dst\"
fi"
}


GetBackupFileName ()
{
    local file=$1 && shift
    echo $file | sed -e 's|/|^^|g' -e 's|$|.backup|' -e "s|^|$SzUnInstDir/|"
}


GetOriginalFileName ()
{
    local file=$1 && shift
    echo $file | sed -e "s|^.*/uninstall/||" -e 's|.backup$||' -e 's|\^\^|/|g'
}


CheckSumIsCorrect ()
{
    local file=$1 && shift
    test "$(CheckSumCompute "$file")" =  "$(CheckSumRead "$file")"
}


CheckSumCompute ()
{
    local file=$1 && shift
    sed '/# Bruker cksum /d' "$file" | cksum | cut -d' ' -f1
}


CheckSumRead ()
{
    local file=$1 && shift
    local cksum
    cksum=$(sed -n '/# Bruker cksum /p' "$file" | cut -d' ' -f4)
    test "$cksum" && echo "$cksum" || CheckSumInvalidStr
}


CheckSumExists ()
{
    local file=$1 && shift
    grep "# Bruker cksum " "$file" >/dev/null
}


CheckSumInvalidStr ()
{
    echo _invalid_
}


SvcLibRestoreBackupFiles ()
{
    local name=
    for name in "$@"
    do
	for restore in $(ls $SzUnInstDir/*$name*.backup.restore 2>/dev/null)
	do
	    Verbose sh $restore
	done
    done
}


SetQueryYumInstall ()
{
    if which yum >/dev/null 2>/dev/null
    then
	# QueryYumInstall returns true if yum succeeds
	QueryYumInstall ()
	{
	    local rpm_text="$1"
	    local tPackage="$2"
	    msg="$rpm_text\n\nDo you want to install $tPackage using \"yum install\"?"
	    AskYesNo "$msg"
	}
    else
	QueryYumInstall ()
	{
	    return 1;
	}
    fi
}


SetSwimEnvironment ()
{
    szDistRootDir=${szDistRootDir:-/mnt/media}
    export SCRIPT_NAME=$0	# Used to display usage message.
    export bAutoInstall=0	# If 1, QUERY does not display anything.
}


# Purpose:	Wrapper for Yes/No query.
#
# Output:	Output from $QUERY
#
# Returns:	TRUE for YES, FALSE for NO
AskYesNo ()
{
    CommonQuery -w "$@" No Yes && return 1 || return 0
}


# Purpose:	Wrapper to call GuiQuery or TuiQuery
# Output:	number of clicked button
CommonQuery ()
{
    local dlgType=$1 && shift
    local dlgMsg=$1 && shift

    local szPrefix
    case $dlgType in
	-e) local szPrefix="Error";;
	-w) local szPrefix="Warning";;
	*)  local szPrefix="Notice";;
    esac

    # Display message.
    dlgMsg="
***  $szPrefix  ***

$dlgMsg"
    Output "$dlgMsg"

    set -- "$dlgType" "$dlgMsg" "$@"
    if test -n "${QUERY:-}"
    then
	GuiQuery "$@"
    else
	TuiQuery "$@"
    fi
}


# Purpose:	Wrapper to call GuiQuery or TuiQuery
# Output:	number of clicked button
# Note:		Echo dlg message and answer for GUI log file.
GuiQuery ()
{
    local dlgType=$1 && shift
    local dlgMsg=$1 && shift

    local out
    local button
    local buttons=
    for button in "$@"
    do
	buttons="$buttons &$button"
    done
    out=$($QUERY "$dlgType" "$dlgMsg" "$@")

    if test $(ArgCount "$@") -gt 0
    then
	case "$out" in
	    0) Output "[$1]";;
	    1) Output "[$2]";;
	esac
	return $out
    fi
}


#***************************************************************************
#
# Purpose:	Substitute $QUERY in SWIM
#
# Arguments:	1	message type:
#			-e	error
#			-w	warning
#		2	message text
#		3-n	Button list (can be empty)
#
# Output:	Number of the selected button, or nothing if there is none.
#
#***************************************************************************
TuiQuery ()
{
    local dlgType=$1 && shift
    local dlgMsg=$1 && shift

    if test $# = 0; then return; fi

    local szAnswer
    local szButton
    local ind
    while :
    do
	ind=0
	for szButton in "$@"
	do
	    $ECHO >&2 "$ind) $szButton"
	    ind=`expr $ind + 1`
	done

	$ECHO >&2 "Choose a number:  \c"
	read szAnswer
	if IsNumber "${szAnswer:-0}"; then return $szAnswer; fi
	$ECHO >&2 "Invalid answer."
	$ECHO >&2 "Please enter number from 0 to `expr $ind - 1||:`"
    done
}


ShellCmd ()
{
    if [ -f "$1" ]
    then
	VerboseOk $BSH $BSHOPT "$@"
    fi
}


IsNumber ()
{
    test "$1" -eq "$1" 2>/dev/null
}


# Purpose:	retrieve only shell opts that are posix compliant
# @note:	'echo -e' would return nothing. Work around is 
#		either 'POSIXLY_CORRECT=1 /bin/echo -e'
#		or echo ' -e'
GetBshOpt ()
{
    local bshOpts=
    case $- in -x) bshOpts=${bshOpts}x; esac
    case $- in -e) bshOpts=${bshOpts}e; esac
    case $bshOpts in
	"") echo;;
	*)  echo " -$bshOpts";;
    esac
}


MakeDir ()
{
    test -d "$1" || mkdir -p "$1"
}


GetRealPath ()
{
    if [ -d $1 ]
    then
	(cd $1 >/dev/null && pwd)
    else
	echo $1
    fi
}


ArgCount ()
{
    echo $#
}


GetWord1 ()
{
    echo "$1"
}


GetWord4 ()
{
    echo "$4"
}


GetActionMsg ()
{
    test $OptRemove = 0 && echo install || echo remove
}


GetDryRunMsg ()
{
    test $OptDryRun = 0 && echo "" || echo " (show only)"
}


Plural ()
{
    test $# -gt 1 && echo s || :
}


#***************************************************************************
#
# Purpose:		Set or retrieve "do not save" flag
#
# Arguments:		1	path of file
#			2	action ( set or test )
#
#***************************************************************************
DoNotSaveFileFlag ()
{
    local flagname
    # substitute characters not suitable for shell variable names
    flagname=$(echo "$1" | sed \
	-e 's|/|_Slash_|g' \
	-e 's| |_Blank_|g' \
	-e 's|\.|_Dot_|g' \
	-e 's|-|_Minus_|g')_DoNotSave

    if test "$2" = "set" 
    then
	eval $flagname="\"$2\""
    else
	eval test \$$flagname
    fi
}


SetDoNotSaveFileFlag ()
{
    DoNotSaveFileFlag "$1" set
}


TestDoNotSaveFileFlag ()
{
    local shellOpts=$-
    set +u
    DoNotSaveFileFlag "$1" test
    set -$shellOpts
}


# @purpose	Print command and, if OptDryRun=0, run it.
# @param 1	-i file: read input from file
#		-o file: write output to file
#		-e: ignore errors
Verbose ()
{
    case ${1:-x} in
	-i)
	    local inputFile=$2 && shift 2
	    Output command: $* '<' $inputFile
	    if test $OptDryRun = 0
	    then
		RunCmd "$@" < $inputFile || ErrorMsg "$@"
	    fi
	    ;;
	-o)
	    local outputFile=$2 && shift 2
	    Output command: $* '>' $outputFile
	    if test $OptDryRun = 0
	    then
		RunCmd "$@" > $outputFile || ErrorMsg "$@"
	    fi
	    ;;
	-e)
	    shift 1
	    Output command: $*
	    RunCmd "$@" || :
	    ;;
	*)
	    Output command: $*
	    RunCmd "$@" || ErrorMsg "$@"
    esac
}


RunCmd ()
{
    if test $OptDryRun = 1
    then
	: "$@"
    else
	"$@"
    fi
}


# Purpose:	Print description, execute command and print result.
VerboseOk ()
{
    Output "$* ... \c"

    if test $OptDryRun = 1
    then
	Output "[TEST]"
    elif "$@" 3>&-
    then
	Output "[OK]"
    else
	echo >&2 "($*) [FAILED]"
	NExitValue=1
	return 1
    fi
}


Output ()
{
    $ECHO >&3 "$@"
}


Warning ()
{
    $ECHO >&3 "\n***  WARNING:  ***\n\n$@\n"
}


# Reset the error variable
ErrorMsg ()
{
    $ECHO >&2 "\n***  ERROR:  ***\n\n$@\n"
    NExitValue=1
}


Fatal ()
{
    $ECHO >&2 "***  Fatal error:  ***\n\n$0: $@\n"
    NExitValue=1
    exit 1
}


Internal ()
{
    $ECHO >&2 "\n***  Internal error::  ***\n\n$@\n"
    NExitValue=1
    exit 1
}


# @note		When called in a sub shell a first exit message is printed. 
#		The exit in turn generates a trap in the parent shell
#		calling ExitHandler 99 which shall exit silently.
ExitHandler ()
{
    trap "" 0

    local status=${1:-0}

    if test $status = 99
    then
	if test $NExitValue = 0
	then
	    NExitValue=$status
	    Output >&2 "$0: error: previous command failed"
	fi
    elif test $status = 0
    then
	if test $NExitValue = 0
	then
	    Output "$0: success"
	else
	    Output >&2 "$0: error: terminated with status $NExitValue"
	fi	    
    else
	NExitValue=$status
	Output >&2 "$0: error: terminated with status $NExitValue"
    fi

    exit $NExitValue
}
