#!/bin/sh
#-*-mode:  sh -*-

# Copyright (c) 2007-2015, Aleksey Cheusov <vle@gmx.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

set -e

. pipestatus

export LC_ALL=C

usage (){
    cat 1>&2 <<EOF
pkg_update_summary - updates pkg_summary(5) efficiently

usage: pkg_update_summary [OPTIONS] summary_file bin_pkg_dir [decomp comp]
OPTIONS:
  -h      display this help message
  -r      keeps only latest version for each pkgpath:pkgbase pair
EOF
}

refresh=cat
while getopts hr f; do
    case "$f" in
	h)  usage; exit 0;;
	r)  refresh=pkg_refresh_summary;;
	?)  printf "Run pkg_update_summary -h for details\n"; exit 2;;
    esac
done
shift `expr $OPTIND - 1`

PKG_SUFX=${PKG_SUFX:=.tgz}
PKG_SUFX_RE="`echo $PKG_SUFX | sed 's,[.],[.],g'`"

PKG_INFO_CMD="${PKG_INFO_CMD:=/usr/sbin/pkg_info -K /usr/pkg/pkgdb}"

if test -z "$TEST_CMD"; then
    TEST_CMD=test
fi

if test $# -eq 2; then
    summary_file="$1"
    bin_pkg_dir="$2"
    uncompress='cat'
    compress='cat'
elif test $# -eq 4; then
    summary_file="$1"
    bin_pkg_dir="$2"
    uncompress="$3"
    compress="$4"
else
    usage
    exit 1
fi

cd "$bin_pkg_dir"

# do not run the command when there is NO input on stdin
xargs_bsdstyle (){
    awk -v cmd="xargs $*" '{print $0 | cmd}'
}

pkgs2summary (){
    xargs_bsdstyle $PKG_INFO_CMD -X
}

get_all_pkgs (){
    # in order to ignore grep's exit status in case
    # binary packages directory is empty
    runpipe_re '0 [01]' ls -1t "$bin_pkg_dir" '|' grep "$PKG_SUFX_RE"'$'
}

get_updated_pkgs (){
    get_all_pkgs |
    while read f; do
	if $TEST_CMD "$f" -nt "$summary_file"; then
	    echo "$f"
	else
	    break
	fi
    done
}

print_uniq (){
    awk '$1 == 1 {print $2}'
}

subtract (){
    runpipe0 \
	env LC_ALL=C sort "$@" '|' \
	env LC_ALL=C uniq -c '|' \
	print_uniq
}

get_unchanged_pkgs (){
#    echo "updated=$updated"
#    echo "all=$all"
    { echo "$all"; echo "$updated"; } | subtract
}

tmpbase="`dirname $summary_file`/`basename $summary_file`.tmp"
tmpfinal="$tmpbase.final"
tmp2="$tmpbase.tmp2"

if test -z "$TRACE"; then
    LIBEXECDIR=${LIBEXECDIR-/usr/pkg/libexec/psu}
    . ${LIBEXECDIR}/sig_handler.sh
    on_exit () { rm -f $tmpfinal $tmp2; }
fi

filter_unchanged (){
    pkgs_fn=$1
    shift

    awk -v pkgs_fn="$pkgs_fn" -v PKG_SUFX_RE="$PKG_SUFX_RE" '
    BEGIN {
	while (0 < (ret = getline < pkgs_fn)){
	    sub(PKG_SUFX_RE "$", "")
	    keep_array [$1] = ""
	}
	if (ret < 0){
	    printf "reading from %s failed\n", pkgs_fn > "/dev/stderr"
	    exit 2
	}

	FS = "="
    }
    NF > 0, NF == 0 {
	if (accu == "")
	    accu = $0
	else
	    accu = accu "\n" $0

	if ($1 == "PKGNAME"){
	    pkgname = $2
	    next
	}

	if (NF > 0){
	    next
	}

        keep = (pkgname in keep_array)

	if (keep){
	    print accu
	}

	accu = ""
    }
    ' "$@"
}

show_debugging_info (){
    if test -n "$TRACE"; then
	printf "$all" > "$tmpbase.all"

	printf "$updated" > "$tmpbase.updated"

	if test -f "$summary_file"; then
	    { echo "$all" | sed 's/[.][^.]*$//'
		sed -n 's/^PKGNAME=//p' "$summary_file"
	    } | subtract > "$tmpbase.removed"
	else
	    printf '' > "$tmpbase.removed"
	fi

	echo "See $tmpbase.{all,updated,removed,final,tmp2} files" 1>&2
    fi
}

update_summary (){
    updated="`get_updated_pkgs`"
    all="`get_all_pkgs`"
    get_unchanged_pkgs > "$tmp2"

    runpipe0 $uncompress "$summary_file" '|' filter_unchanged "$tmp2"
    if test -n "$updated"; then
	runpipe0 echo "$updated" '|' pkgs2summary
    fi
}

if test -f "$summary_file"; then
    runpipe0 \
	update_summary '|' \
	$refresh '|' \
	$compress > "$tmpfinal"
else
    runpipe0 \
	get_all_pkgs '|' \
	pkgs2summary '|' \
	$refresh '|' \
	$compress > "$tmpfinal"
fi

show_debugging_info

if test -z "$TRACE"; then
    mv "$tmpfinal" "$summary_file"
fi
