#!/bin/bash
# aur-repo - operate on local repositories
[[ -v AUR_DEBUG ]] && set -o xtrace
argv0=repo
PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

# default options
db_query=file mode=none conf_args=() vercmp_args=() parse_args=() missing=0

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

usage() {
    printf >&2 'usage: %s [-d repo] [-r path] [-alqtuS]\n' "$argv0"
    exit 1
}

# option parsing
opt_short='c:f:d:i:r:s:F:almqtuJS'
opt_long=('config:' 'database:' 'root:' 'all' 'list' 'path' 'list-path' 'list-repo'
          'search:' 'search-by:' 'list-attr' 'sync' 'upgrades' 'table' 'quiet' 
          'attr:' 'json' 'jsonl' 'format:' 'delim:' 'dbext:' 'missing' 'ignore:' 'ignore-by:')
opt_hidden=('dump-options' 'repo:' 'repo-list' 'path-list' 'attr-list' 'status' 'field:')

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

unset mode list db_ext db_name db_root format status_file pacman_conf format_args vercmp_args attr
while true; do
    case $1 in
        -d|--database|--repo)
            shift; db_name=$1 ;;
        -f|--format)
            shift; format=$1; mode=format ;;
        --delim)
            shift; format_args+=(--delim "$1") ;;
        -r|--root)
            shift; db_root=$1 ;;
        --dbext)
            shift; db_ext=$1 ;;
        -c|--config)
            shift; pacman_conf=$1; conf_args+=(--config "$1") ;;
        -m|--missing)
            missing=1 ;;
        -l|--list)
            mode=list ;;
        -t|--table)
            mode=table ;;
        -J|--json)
            mode=json ;;
        --jsonl)
            mode=jsonl ;;
        -a|--all)
            mode=upgrades; vercmp_args+=(-a) ;;
        -u|--upgrades)
            mode=upgrades ;;
        -q|--quiet)
            parse_args+=(-q); vercmp_args+=(-q) ;;
        -S|--sync)
            db_query=sync ;;
        -F|--attr|--field)
            shift; mode=attr; attr=$1 ;;
        -s|--search)
            shift; parse_args+=(--search "$1") ;;
        --search-by)
            shift; parse_args+=(--search-by "$1") ;;
        -i|--ignore)
            shift; parse_args+=(--ignore "$1") ;;
        --ignore-by)
            shift; parse_args+=(--ignore-by "$1") ;;
        --path)
            mode=path ;;
        --status)
            mode=status ;;
        --path-list|--list-path)
            list=path ;;
        --repo-list|--list-repo)
            list=repo ;;
        --attr-list|--list-attr)
            list=attr ;;
        --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

# assign environment variables
db_ext=${db_ext:-$AUR_DBEXT} db_name=${db_name:-$AUR_REPO} db_root=${db_root:-$AUR_DBROOT}

# option sanity check
if [[ $db_query == 'sync' ]] && [[ $db_root ]]; then
    printf >&2 'warning: ignoring repository root %q with --sync\n' "$db_root"
fi

# parse pacman configuration
declare -A conf_file_serv conf_file_path conf_sync_serv

while read -r key _ value; do
    case $key=$value in
        \[*\]*)
            section=${key:1:-1}
            ;;
        DBPath=*)
            pacman_dbpath=${value%/}
            ;;
        Server=file://*)
            value=${value#file://}

            conf_file_serv[$section]=$value
            conf_file_path[$section]=$value/$section.${db_ext:-db}
            ;;&
        Server=*://*)
            conf_sync_serv[$section]=$value

            if [[ $section == "$db_name" ]] && [[ ! $db_root ]]; then
                db_root=$value
            fi ;;
    esac
done < <(pacman-conf "${conf_args[@]}")
wait $! || exit

# prefix error messages with pacman.conf (#1118)
if [[ -v pacman_conf ]]; then
    err_prefix="$argv0: $pacman_conf"
else
    err_prefix="$argv0"
fi

# existing path is optional for printf modes (#1142)
if (( missing )); then
    realpath_args=(-m)
else
    # require all path components to exist
    realpath_args=(-e)
fi

# list information on available local repositories
case $list in
    path|repo)
        if ! [[ ${!conf_file_path[*]} ]]; then
            printf >&2 '%s: no file:// repository configured\n' "$err_prefix"
            exit 2
        fi ;;&
    path)
        # resolve repo-add symlinks
        realpath "${realpath_args[@]}" -- "${conf_file_path[@]}" || exit 2
        exit 0 ;;
    repo)
        # quote repository name
        printf '%s\n' "${!conf_file_path[@]}"
        exit 0 ;;
    attr)
        aur repo-parse --list-attr  # holds for any repository
        exit ;;
esac

# select local repository from pacman configuration, if no repository
# was specified on the command-line
if [[ ! $db_name ]]; then
    conf_file_repo=("${!conf_file_serv[@]}")

    case ${#conf_file_repo[@]} in
        1) db_root=${conf_file_serv[${conf_file_repo[0]}]}
           db_name=${conf_file_repo[0]}
           ;;
        0) printf >&2 '%s: no file:// repository configured\n' "$err_prefix"
           exit 2
           ;;
        *) printf >&2 '%s: repository choice is ambiguous (use -d to specify)\n' "$err_prefix"

           for db in "${conf_file_repo[@]}"; do
               printf '%q\t%q\n' "$db" "${conf_file_path[$db]}"
           done | column -o $'\t' -t >&2
           exit 1
           ;;
    esac

# check $db_name is a configured pacman repository (local or remote, #1113)
elif ! [[ ${conf_sync_serv[$db_name]} ]]; then
    printf >&2 "warning: repository %s not configured\n" "$db_name"
fi

# basic file checks
case $db_query in
    file)
        if ! [[ $db_root ]]; then
            printf >&2 '%s: database root not specified\n' "$argv0"
            exit 2
        elif [[ $db_root == *://* ]]; then
            printf >&2 '%s: %q: object is remote (use -S to query)\n' "$argv0" "$db_root"
            exit 66
        fi
        db_path=$db_root/$db_name.${db_ext:-db}
        ;;
    sync)
        db_path=$pacman_dbpath/sync/$db_name.${db_ext:-db}
        db_root=$pacman_dbpath/sync
        ;;
esac

# resolve repo-add symlink
db_path=$(realpath "${realpath_args[@]}" -- "$db_path") || exit 2
db_root=$(realpath "${realpath_args[@]}" -- "$db_root") || exit 2

# database operations
case $mode in
    list|table|json|jsonl)
        aur repo-parse "${parse_args[@]}" -p "$db_path" --"$mode"
        ;;
    format)
        aur repo-parse "${parse_args[@]}" -p "$db_path" --jsonl | aur format "${format_args[@]}" -f "$format"
        ;;
    upgrades)
        aur repo-parse "${parse_args[@]}" -p "$db_path" --list  | aur vercmp "${vercmp_args[@]}"
        ;;
    attr)
        aur repo-parse "${parse_args[@]}" -p "$db_path" --attr "$attr"
        ;;
    path)
        printf '%s\n' "$db_path"
        ;;
    *)
        printf 'repo:%s\nroot:%s\npath:%s\n' "$db_name" "$db_root" "$db_path"
        ;;
esac

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