# vim: set ft=sh:
# shellcheck shell=sh
#
# https://shellcheck.net/wiki/SC2154
# shellcheck disable=2154

print()
{
    printf "%s %s\n" "${2:-">>"}" "$1"
}

panic()
{
    print "${1:-unexpected error occurred}" '!>' >&2

    if [ "$$" = 1 ]; then
        sh
    else
        exit 1
    fi
}

# random()
# {
#     _sum=$(cksum < /proc/uptime)
#     printf '%s' "${sum% *}"
# }

# TODO ensure correctness
copy_file()
(
    file=$1; dest=$2

    [ -e "${tmpdir}/${dest}" ] && return

    while [ -h "$file" ]; do
        mkdir -p "${tmpdir}/${file%/*}"
        cp -P "$file" "${tmpdir}/${file}"
        cd -P "${file%/*}" || exit

        symlink=$(ls -ld "$file")
        symlink=${symlink##* -> }

        case $symlink in
            /*) file=$symlink ;;
            *)  file="${PWD}/${symlink##*/}" ;;
        esac
    done

    [ -h "${tmpdir}/${dest}" ] && dest=$file

    mkdir -p "${tmpdir}/${dest%/*}"
    cp "$file" "${tmpdir}/${dest}"

    [ "$3" ] && chmod "$3" "${tmpdir}/${dest}"

    # https://shellcheck.net/wiki/SC2015
    # shellcheck disable=2015
    [ "$4" ] && strip "${tmpdir}/${dest}" > /dev/null 2>&1 || :
)

copy_exec()
{
    _bin=$(command -v "$1")

    case $_bin in /*) ;;
        '')
            panic "unable to find command: $1"
        ;;
        *)
            # https://shellcheck.net/wiki/SC2086
            # shellcheck disable=2086
            { IFS=:; set -- $PATH; unset IFS; }

            for _dir; do
                __bin="${_dir}/${_bin}"

                [ -x "$__bin" ] && break
            done

            # https://shellcheck.net/wiki/SC2015
            # shellcheck disable=2015
            [ -x "$__bin" ] && _bin=$__bin || panic "unable to find command: $_bin"
        ;;
    esac

    copy_file "$_bin" "/bin/${_bin##*/}" 0755 1

    # TODO copy libs to the directory of interpreter.
    ldd "$_bin" 2> /dev/null |

    while read -r _lib || [ "$_lib" ]; do
        _lib=${_lib#* => }
        _lib=${_lib% *}

        [ -e "$_lib" ] && copy_file "$_lib" "$_lib" 0755 1
    done
}

copy_kmod()
{
    modprobe -S "$kernel" -D "$1" 2> /dev/null |

    while read -r _ _mod || [ "$_mod" ]; do
        case $_mod in /*) copy_file "$_mod" "$_mod" 0644; esac
    done
}

# TODO allow full path to hook
copy_hook()
{
    for _dir in "${local+./hook}" /etc/tinyramfs/hook.d /lib/tinyramfs/hook.d; do
        _hook="${_dir}/${1}/${1}"
        [ -f "$_hook" ] && break
    done

    [ -f "$_hook" ] || panic "unable to find hook: $1"

    for _ext in init init.late; do
        [ -f "${_hook}.${_ext}" ] || continue

        print "copying hook: ${1}.${_ext}"

        copy_file "${_hook}.${_ext}" "/lib/tinyramfs/hook.d/${1}/${1}.${_ext}" 0644
    done

    print "evaluating hook: $1"

    # https://shellcheck.net/wiki/SC1090
    # shellcheck disable=1090
    . "$_hook"
}

resolve_device()
{
    device=$1; _count=${2:-30}

    case ${device%%=*} in
        UUID)     device="/dev/disk/by-uuid/${device#*=}"     ;;
        LABEL)    device="/dev/disk/by-label/${device#*=}"    ;;
        PARTUUID) device="/dev/disk/by-partuuid/${device#*=}" ;;
        /dev/*)          ;;
        *)        return ;;
    esac

    # Race condition may occur if device manager is not yet initialized device.
    # To fix this, we simply waiting until device is available. If device
    # didn't appear in specified time, we panic.
    while :; do
        if [ -b "$device" ]; then
            return
        elif [ "$((_count -= 1))" = 0 ]; then
            break
        else
            sleep 1
        fi
    done

    panic "failed to lookup partition: $device"
}