#!/bin/sh

set -eu

libexecdir="${LMBENCH_LIBEXECDIR:-@LIBEXECDIR@/lmbench}"
package_version="@PACKAGE_VERSION@"
workdir="${LMBENCH_WORKDIR:-}"
custom_workdir=no

if [ -n "$workdir" ]; then
    custom_workdir=yes
fi

usage()
{
    cat <<EOF
Usage: lmbench [OPTION]... [COMMAND [ARG]...]
       lmbench PROGRAM [ARG]...

Prepare a private lmbench workspace for the calling user and run the
packaged lmbench driver or one of the packaged benchmark programs.

Commands:
  prepare [--reconfigure]  create the workspace and run the interactive
                           configuration when no configuration exists
  run                      run the benchmark suite with the saved
                           configuration (default)
  see                      summarize the results in the workspace
  list                     list the packaged benchmark programs
  PROGRAM [ARG]...         run a packaged benchmark program directly,
                           for example: lmbench lat_syscall null

Options:
  --workdir DIR    use DIR as the workspace
  --print-workdir  prepare and print the workspace path
  --reset          remove the default workspace before preparing it
  -h, --help       show this help

The default workspace is XDG_STATE_HOME/lmbench, or
HOME/.local/state/lmbench when XDG_STATE_HOME is unset.
EOF
}

die()
{
    echo "lmbench: $*" >&2
    exit 1
}

[ -d "$libexecdir" ] || die "packaged files not found in $libexecdir"
libexecdir=$(cd -- "$libexecdir" && pwd -P)
arch=
for dir in "$libexecdir"/bin/*/; do
    [ -d "$dir" ] || continue
    [ -z "$arch" ] || die "multiple benchmark directories in $libexecdir/bin"
    arch=${dir%/}
    arch=${arch##*/}
done
[ -n "$arch" ] || die "packaged benchmarks not found in $libexecdir/bin"
bindir="$libexecdir/bin/$arch"

reset=no
print_workdir=no
options=no
command=run

while [ "$#" -gt 0 ]; do
    case "$1" in
        --workdir)
            [ "$#" -ge 2 ] && [ -n "$2" ] || {
                echo "lmbench: --workdir requires a directory" >&2
                exit 2
            }
            workdir=$2
            custom_workdir=yes
            options=yes
            shift 2
            ;;
        --print-workdir)
            print_workdir=yes
            options=yes
            shift
            ;;
        --reset)
            reset=yes
            options=yes
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        -*)
            echo "lmbench: unknown option: $1" >&2
            usage >&2
            exit 2
            ;;
        *)
            command=$1
            shift
            break
            ;;
    esac
done

case "$command" in
    prepare|run|see|list)
        ;;
    lmbench|*/*)
        echo "lmbench: unknown command: $command" >&2
        usage >&2
        exit 2
        ;;
    *)
        [ -f "$bindir/$command" ] && [ -x "$bindir/$command" ] || {
            echo "lmbench: unknown command: $command" >&2
            usage >&2
            exit 2
        }
        [ "$options" = no ] ||
            die "options are not supported with benchmark programs"
        PATH="$bindir:$PATH"
        export PATH
        exec "$bindir/$command" "$@"
        ;;
esac

if [ "$command" = list ]; then
    for file in "$bindir"/*; do
        [ -f "$file" ] && [ -x "$file" ] || continue
        [ "${file##*/}" = lmbench ] || printf '%s\n' "${file##*/}"
    done
    exit 0
fi

reconfigure=no
if [ "$command" = prepare ] && [ "$#" -gt 0 ] && [ "$1" = --reconfigure ]; then
    reconfigure=yes
    shift
fi
[ "$#" -eq 0 ] || {
    echo "lmbench: unexpected argument: $1" >&2
    exit 2
}

if [ -z "$workdir" ]; then
    case "${XDG_STATE_HOME:-}" in
        /*) state_home=$XDG_STATE_HOME ;;
        *) state_home="${HOME:?HOME is not set}/.local/state" ;;
    esac
    workdir="$state_home/lmbench"
fi
marker="$workdir/.lmbench-workspace"

if [ "$reset" = yes ]; then
    [ "$custom_workdir" = no ] ||
        die "--reset is only supported for the default workspace"
    if [ -e "$workdir" ] || [ -L "$workdir" ]; then
        [ -d "$workdir" ] && [ ! -L "$workdir" ] && [ -f "$marker" ] ||
            die "refusing to reset an unmanaged directory: $workdir"
        rm -rf -- "$workdir"
    fi
fi

umask 077
if [ ! -e "$workdir" ] && [ ! -L "$workdir" ]; then
    mkdir -p -- "$workdir"
    printf '%s\n' "$package_version" > "$marker"
fi
[ -d "$workdir" ] && [ -f "$marker" ] ||
    die "refusing to use an unmanaged directory: $workdir"
workdir=$(cd -- "$workdir" && pwd -P)

# Upstream scripts locate their files relative to the parent directory, so
# every directory they write into must be a real directory in the workspace.
for dir in bin "bin/$arch" src results; do
    if [ -L "$workdir/$dir" ] ||
       { [ -e "$workdir/$dir" ] && [ ! -d "$workdir/$dir" ]; }; then
        die "unexpected file in the workspace: $workdir/$dir"
    fi
    mkdir -p -- "$workdir/$dir"
done

link()
{
    if [ -e "$2" ] && [ ! -L "$2" ]; then
        die "unexpected file in the workspace: $2"
    fi
    ln -sfn -- "$1" "$2"
}

link "$libexecdir/scripts" "$workdir/scripts"
link "$libexecdir/results/Makefile" "$workdir/results/Makefile"
for file in version.h webpage-lm webpage-lm.tar; do
    link "$libexecdir/src/$file" "$workdir/src/$file"
done
for file in "$workdir/bin/$arch"/*; do
    if [ -L "$file" ] && [ ! -e "$file" ]; then
        rm -f -- "$file"
    fi
done
for file in "$bindir"/*; do
    link "$file" "$workdir/bin/$arch/${file##*/}"
done

OS=$arch
export OS

config=$(cd "$workdir/src" && ../scripts/config)
config_path="$workdir/bin/$arch/$config"

if [ "$print_workdir" = yes ]; then
    printf '%s\n' "$workdir"
    exit 0
fi

if [ "$command" = see ]; then
    cd "$workdir/results"
    exec make -s summary percent 2>/dev/null
fi

if [ -z "${LMBENCH_FSDIR:-}" ]; then
    LMBENCH_FSDIR="/var/tmp/lmbench-$(id -u)"
    mkdir -p -- "$LMBENCH_FSDIR"
    [ -d "$LMBENCH_FSDIR" ] && [ ! -L "$LMBENCH_FSDIR" ] &&
        [ -O "$LMBENCH_FSDIR" ] ||
        die "$LMBENCH_FSDIR is not owned by the current user; set LMBENCH_FSDIR to a private directory"
    chmod 0700 "$LMBENCH_FSDIR"
fi
export LMBENCH_FSDIR

if [ "$command" = prepare ]; then
    if [ "$reconfigure" = yes ] || [ ! -f "$config_path" ]; then
        (cd "$workdir/src" && ../scripts/config-run)
    fi
    printf 'lmbench workspace prepared at %s\n' "$workdir"
    printf 'Configuration: %s\n' "$config_path"
    exit 0
fi

[ -f "$config_path" ] ||
    die "no configuration found; run 'lmbench prepare' first"

results="$workdir/results/$arch"
mkdir -p -- "$results"
base="$results/$(uname -n)"
ext=0
while ! (set -C && : > "$base.$ext") 2>/dev/null; do
    [ -e "$base.$ext" ] || die "cannot create $base.$ext"
    ext=$((ext + 1))
done

printf 'lmbench: writing results to %s\n' "$base.$ext"
cd "$workdir/bin/$arch"
PATH=".:$PATH"
export PATH
exec ./lmbench "$config" 2>"$base.$ext"
