#!/bin/bash
# aur-pkglist - print the AUR package list
[[ -v AUR_DEBUG ]] && set -o xtrace
argv0=pkglist
XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
AUR_LOCATION=${AUR_LOCATION:-'https://aur.archlinux.org'}
AUR_METADEST=${AUR_METADEST:-$XDG_CACHE_HOME/aurutils/$argv0}
PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

# default options
ttl=300 pkglist=packages update=0 verify=0 use_system_time=0

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

http_last_modified() {
    awk -F', ' '/^[Ll]ast-[Mm]odified:/ {print $2}' "$1"
}

list_fetch() {
    curl -A aurutils -f "$1" "${@:2}" --remote-name
}

list_headers() {
    curl -A aurutils -f "$1" --head -Ss
}

usage() {
    printf >&2 'usage: %s [-bqsiv] [-t ttl] [-FP pattern]\n' "$argv0"
    exit 1
}

opt_short='t:bqsivFPu'
opt_long=('pkgbase' 'search' 'info' 'fixed-strings' 'perl-regexp' 'plain'
          'ttl:' 'users' 'verify' 'quiet' 'systime')
opt_hidden=('dump-options' 'time:')

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

unset mode
while true; do
    case "$1" in
        -b|--pkgbase)
            pkglist=pkgbase ;;
        -s|--search)
            pkglist=packages-meta-v1.json ;;
        -i|--info)
            pkglist=packages-meta-ext-v1.json ;;
        --users)
            pkglist=users ;;
        -F|--fixed-strings)
            mode=fixed ;;
        -P|--perl-regexp)
            mode=regex ;;
        --plain)
            mode=plain ;;
        -q|--quiet)
            mode=none ;;
        -t|--time|--ttl)
            shift; ttl=$1 ;;
        -v|--verify)
            verify=1 ;;
        --systime)
            use_system_time=1 ;;
        # Deprecated options
        -u) printf >&2 'deprecation notice: %s -u is an alias for --quiet\n' "$argv0"
            mode=none ;;
        --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

# default to regex if >0 arguments specified
if [[ ! -v mode ]] && (( $# > 0 )); then
    mode=regex
elif [[ ! -v mode ]]; then
    mode=plain
fi

if ! [[ $ttl =~ [0-9]+ ]]; then
    printf >&2 "%s: --ttl requires an integer ('%s' provided)\n" "$argv0" "$ttl"
    exit 1
fi

# packages.gz cache
mkdir -p -- "$AUR_METADEST"

if [[ ! -s $AUR_METADEST/headers_$pkglist || ! -s $AUR_METADEST/$pkglist ]]; then
    update=1

elif [[ -s $AUR_METADEST/$pkglist ]]; then
    sec_l=$(http_last_modified "$AUR_METADEST/headers_$pkglist" | date -f - '+%s')

    if (( use_system_time )); then
        sec_d=$(date '+%s')
    else
        sec_d=$(http_last_modified <(list_headers "$AUR_LOCATION/$pkglist.gz") | date -f - '+%s')
    fi

    if (( sec_d - sec_l > ttl )); then
        update=1
    fi
fi

if (( update )); then
    list_fetch "$AUR_LOCATION/$pkglist.gz" --dump-header "$AUR_METADEST/headers_$pkglist" -o "$AUR_METADEST/$pkglist.gz"

    if (( verify )); then
        list_fetch "$AUR_LOCATION/$pkglist.gz.sha256" -o "$AUR_METADEST/$pkglist.gz.sha256"
        sha256sum --check --strict --quiet "$AUR_METADEST/$pkglist.gz.sha256" || exit
    fi

    if [[ $pkglist == *.json ]]; then
        # Remove newlines separating partial objects, i.e.
        #   [\n{...},\n{...},...\n]  =>  [{...}{...},...]\n
        { gunzip -c "$AUR_METADEST/$pkglist.gz" | tr -d '\n'
          printf '\n'
        } > "$AUR_METADEST/$pkglist"
    else
        gunzip -f "$AUR_METADEST/$pkglist.gz"
    fi
fi >&2

# pattern match
case $mode in
    plain)
        cat "$AUR_METADEST/$pkglist" ;;
    fixed)
        grep -F "$1" "$AUR_METADEST/$pkglist" ;;
    regex)
        grep -P "$1" "$AUR_METADEST/$pkglist" ;;
    none)
        printf '%s\n' "$AUR_METADEST/$pkglist" ;;
esac

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