#!/bin/sh -efu

# Copyright (C) 2025 Paul Wolneykien <manowar@altlinux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# See https://www.gnu.org/software/coreutils/faq/coreutils-faq.html#Sort-does-not-sort-in-normal-order_0021.
export LC_ALL=C

PROG=${0##*/}
VERSION='0.4.6'

usage()
{
    [ "$1" = 0 ] || exec >&2
    cat <<EOF
Usage: $PROG -N|-E [ options ] [ *.chksum ]

Options:

  -N, --new-only    don't overwrite checksum data, list each package
                  version only once;

  -E[FILE], --existing-only[=FILE]    overwrite checksum data for
                                    package versions listed in FILE
                                  (or the first *.chksum file given);

  -o OUTPUT, --out=OUTPUT    write the resulting checksums to the given
                             file OUTPUT (the default is stdout);

  -f, --force    overwrite existing checksum files (use -f -o FILE to
                 update inplace);

  -v, --verbose    be verbose;

  -V, --version    print program version and exit;

  -h, --help    show this text and exit.

Report bugs to https://bugzilla.altlinux.org/.
EOF
    exit "${1:-0}"
}

TEMP="$(getopt -n "$PROG" -o NE::o:fvVh -l new-only,existing-only::,out:,force,verbose,version,help -- "$@")" || usage 1
eval set -- "$TEMP"

output=
force=
verbose=
mode=
exfile=
while :; do
    case "$1" in
	-N|--new-only)
	    mode='new'
	    ;;
	-E|--existing-only)
	    mode='existing'
	    shift
	    exfile="$1"
	    ;;
	-o|--output)
	    shift
	    output="$1"
	    ;;
	-f|--force)
	    force=y
	    ;;
	-v|--verbose)
	    verbose=y
	    ;;
        -h|--help)
	    usage 0
            ;;
	-V|--version)
	    cat <<EOF
$VERSION 2025
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
EOF
	    exit 0
	    ;;
        --)
	    shift
	    break
            ;;
        *)
	    echo "$PROG: unrecognized option: $1" >&2
	    usage 1
            ;;
    esac
    shift
done

case "$mode" in
    new)
	;;
    existing)
	if [ -z "$exfile" ]; then
	    if [ $# -eq 0 ]; then
		echo "ERROR: Either specify the reference checksum file together with the -E option or pass one as a positional argument!" >&2
		exit 1
	    else
		exfile="$1"
	    fi
	fi
	;;
    *)
	echo "ERROR: Please, specify either -N or -E mode of operation." >&2
	usage 1
	;;
esac

if [ $# -eq 0 ]; then
    [ -z "$verbose" ] || echo "Reading checksum data from stdin..." >&2
fi

if [ -n "$output" ] && [ -e "$output" ]; then
    if [ -z "$force" ]; then
	echo "ERROR: The specified output checksum file $output already exists!" >&2
	echo "Use -f option to overwrite it." >&2
	exit 1
    fi
fi

workdir=
cleanup()
{
    if [ "${DEBUG:-0}" -eq 0 ]; then
	[ -z "$workdir" ] || rm -rf "$workdir"
    else
	echo "DEBUG: Workdir: $workdir" >&2
    fi
}
trap 'cleanup' EXIT
workdir="$(mktemp -d --tmpdir "$PROG.XXXX")"

case "$mode" in
    new)
	cat <<EOF
function on_pkg(p) {
    if (!(p in pkgs)) {
        print p
    }
}

function on_data(p, d) {
    if (!(p in pkgs)) {
        print d
    }
}

function on_end(p) {
    if (!(p in pkgs)) {
        print
    }
    pkgs[p] = 1
}

EOF
	;;
    existing)
	cat <<EOF
function on_pkg(p) {
    if (p in pkg_data) {
        pkg_data[p] = p
    }
}

function on_data(p, d) {
    if (p in pkg_data) {
        pkg_data[p] = pkg_data[p] "\n" d
    }
}

function on_end(p) {
    if (p in pkg_data) {
        pkg_data[p] = pkg_data[p] "\n"
    }
}

BEGIN {
    n = 0
EOF
	sed -n -e '
/^.*-[^-]\+-[^-]\+\.[^.]\+\.rpm\/[^@\/]\+@[0-9]\+$/ {
  s/"/\\"/g
  s/^.*$/    pkg_data["&"] = ""\n    pkg_order[n++] = "&"/
  p
}
' "$exfile"
	cat <<EOF
}

EOF
	;;
esac >"$workdir"/filter.awk

cat <<EOF >>"$workdir"/filter.awk
/^[^#[:space:]]+\\.rpm\\/[^@/]+@[0-9]+\$/ {
    pkg = \$0
    on_pkg(pkg)
    while (getline > 0) {
        if (\$0 == "") {
            on_end(pkg)
            next
        }
        on_data(pkg, \$0)
    }
    print("Unexpected EOF or error:", ERRNO) > "/dev/stderr"
    exit 1
}
EOF

case "$mode" in
    existing)
	cat <<EOF

END {
    for (i = 0; i < n; i++) {
        print pkg_data[pkg_order[i]]
    }
}
EOF
	;;
esac >>"$workdir"/filter.awk

is_inplace() {
    while [ $# -gt 0 ]; do
	if [ "$1" = "$output" ]; then
	    return 0
	fi
	shift
    done
    return 1
}

tmpout=
if [ -n "$output" ]; then
    if is_inplace "$@"; then
	tmpout="$workdir"/out.chksum
	exec >"$tmpout"
    else
	exec >"$output"
    fi
fi

awk -f "$workdir"/filter.awk "$@"

if [ -n "$tmpout" ]; then
    cat "$tmpout" >"$output"
fi
