#!/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 "power_getopt.awk"

#.begin-str help
# pkg_src_fetch_var - hack for fetching variable values from package,
#   (partially(!!!) emulates bmake). ../../mk/* and bl3.mk are ignored.
#
# usage: pkg_src_fetch_var [OPTIONS] [files...]
# OPTIONS:
# -h               display this help message
# =f <varnames>    list of variable names separated by space.
#                  PKGNAME and PKGPATH are fetched by default
# -s               slave mode for paexec(1)
# PKGPATHs are read from files or stdin, one PKGPATH per line
#
# output format:
#    for success:
#       + <TAB> FIELD1 <TAB> FIELD2 ...FIELD-N
#    for failure:
#       - <TAB> RAW_FIELD1 <TAB> RAW_FIELD2 ... RAW_FIELD-N PKGPATH
#    PKGNAME means PKGNAME[nbPKGREVISION]
#.end-str

###############################################################
BEGIN {
	good_pkgname_re = "^[^${}()]+$" #"^[[:alnum:]_-]+-[[:digit:]]+([.][[:digit:]]+)*$"
}

function get_var_value (varname,                 value, pkgrev){
	value = ""

	if (varname == "PKGNAME"){
		if (read_error == 0 && ! ("PKGNAME" in badvar)){
			if ("PKGNAME" in var)
				value = check("PKGNAME")
			else if ("DISTNAME" in var)
				value = check("DISTNAME")
		}

		if (value == ""){
			error = 1
			return "<badvalue>"
		}

		pkgrev = ""
		if ("PKGREVISION" in var){
			pkgrev_ = var ["PKGREVISION"]
			if (pkgrev_ != "0" && pkgrev_ != "")
				pkgrev = "nb" pkgrev_
		}
		return value pkgrev
	}else if (varname == "PKGPATH"){
		return pkgpath
	}else{
		if (read_error == 0 && ! (varname in badvar)){
			return check(varname)
		}else{
			error = 1
			return "<badvalue>"
		}
	}
}

BEGIN {
	# options and fields
	if (getarg("h")){
		print_help()
		exitnow(0)
	}

	fields     = getarg("f")
	slave_mode = getarg("s")

	if (fields == "")
		fields = "PKGNAME PKGPATH"

	varnames_count = split(fields, varnames)

	# pkgsrcdir
	pkgsrcdir = ENVIRON ["PKGSRCDIR"]
	if (pkgsrcdir == "")
		pkgsrcdir = "/usr/pkgsrc"
}

function print_bad_vars (               j){
	for (j=1; j <= varnames_count; ++j){
		printf "\t%s", var [varnames [j]]
	}
	printf "\t%s\n", pkgpath
}

function print_good_vars (               j){
	for (j=1; j <= varnames_count; ++j){
		printf "\t%s", good_var [varnames [j]]
	}
	printf "\n"
}

function calc_and_print (               i, varname, value){
	error = 0
	for (i=1; i <= varnames_count; ++i){
		varname = varnames [i]
		good_var [varname] = value = get_var_value(varname)
		if (error){
			error = 0
			printf "-"
			print_bad_vars()
			return
		}
	}

	printf "+"
	print_good_vars()
}

# try to get a real PKGNAME...
function check (variable,

				value,left,right,subvarname,subvalue,repl,old_string,new_string)
{
	# fast checks

	# PKGNAME was assigned more than once, i.e. badname?
	if (variable in badvar)
		return ""

	value = var [variable]
	# not set?
	if (value == "")
		return ""

	# try replacements...
	while (match(value, /[$][{][[:alnum:]_.]+(:S\/[^\/]*\/[^\/]*\/)?[}]/)){
		left  = substr(value, 1, RSTART-1)
		right = substr(value, RSTART+RLENGTH)

		# ${VARNAME} found?
		if (value !~ /:S\//){
			# yes!
			subvarname = substr(value, RSTART + 2, RLENGTH - 3)
			subvalue = check(subvarname)

			if (subvalue == ""){
				return ""
			}

			value = left subvalue right
			continue
		}

		# ${VARNAME:S/old_substr/new_substr/} found!
		repl  = substr(value, RSTART + 2, RLENGTH - 4)
		subvarname = repl
		sub(/:.*$/, "", subvarname)
		sub(/^[^:]+:S\//, "", repl)

		subvalue = check(subvarname)
		if (subvalue == ""){
			return ""
		}

#		print "left=" left
#		print "right=" right
#		print "varname=" varname
#		print "repl=" repl
#		print "----"

		match(repl, "/")
		old_string = substr(repl, 1, RSTART-1)
		new_string = substr(repl, RSTART+1)

		# complex old_string?
		if (old_string ~ /[$^\[\]\\]/){
			# yes :-(
			return ""
		}

		# string to regexp
		gsub(/[?{}|()*+.]/, "[&]", old_string)

		# old_string to new_string
		sub(old_string, new_string, subvalue)
#		print "result of substritution: var [" varname "]=" var [varname]
		#
		value = left subvalue right
	}

	# final check
	if (value ~ good_pkgname_re){
		return value
	}

	# :-(
	return ""
}

function trim (s){
	sub(/^[ \t]+/, "", s)
	sub(/[ \t]+$/, "", s)

	return s
}

function dirname (fn){
	if (sub(/\/[^\/]+$/, "", fn))
		return fn
	else
		return "."
}

function process_include (fn, inc, cond_cnt,              ret,varname){
	if (inc ~ /^\//)
		fn = inc
	else
		fn = dirname(fn) "/" inc

#	print "incl:" fn

	while ((ret = getline < fn) > 0){
		# in:  <spaces>VAR=...
		# out: VAR=...
		sub(/^[ \t]+/, "")

		# in:  VAR=123 # assignment
		# out: VAR=123
		sub(/#.*$/, "")

		# in:  VAR=...<spaces>
		# out: VAR=...
		sub(/[ \t]+$/, "")

		if ($1 ~ /^[[:alnum:]_.]+$/ && $2 ~ /=/){
			# in:  VAR =...
			# out: VAR=...
			sub(/[ \t]+/, "")
		}

		if ($1 ~ /[[:alnum:]_.]+[!+?]=/){
			# in:  VAR=123
			# out: VAR= 123
			sub(/=/, "= ")
		}

		if ($1 ~ /^[.]if/) {
			++cond_cnt
			continue
		}

		if ($1 == ".endif") {
			--cond_cnt
			continue
		}

		if ($1 ~ /^[[:alnum:]_.]+!=$/){
			varname = $1
			sub(/!=$/, "", varname)
			badvar [varname] = 1
		}

		if ($1 == ".undef"){
			badvar [$2] = 1
		}

		if (match ($1, /^[[:alnum:]_.]+[?:]?=/)) {
			varname = $1
			sub(/[?:]?=.*$/, "", varname)

			sub(/^[^=]+=/, "", $0)
			$0 = trim($0)

			if (cond_cnt != 0 || (varname in var) && var [varname] != $0){
				# double assignment? -> badvar
				badvar [varname] = 1
			}else{
				# conditional assignments are not remembered
				var [varname] = $0
#				print varname " ---> " var [varname]
			}
			continue
		}

		if (match ($1, /^[[:alnum:]_.]+[+]=/)) {
			varname = $1
			sub(/[+]=.*$/, "", varname)

			sub(/^[^=]+=/, "", $0)
			$0 = trim($0)

			if (cond_cnt != 0){
				# double assignment? -> badvar
				badvar [varname] = 1
			}else{
				# conditional assignments are not remembered
				var [varname] = (var [varname] " " $0)
#				print varname " ---> " var [varname]
			}
			continue
		}

		if ($1 == ".include" &&
			$2 !~ /buildlink3.mk"$/ && $2 !~ /^"[.][.]\/[.][.]\/mk/)
		{
			# recursive .include processing
			if (cond_cnt > 0)
				new_cnt = 10000 # unbalanced .if/.endif? who knows
			else
				new_cnt = 0

			inc = substr($2, 2, length($2)-2)
			sub(/[$][{][.]CURDIR[}]/, top_dir, inc)
			process_include(fn, inc, new_cnt)
		}
	}

	close(fn)

	if (ret < 0){
		read_error = 1
	}
}

{
	gsub(/,/, " ")
	for (i=1; i <= NF; ++i){
		pkgpaths [i] = $i
	}
	pkgpath_cnt = NF

	for (i=1; i <= pkgpath_cnt; ++i){
		pkgpath = pkgpaths [i]
		last_fn = pkgsrcdir "/" pkgpath "/Makefile"

		top_dir = dirname(last_fn)
		read_error = 0
		process_include(".", last_fn, 0)
		calc_and_print()

		delete var
		delete badvar
	}

	if (slave_mode){
		printf "success\n\n"
		fflush()
	}
}
