#! /bin/sh

#
# patch-source                                                  (jh,12.04.2004)
#

#
#   patch-source: generates and patches the source from a recipe
#   Copyright (C) 2004  Jochen Hepp <jochen.hepp@gmx.de>
#
#   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#



# --- definitions ---

src_prefix=.



# --------- usage ---------

print_usage () {
	cat <<EOF
Usage: $script [OPTIONS] FILE ...

       FILE                recipe of sources to use and patches to apply

       --src-prefix=DIR    prefix of source directory
                           [default=$src_prefix]
       --[no-]verify-gpg   [don't] use gpg to verify signatures
                           [default=$verify_gpg]
       --trustlevel=LEVEL  set needed trustworthy of a signature:
                           undefined, never, marginal, fully or ultimate
                           [default=$trustdefault]

       -V  --version       display version number
       -h  --help          display this help and exit
EOF
}



# --------- version ---------

print_version () {
	cat <<-EOF
		$script $version

		Copyright (C) 2004 Jochen Hepp
		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.

		Written by Jochen Hepp <jochen.hepp@gmx.de>.
EOF
}



# --------- config ---------

config () { # destdir   global: src_prefix, verfiy_gpg, trustdefault
	local destdirdefault="$1"
	local srcdir="$src_prefix"
	local destdir=''
	local checkdestdir=''
	local file
	local line
	local cmd
	local verifytmp
	local trustlevel="$trustdefault"
	local keylongid=''
	local keyname=''
	local keyemail=''
	local gpg_status=''
	local gpg_info=''
	local cwd=''

	if [ "$verify_gpg" != 'no' ]; then
		gpg_status="$(mktemp)"
		gpg_info="$(mktemp)"
		if [ ! -f "$gpg_status" -o ! -f "$gpg_info" ]; then
			error "unable to create temporary files"
			exit 1
		fi
	fi


	while read line; do
		case "$line" in
			\#*)
				line="`echo \"$line\" | sed 's/^# *//; s/ .*$//'`"
				if [ "$line" -a -z "$destdir" ]; then
					destdir="$line"
					checkdestdir=''
				fi
				;;

			trustlevel=*)
				line="${line#trustlevel=}"
				if [ `trustlevel "$line"` -gt `trustlevel "$trustdefault"` ]; then
					trustlevel="$line"
				else
					trustlevel="$trustdefault"
				fi
				;;

			keylongid=*|keyname=*|keyemail=*)
				eval "`echo \"${line%%=*}\" | sed 's/-/_/g'`=\"${line##*=}\""
				;;

			dir=*)
				destdir="${line#dir=}"
				checkdestdir=''
				;;

			src=*)
				srcdir="${line#src=}"
				if [ "$src_prefix" -a "${src_prefix%/}" != '.' ]; then
					case "$srcdir" in
						/*|\
						http://*|\
						ftp://*) ;;
						*)  srcdir="${src_prefix%/}/$srcdir" ;;
					esac
				fi
				;;

			cmd=*)
				cmd="${line#cmd=}"
				if [ -z "$checkdestdir" ]; then
					if [ -z "$destdir" ]; then
						destdir="$destdirdefault"
					fi
					makedestdir "$destdir"
					checkdestdir='ok'
				fi

				echo "  Command  $cmd"
				cwd="$PWD"
				cd "$destdir" || \
				{	error "$destdir: no such directory";
					exit 1; }
				if ! eval "$cmd"; then
					error "$cmd: command failed"
					exit 1
				fi
				cd "$cwd"
				;;

			*)
				if [ -z "$line" ]; then continue; fi
				if [ -z "$checkdestdir" ]; then
					if [ -z "$destdir" ]; then
						destdir="$destdirdefault"
					fi
					makedestdir "$destdir"
					checkdestdir='ok'
				fi

				file="$line"
				if [ "$srcdir" -a "${srcdir%/}" != '.' ]; then
					case "$file" in
						/*|\
						http://*|\
						ftp://*) ;;
						*)  file="${srcdir%/}/$file" ;;
					esac
				fi

				verifytmp=''
				if [ "$verify_gpg" != 'no' ]; then
					verify "$file" "$trustlevel" \
					       "$keylongid" "$keyname" "$keyemail" \
					       "$gpg_status" "$gpg_info"
					# sets verifytmp
				fi
				auto "$file" "$verifytmp"
				;;
		esac
	done


	if [ "$verify_gpg" != 'no' ]; then
		rm -f "$gpg_status" "$gpg_info"
	fi
}



# --------- makedestdir ---------

makedestdir () { # destdir
	if [ -z "$destdir" ]; then
		destdir="$1"
	fi
	echo "# $destdir"
	makedir "$destdir"
}



# --------- auto ---------

auto () { # file, tmp   global: destdir
	local file_orig="$1"
	local file="$2"
	local name="${file_orig##*/}"
	local pipe=''
	local cwd=''
	local tmpdir

	if [ -z "$file" ]; then
		file="`get_file \"$file_orig\"`"
	fi

	if [ ! -r "$file" ]; then
		error "$file: can't read file"
		exit 1
	fi


	case "$name" in
		*.tgz|\
		*.tar.gz)  pipe='gunzip -dc' ;;
		*.tbz|\
		*.tbz2|\
		*.tar.bz|\
		*.tar.bz2) pipe='bunzip2 -dc' ;;
		*.tar.Z)   pipe='uncompress -c' ;;
		*.tar.zip) pipe='unzip -c' ;;
		*.tar)     pipe='cat';;
	esac

	if [ "$pipe" ]; then
		tmpdir="`env TMPDIR=\"$destdir\" mktemp -d`"
		if [ -z "$tmpdir" ]; then
			error "unable to create temporary directory"
			exit 1
		fi

		echo -n "Extracting $file_orig ..."
		$pipe < "$file" | tar -C "$tmpdir" -xf -
		if [ "`find \"$tmpdir\" -mindepth 1 -maxdepth 1 -type d -print | \
		       wc -l`" -ne 1 ]; then
			error "$file_orig: does not contain exactly one directory"
			exit 1
		else
			find "$tmpdir" -mindepth 2 -maxdepth 2 -print0 | \
			xargs -0 mv --target-directory "$destdir"
		fi
		find "$tmpdir" -depth -type d -exec rmdir "{}" \;
		echo " done."
		return
	fi


	case "$name" in
		patch*.gz|\
		*.diff.gz)   pipe='gunzip -dc' ;;
		patch*.bz2|\
		*.diff.bz2)  pipe='bunzip2 -dc' ;;
		patch*.Z|\
		*.Z)         pipe='uncompress -c' ;;
		patch*.zip|\
		*.zip)       pipe='unzip -c' ;;
		patch*|\
		*.patch|\
		*.diff)      pipe='cat' ;;
		*)
			error "$file_orig: unknown suffix"
			exit 1
			;;
	esac

	cwd="$PWD"
	echo -n "  Applying $file_orig ..."
	cd "$destdir" ||  \
	{	error "$destdir: no such directory";
		exit 1; }
	( cd "$cwd" && $pipe < "$file" ) | \
	patch -p1 -s

	if [ $? -ne 0 ]; then
	 	echo " failed."
		exit 1
	fi

	if [ "`find . '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ]; then
	 	echo " failed."
		error "Aborting.  Reject files found."
		exit 1
	fi
	find . '(' -name '*.orig' -o -name '.*.orig' ')' -print0 | \
	xargs -0 rm -f

 	echo " done."

	if [ "$file" != "$file_orig" ]; then
		unget_file "$file"
	fi

	cd "$cwd"
}



# --------- verify ---------

verify () { # file, trustlevel, keylongid, keyname, keyemail, tmp1, tmp2
            # global: verbose, verifytmp
	local file_orig="$1"
	local trustlevel="$2"
	local keyid="$3"
	local keyname="$4"
	local keyemail="$5"
	local gpg_status="$6"
	local gpg_info="$7"
	local file
	local ext
	local signfile_orig
	local signfile_tmp
	local signfile
	local gpgargs
	local line
	local trust
	local siglongid
	local signame
	local sigemail

	for ext in sign asc; do
		signfile_orig="$file_orig.$ext"
		signfile_tmp="`get_file \"$signfile_orig\"`"
		if [ -r "$signfile_tmp" ]; then
			signfile="$signfile_tmp";
			break
		fi
	done

	if [ -z "$signfile" ]; then
		error "$file_orig: no signature file found"
		exit 1
	fi

	file="`get_file \"$file_orig\"`"

	if [ ! -r "$file" ]; then
		error "$file: can't read file"
		exit 1
	fi

	if [ "$verbose" ]; then
		gpgargs=--verbose
	else
		gpgargs=--quiet
	fi

	exec 7>"$gpg_status" 8>"$gpg_info"

	if ! gpg --no-options --batch --no-tty "$gpgargs" \
	         --status-fd 7 --logger-fd 8 \
	         --verify "$signfile" "$file"; then
		exec 7>&- 8>&-
		error "$signfile_orig: signature not trusted"
		cat "$gpg_info" >&6
		rm -f "$gpg_status" "$gpg_info"
		exit 1

	else
		exec 7>&- 8>&-
		trust=''
		siglongid=''
		signame=''
		sigemail=''
		exec 8<&0 <"$gpg_status"
			while read line; do
				set -- $line
				if [ "$1" = '[GNUPG:]' ]; then
					case "$2" in
						TRUST_*) trust="${2#TRUST_}" ;;
						GOODSIG)
							siglongid="`echo \"$3\" | sed 'y/ABCDEF/abcdef/'`"
							shift 3
							signame="`echo \"$*\" | sed 's/<.*$//'`"
							sigemail="`echo \"$*\" | sed 's/^.*<//; s/>$//'`"
							;;
					esac
				fi
			done
		exec 0<&8 8<&-
		rm -f "$gpg_status"

		if [ `trustlevel "$trust"` -lt `trustlevel "$trustlevel"` ]; then
			error "$signfile_orig: signature not trusted (trust to small)"
			cat "$gpg_info" >&6
			rm -f "$gpg_info"
			exit 1
		fi
		rm -f "$gpg_info"

		if [ "$keylongid" ]; then
			keylongid="`echo \"$keylongid\" | sed 'y/ABCDEF/abcdef/'`"
			if [ "$keylongid" != "$siglongid" ]; then
				error "$keylongid <> $siglongid: long ids of signing key differ"
				exit 1
			fi
		fi
		if [ "$keyname" -a "$keyname" != "$signame" ]; then
			error "$keyname <> $signame: names of signing key differ"
			exit 1
		fi
		if [ "$keyemail" -a "$keyemail" != "$sigemail" ]; then
			error "$keyemail <> $sigemail: email addresses of signing key differ"
			exit 1
		fi
	fi

	if [ "$signfile" != "$signfile_orig" ]; then
		unget_file "$signfile"
	fi

	verifytmp="$file"
}



# --------- trustlevel ---------

trustlevel () { # trustlevel
	local trustlevel="$1"
	local value=-2;

	case "$trustlevel" in
		UNDEFINED|undefined) value=-1 ;;
		NEVER|never)         value=0 ;;
		MARGINAL|marginal)   value=1 ;;
		FULLY|fully)         value=2 ;;
		ULTIMATE|ultimate)   value=3 ;;
	esac

	echo "$value"
}



# --------- get_file ---------

get_file () { # file
	local file_orig="$1"
	local file="$file_orig"

	case "$file_orig" in
		http://*|\
		ftp://*)
			file="$(mktemp)"
			if [ ! -f "$file" ]; then
				error "unable to create temporary file"
				exit 1
			fi
			if [ ! -s "$file" ]; then
				wget -q -O "$file" "$file_orig" >&5 2>&6
			fi
			if [ ! -s "$file" ]; then
				rm -f "$file"
			fi
			;;
	esac

	echo "$file"
}



# --------- unget_file ---------

unget_file () { # file
	local file="$1"

	if [ -f "$file" ]; then
		rm -f "$file"
	fi
}



# --------- makedir ---------

makedir () { # dir
	local dir="$1"

	if [ ! -d "$dir" ]; then
		mkdir "$dir" || \
		{	error "$dir: unable to create directory";
			exit 1; }
	fi
}



# --------- error ---------

error () { # error
	echo "$script: $1" 1>&6
}



# --------- main ---------

# main {
	script="${0##*/}"
	version='0.0.5'
	filefound=''

	# file descriptors
	# 5: output
	# 6: warnings
	exec 5>&1 6>&2

	if [ "$SRC_PREFIX" ]; then
		src_prefix="$SRC_PREFIX"
	fi

	verify_gpg='yes'
	trustdefault='marginal'

	if [ $# -lt 1 ]; then
		print_usage >&6
		exit 1
	fi

	while [ $# -gt 0 ]; do
		case "$1" in
			--version|-V)
				print_version
				exit 0
				;;
			--help|-h)
				print_usage
				exit 0
				;;
			--src-prefix=*)
				v="${1#--}"
				eval "`echo \"${v%%=*}\" | sed 's/-/_/g'`=\"${1##*=}\""
				;;
			--verify-gpg)
				eval "`echo \"${1#--}\" | sed 's/-/_/g'`=yes"
				;;
			--no-verify-gpg)
				eval "`echo \"${1#--no-}\" | sed 's/-/_/g'`=no"
				;;
			--trustlevel=*)
				trustdefault="${1##*=}"
				if [ `trustlevel "$trustdefault"` -lt -1 ]; then
					error "$trustdefault: unknown trust level"
					exit 1
				fi
				;;
			*)
				srcdir=''
				destdir=''
				if [ ! -r "$1" ]; then
					error "$1: cannot read file"
					exit 1
				else
					exec 9<&0 <"$1"
						config "${1##*/}.d"
					exec 0<&9 9<&-
					filefound='yes'
				fi
				;;
		esac
		shift
	done

	if [ "$filefound" != 'yes' ]; then
		print_usage >&6
		exit 1
	fi

	exec 5>&- 6>&-
# }



# --- end ---

