#!/bin/bash
# -*- mode: sh; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: nil -*-
# vi: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab:
# :indentSize=4:tabSize=8:noTabs=true:
# vim: filetype=sh

# ---- error handling
set -o errexit;
set -o posix;
set -o pipefail;
set -o errtrace;
unexpected_error() {
    local errstat=$?
    echo "${g_prog:-$(basename "$0")}: Error! Encountered unexpected error at 'line $(caller)', bailing out..." 1>&2
    exit $errstat;
}
trap unexpected_error ERR;

# Force the path to only what we need, saving off the original path
PATH_ORIG=$PATH;
PATH=/usr/bin:/bin

g_prog=$(basename "$0");
g_progdir=$(dirname "$0");
g_progdir_abs=$(dirname "$(readlink -f "$0")");

# ---- global env

. "$g_progdir_abs/../../private/runtime-common/lib/globalenv.ish"

# ---- usage

usage() {
    local exitstat=2;
    if [[ ! -z "${2:-}" ]] ; then
        if [[ ! $2 =~ [[:digit:]]+ ]] ; then
            minterror "usage(): exitstat ($2) must be numeric."
        fi
        exitstat=$2;
    fi

    # Only redirect to stderr on non-zero exit status
    if [[ $exitstat -ne 0 ]] ; then
        exec 1>&2;
    fi

    if [[ ! -z "${1:-}" ]] ; then
        echo "$g_prog: Error! $1"
    fi

    echo "Usage: $g_prog [--] [update_args] upgrade_tarball.tar.gz"
    echo "       $g_prog [--] [update_args] upgrade_tarball.run"
    echo "       $g_prog [--] [update_args] upgrade_tarball.bin"
    echo "         --              -- ignore remaining args, pass to upgrader subprog"
    echo "         --help          -- print this usage"
    echo ""

    if [[ $exitstat -ne 0 ]] ; then
        # Print error again, useful for long usages messages
        if [[ ! -z "${1:-}" ]] ; then
            echo ""
            echo "$g_prog: Error! $1"
        fi
    fi

    # bash only:
    if [[ $exitstat -ne 0 ]] ; then
        echo "  at: $(caller)";
    fi
    exit $exitstat;
}

# ---- error functions
merror() {
    echo "$g_prog: Error! ""$@" 1>&2;
    exit 1;
}
minterror() {
    echo "$g_prog: Internal Error! ""$@" 1>&2;
    exit 1;
}
mwarn() {
    echo "$g_prog: Warning! ""$@" 1>&2;
}

# ---- argument parsing

# Save off the original args, use as "${g_origargs[@]}" (with double quotes)
declare -a g_origargs;
g_origargs=( ${1+"$@"} )

opt_havesep=false;
for opt in ${1+"$@"}; do [[ x"$opt" == x"--" ]] && opt_havesep=true; done

# Ignore any unrecogninzed arguments, just pass them to the subprogram
declare -a opt_subargs;
while [[ $# != 0 ]]; do
    opt="$1"; shift;
    case "$opt" in
        # Flag with no argument example:
        #   --flag|--fla|--fl|--f)
        #     opt_flag=true;
        # Option with argument example:
        #   --arg|--ar|--a)
        #     [ $# -eq 0 ] && usage;
        #     opt_somearg=$1; shift;;
        -h|-help|--help|--hel|--he|--h) usage "" 0;;
        --)
            # We have a separator. Pass everything after the separator to
            # the subprogram, with the exception of the tarball (removed
            # below).
            opt_subargs=( ${1+"$@"} ); break;;
        *)
            # Unrecognized arguments.  If we have a ("--") separator in the
            # arglist, then issue an error.  If we don't have a separator,
            # then just pass any unrecognized arguments to the subprogram.
            if $opt_havesep; then
                usage "Unrecognized argument: $opt"
            else
                opt_subargs=( "${opt_subargs[@]}" "$opt" );
            fi
            ;;
    esac
done

# Remove the last argument in opt_subargs (it should be the tarball arg)
unset opt_subargs[${#opt_subargs[@]}-1]

# Must have at least one argument in the orignal args (the upgrade tarball)
[[ ${#g_origargs[@]} == 0 ]] && usage "Missing upgrade_tarball argument.";
# Always get the tarball from the final argument of the orignal args
opt_tarball=${g_origargs[${#g_origargs[@]}-1]};
# Check that tarball exists, is a file, and is readable
[[ ! -e "$opt_tarball" ]] && usage "tarball '$opt_tarball' does not exist.";
[[ ! -f "$opt_tarball" ]] && usage "tarball '$opt_tarball' is not a regular file";
[[ ! -r "$opt_tarball" ]] && usage "tarball '$opt_tarball' is not a readable";

# ---- globals

# ---- subroutines

cleanup_on_exit() {
    local dir;
    local file;
    for file in $g_cleanup_files; do
        /bin/rm -f "$file";
    done
    for dir in $g_cleanup_dirs; do
        /bin/rm -rf "$dir";
    done
}

run_selfextract_tarball() {
    local script_cmd;
    local shebang_cmd;
    local cmd;
    script_cmd=$(readlink -f "$opt_tarball");

    if [[ -x "$script_cmd" ]] ; then
        cmd="$script_cmd";
    else
        shebang_cmd=$(sed -ne '1 { s/#![[:space:]]*//; s/[[:space:]].*//;p}' "$script_cmd");
        if [ -f "$shebang_cmd" ] && [ -x "$shebang_cmd" ] ; then
            cmd="$shebang_cmd $script_cmd";
        else
            # can't find the #! command, last ditch, try to run with bash
            cmd="bash $script_cmd";
        fi
    fi

    echo "Running update from self-extracting tarball..."
    local stat=0
    PATH=$PATH_ORIG $cmd --update --start-origcmd "$0" "${g_origargs[@]}" --end-origcmd "${opt_subargs[@]}" || stat=$?
    if [[ $stat -ne 0 ]] ; then
        exit $stat;
    fi
}

run_tarball_installer() {
    local extract_arg;
    case "$opt_tarball" in
        *.tar.gz|*.tgz) extract_arg="z";;
        *.tar.bz2)      extract_arg="j";;
        *.tar)          extract_arg="";;
        *) minterror "Unrecognized tarball extension: $opt_tarball";
    esac

    local tmpdir;
    tmpdir=$(mktemp -d -t "smrtupdater.tmpdir.XXXXXXXXXX");

    # Make sure we clean up the tmp dir on exit
    g_cleanup_dirs="$g_cleanup_dirs $tmpdir";
    trap cleanup_on_exit EXIT;

    local tarball_scriptpath="etc/scripts/install_release.sh";

    echo "Extracting install script from tarball..."
    tar -C "$tmpdir" -x${extract_arg}f "$opt_tarball" --wildcards --no-wildcards-match-slash "*/$tarball_scriptpath";

    local install_script;
    install_script=$(echo "$tmpdir/"*"/$tarball_scriptpath");


    local script_cmd="$install_script";
    local shebang_cmd;
    local cmd;
    if [[ -x "$script_cmd" ]] ; then
        cmd="$script_cmd";
    else
        shebang_cmd=$(sed -ne '1 { s/#![[:space:]]*//; s/[[:space:]].*//;p}' "$script_cmd");
        if [ -f "$shebang_cmd" ] && [ -x "$shebang_cmd" ] ; then
            cmd="$shebang_cmd $script_cmd";
        else
            # can't find the #! command, last ditch, try to run with bash
            cmd="bash $script_cmd";
        fi
    fi

    echo "Executing install script from tarball..."
    local stat=0
    PATH=$PATH_ORIG $cmd --update --start-origcmd "$0" "${g_origargs[@]}" --end-origcmd "${opt_subargs[@]}" || stat=$?
    if [[ $stat -ne 0 ]] ; then
        exit $stat;
    fi
}

# ---- main

case "$opt_tarball" in
    *.run|*.bin)
        run_selfextract_tarball;;
    *.tar.gz|*.tgz|*.tar.bz2|.tar)
        run_tarball_installer;;
    *)
        merror "Unrecognized tarball type: $opt_tarball";;
esac
exit 0;
