#!/bin/sh -efu
#
# Copyright (C) 2006  Dmitry V. Levin <ldv@altlinux.org>
# Copyright (C) 2006  Alexey Gladkov <legion@altlinux.org>
#
# gear common shell functions.
#
# This file 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 St, Fifth Floor, Boston, MA 02110-1301, USA.
#

PROG="${0##*/}"
PROG_VERSION='1.0.2'

info()
{
	printf %s\\n "$PROG: $*" >&2
}

fatal()
{
	printf %s\\n "$PROG: $*" >&2
	exit 1
}

quiet=
verbose=
verbose()
{
	[ -n "$verbose" ] || return 0
	info "$@"
}

show_usage()
{
	[ -z "$*" ] || info "$*"
	echo "Try \`$PROG --help' for more information." >&2
	exit 1
}

lineno=
rules=
rules_error()
{
	local lineno_text=
	[ -z "$lineno" ] || lineno_text=" line $lineno"
	fatal "$rules$lineno_text: $*"
}

rules_info()
{
	local lineno_text=
	[ -z "$lineno" ] || lineno_text=" line $lineno"
	info "$rules$lineno_text: $*"
}

# quote argument for sed regexp.
quote_sed_regexp()
{
	local out="$*"
	if [ -z "${out##*[\[\].^\$\\/]*}" ]; then
		out="$(printf %s "$out" |sed -e 's/[].^$[\/]/\\&/g')" ||
			return 1
	fi
	printf %s "$out"
}

# quote argument for shell.
quote_shell_arg()
{
	local out="$*"
	if [ -z "${out##*[\"\$\`\\]*}" ]; then
		out="$(printf %s "$out" |sed -e 's/["$`\]/\\&/g')" ||
			return 1
	fi
	printf %s "$out"
}

traverse_tree()
{
	local tree="$1" && shift
	local path="$1" && shift
	local optional="$1" && shift

	set -- `printf %s "$path" |tr -s / ' '`
	local dir id quoted
	for dir; do
		[ "$dir" != . ] ||
			continue
		quoted="$(quote_sed_regexp "$dir")"
		id="$(git-ls-tree "$tree" "$dir" |
			sed -ne 's/^[^[:space:]]\+[[:space:]]\+tree[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+'"$quoted"'$/\1/p')"
		if [ -z "$id" ]; then
			if [ "$optional" = 1 ]; then
				return 2
			else
				rules_error "tree $dir not found in $tree"
			fi
		fi
		tree="$id"
	done
	printf %s "$tree"
}

# fetch blob by id+name.
cat_blob()
{
	local tree="$1" && shift
	local name="$1" && shift

	local dir_name base_name
	dir_name="$(dirname -- "$name")"
	base_name="$(basename -- "$name")"
	tree="$(traverse_tree "$tree" "$dir_name" 0)" ||
		rules_error "blob $name not found"

	# fetch blob from given tree.
	local blob quoted
	quoted="$(quote_sed_regexp "$base_name")"
	blob="$(git-ls-tree "$tree" "$base_name" |
		sed -ne 's/^[^[:space:]]\+[[:space:]]\+blob[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+'"$quoted"'$/\1/p')"

	[ -n "$blob" ] ||
		rules_error "blob $base_name not found in $tree"

	git-cat-file blob "$blob"
}

check_path()
{
	local name="$1" && shift
	local value="$1" && shift

	[ "$value" != '..' -a \
	  -n "${value##/*}" -a \
	  -n "${value##../*}" -a \
	  -n "${value%%*/..}" -a \
	  -n "${value##*/../*}" ] ||
		rules_error "Invalid $name \"$value\" specified"
}

check_name()
{
	local name="$1" && shift
	local value="$1" && shift

	[ -n "$value" ] ||
		rules_error "Empty $name \"$value\" specified"

	[ -z "$(printf %s "$value" |tr -d '[:alnum:]_.+-')" ] ||
		rules_error "Invalid $name \"$value\" specified"
}

workdir=
tree_id=
rules=
specfile=
pkg_name=
pkg_version=
pkg_release=
find_specfile()
{
	# first try specfile defined in $rules if any.
	if [ -n "$(git-ls-tree "$tree_id" "$rules")" ]; then
		cat_blob "$tree_id" "$rules" >"$workdir/rules"
		# format: "spec: filename"
		specfile="$(sed -ne 's/^spec:[[:space:]]\+\([^[:space:]]\+\)/\1/p' "$workdir/rules")"
		[ `printf %s "$specfile" |wc -l` -le 1 ] ||
			rules_error "Too many specfiles specified"
		[ -z "$specfile" ] ||
			check_path specfile "$specfile"
	fi
	# second try specfile in toplevel tree.
	if [ -z "$specfile" ]; then
		specfile="$(git-ls-tree "$tree_id" |
			sed -ne 's/^[^[:space:]]\+[[:space:]]\+blob[[:space:]]\+[^[:space:]]\+[[:space:]]\+\([^/[:space:]]\+\.spec\)$/\1/p')"
		[ `printf %s "$specfile" |wc -l` -le 1 ] ||
			fatal "Too many specfiles found${GIT_DIR:+ in $GIT_DIR}"
	fi
	[ -n "$specfile" ] ||
		fatal "No specfiles found${GIT_DIR:+ in $GIT_DIR}"
	cat_blob "$tree_id" "$specfile" >"$workdir/specfile"
	pkg_name="$(sed '/^name:[[:space:]]*/I!d;s///;q' "$workdir/specfile")"
	pkg_version="$(sed '/^version:[[:space:]]*/I!d;s///;q' "$workdir/specfile")"
	pkg_release="$(sed '/^release:[[:space:]]*/I!d;s///;q' "$workdir/specfile")"
}

def_spec_pattern='*.spec'
spec_pattern="$def_spec_pattern"
find_specfile_in_cwd()
{
	local prefix="$1" && shift

	local f pattern spec= many_specs=
	for pattern in ${spec_pattern}; do
		for f; do
			[ -z "${f##$pattern}" -o -z "${f%%$pattern}" ] ||
				continue
			[ ! -L "$f" -a -f "$f" ] ||
				continue
			[ -z "$spec" ] ||
				many_specs=1
			spec="$f"
		done
		[ -z "$spec" ] ||
			break
	done
	if [ -z "$spec" ]; then
		info "${prefix}Spec file not found."
	elif [ -n "$many_specs" ]; then
		info "${prefix}Too many spec files - ignored all."
		spec=
	fi

	if [ -n "$spec" ]; then
		printf %s "$spec"
		return 0
	else
		return 1
	fi
}

run_command()
{
	verbose "Executing: $*"
	"$@"
}

opt_check_dir()
{
	local value
	value="$(readlink -ev "$2")" &&
		[ -d "$value" -a -x "$value" ] ||
		fatal "$1: $2: Directory not available"
	printf %s "$value"
}
