#!/bin/gawk -f
# aur-graph - print package/dependency pairs

# shellcheck disable=all

# < 0 : if ver1 < ver2
# = 0 : if ver1 == ver2
# > 0 : if ver1 > ver2
function get_vercmp(ver1, ver2, op) {
    command = ("vercmp " ver1 " " ver2)

    if (op == "-" || ver2 == "-") {
        return "true" # dependency is unversioned
    } else if (op == "=") {
        return (ver1 == ver2) # common case
    } else if (op == "<") {
        command | getline
        close(command)
        return ($1 < 0)
    } else if (op == ">") {
        command | getline
        close(command)
        return ($1 > 0)
    } else if (op == "<=") {
        command | getline
        close(command)
        return ($1 <= 0)
    } else if (op == ">=") {
        command | getline
        close(command)
        return ($1 >= 0)
    } else {
        printf("invalid operation\n") > "/dev/stderr"
        exit 1
    }
}

function setopt(opt, default) {
    if(opt ~ /[01]/)
       return opt
    else
       return default
}

function has_valid_arch(attr) {
    i = index(attr, "_")
    return !i || (substr(attr, i+1) in arch)
}

function print2(str1, str2) {
    if (REVERSE)
        printf("%s\t%s\n", str2, str1)
    else
        printf("%s\t%s\n", str1, str2)
}

BEGIN {
    DEPENDS = setopt(DEPENDS, 1)
    MAKEDEPENDS = setopt(MAKEDEPENDS, 1)
    CHECKDEPENDS = setopt(CHECKDEPENDS, 1)
    OPTDEPENDS = setopt(OPTDEPENDS, 0)
    PRINTALL = setopt(PRINTALL, 0)
    REVERSE = setopt(REVERSE, 0)
}

$1 == "pkgbase" {
    pkgbase = $3
    pkgver = ""

    # SRCINFO(5), "Section headers"
    in_split_pkg = 0
    delete(arch)

    # track both the pkgbases themselves and their number of deps
    dep_counts[pkgbase] = 0
}

$1 == "pkgver" && !in_split_pkg {
    pkgver = $3
}

$1 == "arch" {
    arch[$3]
}

index($1, "depends") == 1 && !in_split_pkg {
    if (DEPENDS && has_valid_arch($1)) {
        pkg_deps[pkgbase, ++dep_counts[pkgbase]] = $3
    }
}

index($1, "makedepends") == 1 && !in_split_pkg {
    if (MAKEDEPENDS && has_valid_arch($1)) {
        pkg_deps[pkgbase, ++dep_counts[pkgbase]] = $3
    }
}

index($1, "checkdepends") == 1 && !in_split_pkg {
    if (CHECKDEPENDS && has_valid_arch($1)) {
        pkg_deps[pkgbase, ++dep_counts[pkgbase]] = $3
    }
}

index($1, "optdepends") == 1 && !in_split_pkg {
    if (OPTDEPENDS && has_valid_arch($1)) {
        split($3, optname, ":")  # split optdepends name from description
        pkg_deps[pkgbase, ++dep_counts[pkgbase]] = optname[1]
    }
}


$1 == "pkgname" {
    # SRCINFO(5), "Section headers"
    in_split_pkg = 1

    pkg_map[$3] = pkgbase # node
    ver_map[$3] = pkgver  # weight
}

index($1, "provides") == 1 && has_valid_arch($1) {
    split($3, prov, "=")

    # if provider is unversioned, use pkgver
    if ("2" in prov)
        ver_map[prov[1]] = prov[2]
    else
        ver_map[prov[1]] = pkgver

    # append node
    pkg_map[prov[1]] = pkgbase
}

END {
    _vercmp_exit = 0

    for (pkgbase in dep_counts) {
        # add a loop to isolated nodes (#402)
        print2(pkgbase, pkgbase)

        for (dep = 1; dep <= dep_counts[pkgbase]; dep++) {
            if (PRINTALL) {
                print2(pkgbase, pkg_deps[pkgbase, dep])
                continue
            }
            dep_op = "-" # unversioned / no comparison

            # valid operators (important: <= before <)
            split("<=|>=|<|=|>", cmp, "|")

            # split: fourth argument is gawk extension
            for (i in cmp) {
                split(pkg_deps[pkgbase, dep], dep_split, cmp[i])

                if ("2" in dep_split) {
                    dep_op = cmp[i]
                    break
                }
            }

            if ("1" in dep_split)
                dep_pkgname = dep_split[1]
            else
                exit 2

            if ("2" in dep_split)
                dep_pkgver = dep_split[2]
            else
                dep_pkgver = "-"

            # only print dependency if it was encountered before
            if (dep_pkgname in pkg_map == 0)
                continue

            # run vercmp with provider and versioned dependency
            if (get_vercmp(ver_map[dep_pkgname], dep_pkgver, dep_op)) {
                print2(pkgbase, pkg_map[dep_pkgname])
            } else {
                printf("invalid node: %s %s (required: %s%s)\n",
                       dep_pkgname, ver_map[dep_pkgname], dep_op, dep_pkgver) > "/dev/stderr"
                close("/dev/stderr")

                # delay mismatches to loop end
                _vercmp_exit = 1
            }
        }
    }

    exit _vercmp_exit
}
