#!/bin/sh -e
# Enter a chroot
# shellcheck disable=2004

## SYNOPSIS:
## .Nm cpt-chroot
## .Op Fl m
## .Op Ar dir

## DESCRIPTION:
## .Nm
## is a wrapper script to chroot inside other root filesystems. It automatically
## mounts important filesystems in to the chroot directory, and unmounts them
## when the user exits the chroot and cleans up leftover host files. If the flag
## .Fl m
## is given,
## .Nm
## does not try to mount or unmount any filesystems.

# We generate the parser into the script, because we don't want this script to
# depend on cpt-lib.
nomount=''
REST=''
parse() {
    OPTIND=$(($#+1))
    while OPTARG= && [ $# -gt 0 ]; do
        case $1 in
            --?*=*) OPTARG=$1; shift
                    eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
                    ;;
            --no-*) unset OPTARG ;;
            -[mh]?*) OPTARG=$1; shift
                     eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
                     OPTARG= ;;
        esac
        case $1 in
            '-m')
                [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
                eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG=''
                nomount="$OPTARG"
                ;;
            '-h'|'--help')
                usage
                exit 0 ;;
            --)
                shift
                while [ $# -gt 0 ]; do
                    REST="${REST} \"\${$(($OPTIND-$#))}\""
                    shift
                done
                break ;;
            [-]?*)
                set "unknown" "$1"; break ;;
            *)
                REST="${REST} \"\${$(($OPTIND-$#))}\""
        esac
        shift
    done
    [ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
    case $1 in
        unknown) set "Unrecognized option: $2" "$@" ;;
        noarg) set "Does not allow an argument: $2" "$@" ;;
        required) set "Requires an argument: $2" "$@" ;;
        pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
        notcmd) set "Not a command: $2" "$@" ;;
        *) set "Validation error ($1): $2" "$@"
    esac
    echo "$1" >&2
    exit 1
}
usage() { printf '%s\n' "usage: ${0##*/} [-m] [dir]" "" "Options:" \
    "  -m                          Don't mount or unmount directories"
}

parser_definition() {
    setup REST help:usage -- "usage: ${0##*/} [-m] [dir]"
    msg -- '' 'Options:'
    flag nomount -m        -- "Don't mount or unmount directories"
    disp :usage  -h --help hidden:1
}

log() {
    printf '\033[32m->\033[m %s.\n' "$*"
}

die() {
    log "$*" >&2
    exit 1
}

clean() {
    log Unmounting /dev, /proc and /sys from chroot; {
        umount "$1/dev"  ||:
        umount "$1/proc" ||:
        umount "$1/sys"  ||:
    }

    log Cleaning leftover host files; {
        rm -f "$1/root/.ash_history"
    }
}

main() {
    parse "$@" && eval set -- "$REST"
    [ -d "$1" ]        || die Given path does not exist
    [ "$(id -u)" = 0 ] || die Script needs to be run as root

    [ "$2" ] || {
        march=$(uname -m 2>/dev/null) ||:
        case "$march" in
            '')     march=native ;;
            x86_64) march=x86-64 ;;
            i*86)   march=i686 ;;
        esac
    }

    [ -z "$nomount" ] && {
        trap 'clean "$1"' EXIT INT

        log Mounting /dev, /proc and /sys from host; {
            mountpoint -q "$1/dev"  || mount -o bind /dev "$1/dev"
            mountpoint -q "$1/proc" || mount -t proc proc "$1/proc"
            mountpoint -q "$1/sys"  || mount -t sysfs sys "$1/sys"

        }

        log Copying /etc/resolv.conf from host; {
            [ -f "$1/etc/resolv.conf" ] || cp /etc/resolv.conf "$1/etc"
        }
    }

    log Entering chroot; {
        chroot "$1" /usr/bin/env -i \
            HOME=/root \
            TERM="$TERM" \
            SHELL=/bin/sh \
            USER=root \
            CFLAGS="${CFLAGS:--march=$march -mtune=generic -pipe -Os}" \
            CXXFLAGS="${CXXFLAGS:--march=$march -mtune=generic -pipe -Os}" \
            MAKEFLAGS="${MAKFLAGS:--j$(nproc 2>/dev/null || echo 1)}" \
            /bin/sh -l
    }
}

main "$@"