#!/usr/pkg/bin/runawk

# 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.

############################################################

#env "LC_ALL=C"

#use "pkgsrc-dewey.awk"
#use "power_getopt.awk"
#use "trim.awk"

############################################################
#.begin-str help
# pkg_cmp_summary - compares two summary files
# usage: pkg_cmp_summary -h
#        pkg_cmp_summary [OPTIONS] summary1 summary2
# OPTIONS:
#   -h         display this help
#   =f <id>    specifies what fields to use for package identifying:
#              "b" -- PKGBASE (the default),
#              "p" -- PKGPATH,
#              "a" -- ASSIGNMENTS.
#   -c         synonym for '-a CVS_CHECKSUM'
#   -d         consider packages different
#              if they have different DEPENDS
#   -b         synonym for '-a BUILD_DATE'
#   -2         output PKGPATHe from summary1 and from summary2 (<>!=)
#   -e         normally a number of duplications is output as a result of
#              comparison if they (duplications) exist.  If -e was
#              applied, = (or !) is output if package from summary2
#              exists (or doesn't exist) in summary1
#   -u         output A at the end of line for automatically
#              installed packages and U for packages installed by user.
#              automatic=yes is expected in summary1
#   =a <fields>   use the specified fields for comparing packages
#                 in addition to package versions. Fields are separated
#                 by space or comma
#   =A <fields>   imply -a and output different fields in
#                 the following lines with leading space
#   =O <fields>   imply -E and output fields just like -A but
#                 don't use them for comparison.
#   -E            if -A and -E applied field values are output even
#                 if they are equal in summary1 and summary2
# DEPRECATED OPTIONS
#   -p         synonym for '-f pab' (deprecated since 2014-06-28)
#   -P         synonym for '-f pa'  (deprecated since 2014-06-28)
#
#.end-str
############################################################

function set_pkg_id(id){
#	print "lalala ", id
	if (index(id, "b")) with_pkgname = 1
	if (index(id, "p")) with_pkgpath = 1
	if (index(id, "a")) with_assigns = 1
	if (id ~ /[^bpa]/)
		abort("invalid argument for 'pkg_cmp_summary -f'")
}

BEGIN {
	if (getarg("h")){
		print_help()
		exit 0
	}

	if (getarg("p"))
		set_pkg_id("pab")
	else if (getarg("P"))
		set_pkg_id("pa")
	else
		set_pkg_id(getarg("f", "b"))

	use_dep         = getarg("d")
	check_existence = getarg("e")
	user_auto       = getarg("u")

	if (getarg("c"))
		add_fields = "CVS_CHECKSUM"
	if (getarg("b"))
		add_fields = add_fields " BUILD_DATE"
	add_fields      = add_fields " " getarg("a", "")

	ADD_fields      = trim_lrc(getarg("A", ""))
	OUT_fields      = trim_lrc(getarg("O", ""))

	output_equal    = getarg("E") || OUT_fields != ""

	add_fields = trim_lrc(add_fields " " ADD_fields)

	output2 = getarg("2")

	# -a
	add_fields_cnt = split(add_fields, add_fields_arr, /[ ,]/)
	for (i=1; i <= add_fields_cnt; ++i)
		add_fields_hash [add_fields_arr [i]] = 1

	# -A
	ADD_fields_cnt = split(ADD_fields, ADD_fields_arr, /[ ,]/)
	for (i=1; i <= ADD_fields_cnt; ++i)
		ADD_fields_hash [ADD_fields_arr [i]] = 1

	# -O
	OUT_fields_cnt = split(OUT_fields, OUT_fields_arr, /[ ,]/)
	for (i=1; i <= OUT_fields_cnt; ++i)
		OUT_fields_hash [OUT_fields_arr [i]] = 1

	#
	ADDOUT_fields_cnt = OUT_fields_cnt + ADD_fields_cnt;

	for (ind=1; ind < ARGC && ARGV [ind] == ""; ++ind);

	if (ARGC != ind+2){
		print_help()
		exit 1
	}

	#
	file1 = ARGV [ind]

	installed_by_user = 1
}

with_pkgname && /^PKGNAME=/ {
	pkgname = trim_lr(substr($0, 9))
	next
}

with_pkgpath && /^PKGPATH=/ {
	pkgpath = trim_lr(substr($0, 9))
	full_pkgpath = pkgpath
	sub(/:.*$/, "", pkgpath)
	next
}

user_auto && FILENAME == file1 && /^automatic=/ {
	installed_by_user = tolower(trim_lr(substr($0, 11))) != "yes"
	next
}

with_assigns && /^ASSIGNMENTS=/ {
	assigns = trim_lr(substr($0, 13))
	next
}

use_dep && /^DEPENDS=/ {
	$0 = substr($0, 9)
	if (FILENAME == file1){
		for (i=1; i <= NF; ++i){
			dep1 [$i] = 0
		}
	}else{
		for (i=1; i <= NF; ++i){
			dep2 [$i] = 0
		}
	}
	next
}

{
	eq_pos = index($0, "=")
	field  = substr($0, 1, eq_pos-1)
	if ((field in add_fields_hash) || (field in OUT_fields_hash))
		field_val [field] = substr($0, eq_pos+1)
}

function get_ua_sufx (pkgbase){
	if (user_auto){
		if (! (pkgbase in pkgbase2ua))
			return " ?"
		else if (pkgbase2ua [pkgbase])
			return " U"
		else
			return " A"
	}else{
		return ""
	}
}

function get_str_val (hash, key){
	if (!(key in hash))
		return ""

	return hash [key]
}

function get_diff_accu (pkgbase,                 diff_accu,f,i,vo,vn){
	# side effect - res variable
	diff_accu = ""
	for (i=1; i <= add_fields_cnt; ++i){
		f = add_fields_arr [i]

		vo = get_str_val(pkgbase_field, (pkgbase SUBSEP f))
		vn = get_str_val(curr_field_val, f)

		if (vo != vn){
			if (res == "=")
				res = "!"
		}
		if (output_equal || vo != vn)
			if (f in ADD_fields_hash)
				diff_accu = diff_accu " " f " " vo "\n " f " " vn "\n"
	}
	for (i=1; i <= OUT_fields_cnt; ++i){
		f = OUT_fields_arr [i]

		vo = get_str_val(pkgbase_field, (pkgbase SUBSEP f))
		vn = get_str_val(curr_field_val, f)

		diff_accu = diff_accu " " f " " vo "\n " f " " vn "\n"
	}

	return diff_accu
}

NF == 0 {
	if (assigns != ""){
		full_pkgpath = full_pkgpath ":" assigns
		assigns = ""
	}

	# ver
	ver = pkgname
	sub(/^.*-/, "", ver)

	# pkgbase
	sub(/-[^-]+$/, "", pkgname)

	# option PKGPATH
	if (with_pkgpath && with_pkgname){
		pkgbase      = pkgpath      " " pkgname
		full_pkgbase = full_pkgpath " " pkgname
	}else if (with_pkgpath){
		pkgbase      = pkgpath
		full_pkgbase = full_pkgpath
	}else{
		pkgbase      = pkgname
		full_pkgbase = pkgname
	}

	# add_fields
	delete curr_field_val
	for (f in field_val)
		curr_field_val [f] = field_val [f]

	# cleaning...
	pkgname = pkgpath = ""
	delete field_val

	#
	if (FILENAME == file1){
		# first file!
		if (user_auto)
			pkgbase2ua [pkgbase] = installed_by_user

		installed_by_user = 1

		if (pkgbase in names){
			if (check_existence){
#				print "???", full_pkgbase, "!", full_pkgpaths [pkgbase]
				exists [pkgbase "-" ver] = full_pkgbase
				exists [pkgbase "-" names [pkgbase]] = full_pkgpaths [pkgbase]
			}
			duplicates [pkgbase] += 1
		}else{
			names [pkgbase] = ver
			full_pkgpaths [pkgbase] = full_pkgbase

			if (use_dep){
				for (dep in dep1){
					depends [pkgbase, ++depends_cnt [pkgbase]] = dep
				}
				delete dep1
			}

			for (f in curr_field_val){
				pkgbase_field [pkgbase, f] = curr_field_val [f]
			}
		}
	}else{
		# second file!
		present [pkgbase] = 0

		if (user_auto){
			ua_sufx = get_ua_sufx(pkgbase)
		}

		if (pkgbase in duplicates){
			if (check_existence){
				if ((pkgbase "-" ver) in exists){
					if (output2){
						_full = exists [pkgbase "-" ver]
						sub(/ .*$/, "", _full)
						if (with_pkgname)
							print "=", _full, full_pkgbase, ver ua_sufx
						else
							print "=", _full, full_pkgbase ua_sufx
					}else{
						if (with_pkgname)
							print "=", full_pkgbase, ver ua_sufx
						else
							print "=", full_pkgbase ua_sufx
					}
				}else{
					if (ver != "")
						print "!", full_pkgbase, ver ua_sufx
					else
						print "!", full_pkgbase ua_sufx
				}
			}

			next
		}

		res = "="

		# -a

		if (! (pkgbase in names)){
			if (ver != "")
				print "+", full_pkgbase, ver
			else
				print "+", full_pkgbase

			if (ADDOUT_fields_cnt >= 1){
				diff_accu = get_diff_accu(pkgbase)
				print diff_accu
			}

			next
		}

		# ! -P
		if (with_pkgname){
			ver1 = names [pkgbase]
			res = dewey_cmp(ver1, ver)
		}

		# -a|-A
		diff_accu = get_diff_accu(pkgbase)

		# -d
		if (use_dep && res == "="){
			if (pkgbase in depends_cnt){
				cnt = depends_cnt [pkgbase]
				for (i=1; i <= cnt; ++i){
					dep = depends [pkgbase, i]
					if (dep in dep2){
						delete dep2 [dep]
					}else{
						res = "!"
						break
					}
				}
				if (res == "="){
					for (dep in dep2){
						res = "!"
						break
					}
				}
			}else{
				res = "!"
			}
			delete dep2
		}

		#
		if (output2){
			_full = full_pkgpaths [pkgbase]
			sub(/ .*$/, "", _full)
			if (with_pkgname)
				print res, _full, full_pkgbase, ver1, ver ua_sufx
			else
				print res, _full, full_pkgbase ua_sufx
		}else{
			if (with_pkgname)
				print res, full_pkgbase, ver1, ver ua_sufx
			else
				print res, full_pkgbase ua_sufx
		}

		if (ADDOUT_fields_cnt >= 1)
			print diff_accu
	}

	next
}

END {
	delete curr_field_val

	for (pkgbase in names){
		if (user_auto){
			ua_sufx = get_ua_sufx(pkgbase)
		}

		if (! (pkgbase in present)){
			if (names [pkgbase] != ""){
				nm = " " names [pkgbase]
			}else{
				nm = ""
			}

			print "-", full_pkgpaths [pkgbase] nm ua_sufx
			if (ADDOUT_fields_cnt >= 1){
				diff_accu = get_diff_accu(pkgbase)
				print diff_accu
			}

			delete duplicates [pkgbase]
		}else if (!check_existence && (pkgbase in duplicates)){
			print duplicates [pkgbase]+1, pkgbase
			if (ADDOUT_fields_cnt >= 1)
				print ""
		}
	}
}
