# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Be aware that this code runs very early in the script.  Therefore, we
# cannot assume that any of the other shtk libraries have been loaded (and
# we cannot attempt to load them).  The only thing we can assume is that
# bootstrap.subr has been loaded and that we are being called by that piece
# of code.


# List of already-loaded modules.
_Shtk_LoadedModules=


if [ -n "${ZSH_VERSION+set}" ]; then
    emulate sh >/dev/null 2>&1
fi


# Kills all subshells up to the main script, except for self.  Calls to this
# function must be followed by a call to exit.
_shtk_kill_subshells() {
    # AWK script to extract the single PID printed by ps(1).
    local awk_get_pid='/[a-zA-Z]/ {next;} {print $1;}'

    local pid=
    if [ -f /proc/curproc/status ]; then
        # FreeBSD/NetBSD-style proc file system.
        IFS=' 	' read unused_name pid unused_rest </proc/curproc/status
    elif [ -f /proc/self/stat ]; then
        # Linux-style proc file system.
        IFS=' 	' read pid unused_rest </proc/self/stat
    else
        # No known proc file system available.  Obtain the PID of this subshell
        # by using a helper script that outputs the PID of its parent (which
        # happens to be the subshell we are interested in).
        local script="$(mktemp "${TMPDIR:-/tmp}/shtk.XXXXXX")"
        cat >"${script}" <<EOF
ps -o ppid -p "\${$}" | awk '${awk_get_pid}'
EOF
        /bin/sh "${script}" >"${script}.out"
        read pid <"${script}.out"
        rm -f "${script}" "${script}.out"
    fi

    local top_pid="${$}"
    local pids=
    local rounds=100  # Prevent infinite loops.
    while [ "${pid}" != "${top_pid}" -a -n "${pid}" -a ${rounds} -gt 0 ]; do
        pid="$(ps -o ppid -p "${pid}" | awk "${awk_get_pid}")"
        if [ "${pid}" = 1 ]; then
            # Search failed for some reason (probably due to a bug here).
            # Better ignore anything we have found and leave processes around
            # than attempt killing ~everything in the user's session.
            pids=
            break
        fi
        pids="${pid} ${pids}"
        rounds="$((${rounds} - 1))"
    done
    if [ ${rounds} -eq 0 ]; then
        # If we encountered an infinite loop or too many processes for some
        # strange reason, do nothing.  This is nicer to the user and our tests
        # should catch this condition.
        :
    else
        [ -z "${pids}" ] || kill ${pids}
    fi
}


shtk_abort() {
    [ ${#} -eq 0 ] || echo "${0##*/}: A: ${*}" 1>&2
    _shtk_kill_subshells
    exit 127
}


shtk_import() {
    local module="${1}"; shift

    for loaded_module in ${_Shtk_LoadedModules}; do
        [ "${loaded_module}" != "${module}" ] || return 0
    done

    local modules_path="${SHTK_MODULESPATH}:${SHTK_MODULESDIR}"
    local attempts=
    local oldifs="${IFS}"
    IFS=:
    for dir in ${modules_path}; do
        [ -n "${dir}" ] || continue

        local file="${dir}/${module}.subr"
        if [ -f "${file}" ]; then
            IFS="${oldifs}"
            . "${file}"
            _Shtk_LoadedModules="${_Shtk_LoadedModules} ${module}"
            IFS=:

            attempts=
            break
        else
            if [ -z "${attempts}" ]; then
                attempts="${file}"
            else
                attempts="${attempts} ${file}"
            fi
        fi
    done
    IFS="${oldifs}"

    if [ -n "${attempts}" ]; then
        echo "${0##*/}: E: Cannot load module ${module}; tried ${attempts}" 1>&2
        exit 1
    fi
}
