#!/bin/bash
# aur-srcver - update and print package revisions
[[ -v AUR_DEBUG ]] && set -o xtrace
shopt -s nullglob
argv0=srcver
PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

# default arguments
# XXX: --skipinteg is an optional argument
makepkg_args=('--nobuild' '--nodeps' '--skipinteg')
num_procs=$(( "$(nproc)" + 2 ))

args_csv() {
    # shellcheck disable=SC2155
    local str=$(printf '%s,' "$@")
    printf '%s' "${str%,}"
}

# shellcheck disable=SC2154
get_full_version() {
    if (( epoch > 0 )); then
        printf "%s\n" "$epoch:$pkgver-$pkgrel"
    else
        printf "%s\n" "$pkgver-$pkgrel"
    fi
}

trap_exit() {
    if [[ ! -v AUR_DEBUG ]]; then
        rm -rf -- "$tmp"
    else
        printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$tmp"
    fi
}

usage() {
    printf >&2 'usage: %s [--no-prepare] <pkgbase> [<pkgbase> ...]\n' "$argv0"
    exit 1
}

# mollyguard for makepkg
if (( UID == 0 )) && [[ ! -v AUR_ASROOT ]]; then
    printf >&2 'warning: aur-%s is not meant to be run as root.\n' "$argv0"
    printf >&2 'warning: To proceed anyway, set the %s variable.\n' 'AUR_ASROOT'
    exit 1
fi

# option parsing
opt_short=a:j:z
opt_long=('arg-file:' 'no-prepare' 'jobs:' 'buildscript:' 'margs:' 'makepkg-args:' 'null')
opt_hidden=('dump-options' 'noprepare')

if opts=$(getopt -o "$opt_short" -l "$(args_csv "${opt_long[@]}" "${opt_hidden[@]}")" -n "$argv0" -- "$@"); then
    eval set -- "$opts"
else
    usage
fi

unset arg_file buildscript mapfile_args
while true; do
    case "$1" in
        -a|--arg-file)
            shift; arg_file=$1 ;;
        --noprepare|--no-prepare)
            makepkg_args+=(--noprepare) ;;
        --makepkg-args|--margs)
            shift; IFS=, read -a arg -r <<< "$1"
            makepkg_args+=("${arg[@]}") ;;
        -j|--jobs)
            shift; num_procs=$1 ;;
        --buildscript)
            shift; buildscript=$1
            makepkg_args+=(-p "$1") ;;
        -z|--null)
            mapfile_args+=(-d $'\0') ;;
        --dump-options)
            printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
            printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
            exit ;;
        --)
            shift; break ;;
    esac
    shift
done

# Single hyphen to denote input taken from stdin
stdin=0
if (( $# == 1 )) && [[ $1 == "-" || $1 == "/dev/stdin" ]]; then
    stdin=1
fi

# shellcheck disable=SC2174
mkdir -pm 0700 -- "${TMPDIR:-/tmp}/aurutils-$UID"
tmp=$(mktemp -d --tmpdir "aurutils-$UID/$argv0.XXXXXXXX") || exit
trap 'trap_exit' EXIT

# A pipeline `foo | bar &` causes `bar` to detach from the script. In
# this case, aur-srcver returns immediately with `makepkg` processes
# still running in the background. Read all input into an array to
# avoid this. (cf. aur-view, #958)
if (( stdin )); then
    mapfile "${mapfile_args[@]}" -t packages

elif [[ -v argfile ]]; then
    mapfile "${mapfile_args[@]}" -t packages <"$arg_file"
else
    packages=("${@:-$PWD}")
    set --
fi

# Remove duplicates in pkgbuild dirs
declare -A seen

i=0 # package counter
for n in "${packages[@]}"; do
    n=$(realpath "$n"); [[ ${seen[$n]} ]] && continue
    seen[$n]=1

    if (( i++ >= num_procs )); then
        wait -n
    fi

    { mkdir -p -- "$tmp/$i"
      echo "$n" > "$tmp/$i"/path
      mpkg_err=0

      #shellcheck disable=SC2086
      env -C "$n" nice -n 20 ${AUR_MAKEPKG:-makepkg} \
          "${makepkg_args[@]}" >"$tmp/$i"/log 2>&1 || mpkg_err=$?

      if (( mpkg_err )); then
          echo "$mpkg_err" >"$tmp/$i"/failed
      fi
    } &
done
wait

declare -A failed
for d in "$tmp"/*/; do # iterate over directories
    i=$(basename "$d") p=$(<"$d"/path)

    if [[ -e $d/failed ]]; then
        failed[$i]=$p
    else
        # Precautions when sourcing the PKGBUILD have no effect here,
        # because makepkg already sourced the PKGBUILD above.
        # shellcheck disable=SC1090
        ( source "$p/${buildscript-PKGBUILD}"

          fullver=$(get_full_version)
          printf '%s\t%s\n' "${pkgbase:-$pkgname}" "$fullver"
        )
    fi
done

for i in "${!failed[@]}"; do
    printf >&2 '%s: makepkg %s failed for path %s with exit %s\n' \
               "$argv0" "${makepkg_args[*]}" "${failed[$i]}" "$(<"$tmp/$i"/failed)"

    cat "$tmp/$i"/log >&2
    printf >&2 '8<----\n'
done

if (( ${#failed[@]} )); then
    exit 1 # E_FAIL
fi

# vim: set et sw=4 sts=4 ft=sh:
