#!/bin/sh
# $FreeBSD: ports/ports-mgmt/bpkg/files/bpkg.sh.in,v 1.33 2012/11/17 06:00:45 svnexp Exp $

#  Copyright (c) 2007-2012 Andy Kosela <akosela@andykosela.com>
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#  1. Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  2. 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 AUTHOR 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 AUTHOR 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.

trap trap_exit 2
pkg=$@

trap_exit()
{
	echo -e "\n*** SIGINT ***"
	exit 1
}

root()
{
	if [ `id -u` != 0 ]; then
		echo "bpkg: access denied, root privileges required"
		exit 1
	fi
}

check_index()
{
	local re

	re=`uname -r | cut -d. -f1`
	if [ -e /usr/ports/INDEX-$re ]; then
		return 0
	else
		root
		echo "Downloading INDEX file..."
		(export CVSROOT=:pserver:anoncvs@anoncvs.fr.freebsd.org:2401/home/ncvs
		echo "Type anoncvs for CVS password"
		echo ""
		cvs login
		cd /usr && /usr/bin/cvs -Q -f -z3 -R \
		    checkout -A -T -P -l ports
		cd /usr/ports && /usr/bin/cvs -Q -f -z3 -R \
		    update -A -T -P -d Mk Templates Tools
		make fetchindex)
		return 0
	fi
}

info_fn()
{
	info=`pkg_info | cut -d' ' -f1 | grep $e`
}

########################################################################
# Start of main functions

pkg_info-xI()
{
	local e info pwd i re
	re=`uname -r | cut -d. -f1`

	for e in $pkg; do
		info_fn
	
		if [ -z "$info" ]; then
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			echo "*** not installed ***"
			for i in $pwd; do
				echo "$i: `grep "/$i|" /usr/ports/INDEX-$re |
				    cut -d'|' -f4`"
			done
		else
			echo "*** installed ***"
			pkg_info -I $info
		fi
	done
	exit 0
}

pkg_info-xL()
{
	local i

	i=`pkg_info -xL $pkg 2>/dev/null`
	if [ -z "$i" ]; then
		echo "bpkg: no installed ports match pattern(s)"
		exit 1
	fi
	echo "$i"
	exit 0
}

pkg_inf()
{
	pkg_info
	echo "`pkg_info | wc -l` ports installed"
	exit 0
}

pkg_info-xs()
{
	local i

	i=`pkg_info -xs $pkg 2>/dev/null`
	if [ -z "$i" ]; then
		echo "bpkg: no installed ports match pattern(s)"
		exit 1
	fi
	echo "$i"
	exit 0
}

pkg_info-xr()
{
	local e info pwd i re
	re=`uname -r | cut -d. -f1`

	for e in $pkg; do
		info_fn
	
		if [ -z "$info" ]; then
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			for i in $pwd; do
				echo "*** not installed ***"
				echo "$i: `grep "/$i|" /usr/ports/INDEX-$re |
				    cut -d'|' -f4`"
				echo "Information in ports for `echo $i |
				    cut -d/ -f2`:"
				echo "B-deps: `grep "/$i|" \
				    /usr/ports/INDEX-$re | cut -d'|' -f8`"
				echo "R-deps: `grep "/$i|" \
				    /usr/ports/INDEX-$re | cut -d'|' -f9`"
				echo ""
			done
		else
			echo "*** installed ***"
			pkg_info -xrR $info
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			for i in $pwd; do
				echo "Information in ports for $i:"
				echo "B-deps: `grep "/$i|" \
				    /usr/ports/INDEX-$re | cut -d'|' -f8`"
				echo "R-deps: `grep "/$i|" \
				    /usr/ports/INDEX-$re |  cut -d'|' -f9`"
				echo ""
			done
		fi
	done
	exit 0
}

pkg_info-xg()
{
	local i

	i=`pkg_info -xg $pkg 2>/dev/null`
	if [ -z "$i" ]; then
		echo "bpkg: no installed ports match pattern(s)"
		exit 1
	fi
	echo "$i"
	exit 0
}


pkg_info-xf()
{
	local e info pwd i re
	re=`uname -r | cut -d. -f1`

	for e in $pkg; do
		info_fn
	
		if [ -z "$info" ]; then
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			echo "*** not installed ***"
			for i in $pwd; do
				echo "$i: `grep "/$i|" /usr/ports/INDEX-$re |
				    cut -d'|' -f4`"
				echo "Packing list:"
				[ -d /usr/ports/$i ] &&
				    cat /usr/ports/$i/pkg-plist
				echo ""
			done
		else
			pkg_info -f $info
		fi
	done
	exit 0
}

pkg_info-xk()
{
	local i

	i=`pkg_info -xk $pkg 2>/dev/null`
	if [ -z "$i" ]; then
		echo "bpkg: no installed ports match pattern(s)"
		exit 1
	fi
	echo "$i"
	exit 0
}

pkg_info-xD()
{
	local i

	i=`pkg_info -xD $pkg 2>/dev/null`
	if [ -z "$i" ]; then
		echo "bpkg: no installed ports match pattern(s)"
		exit 1
	fi
	echo "$i"
	exit 0
}

pkg_info-x()
{
	local i

	i=`pkg_info -x $pkg 2>/dev/null`
	if [ -z "$i" ]; then
		echo "bpkg: no installed ports match pattern(s)"
		exit 1
	fi
	echo "$i"
	exit 0
}

pkg_ver()
{
	pkg_version -Iv
	exit 0
}

pkg_path()
{
	local e pwd i re
	re=`uname -r | cut -d. -f1`

	for e in $pkg; do
		pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
		    cut -d/ -f4,5`
		for i in $pwd; do
			echo "$i: `grep "/$i|" /usr/ports/INDEX-$re |
			    cut -d'|' -f4`"
		done
	done
	exit 0
}

pkg_makefile()
{
	local pwd re
	re=`uname -r | cut -d. -f1`

	pwd=`grep "/$pkg|" /usr/ports/INDEX-$re | sed -n 1p | cut -d'|' -f2`
	[ -z "$pwd" ] &&
	    echo "bpkg: can't find the port in /usr/ports/" && exit 0

	[ -d $pwd ] && less $pwd/Makefile 2>/dev/null
	exit 0
}

pkg_distinfo()
{
	local pwd e re
	re=`uname -r | cut -d. -f1`

	pwd=`grep "/$pkg|" /usr/ports/INDEX-$re | sed -n 1p | cut -d'|' -f2`
	[ -z "$pwd" ] && echo "bpkg: can't find the port in /usr/ports/"

	for e in `cat $pwd/distinfo 2>/dev/null | grep SIZE |
	    cut -d'=' -f2`; do
		echo "`cat $pwd/distinfo | grep $e |
		    sed 's/SIZE// ; s/(// ; s/)//' |
		    cut -d' ' -f2` `convert $e`"
	done
	exit 0
}

make_search()
{
	local e

	for e in $pkg; do
		cd /usr/ports && make search path=/$e$
	done
	exit 0
}

make_search_info()
{
	local e

	for e in $pkg; do
		cd /usr/ports && make search info=$e
	done
	exit 0
}

pkg_releaseftp()
{
	sys=`uname -r | cut -d- -f1`
	branch='-release'
	pkg_ftp
}

pkg_stableftp()
{
	sys=`uname -r | cut -d. -f1`
	branch='-stable'
	pkg_ftp
}

pkg_currentftp()
{
	sys=''
	branch=current
	pkg_ftp
}

pkg_ftp()
{
	local arch site e n size_pkg size mtime

	arch=`uname -m`
	site='ftp.freebsd.org/pub/FreeBSD/ports'

	[ -e /tmp/bpkg*.tmp ] && rm -f /tmp/bpkg*.tmp 1>/dev/null 2>&1
	for e in $pkg; do
		echo "ls $e*" >> /tmp/bpkg.tmp
	done
	sed -i '' s/[0-9][0-9].*/*/ /tmp/bpkg.tmp

	echo "$site/$arch/packages-${sys}$branch/Latest/"
	echo "Loading data..."
	ftp -4Va ftp://$site/$arch/packages-${sys}$branch/Latest/ < /tmp/bpkg.tmp > /tmp/bpkg1.tmp
	ftp -4Va ftp://$site/$arch/packages-${sys}$branch/All/ < /tmp/bpkg.tmp > /tmp/bpkg2.tmp

	n=1
	for e in `cat /tmp/bpkg1.tmp | awk '{print $9}' | cut -d. -f1`
	do
		size_pkg=`cat /tmp/bpkg1.tmp | sed '/^[A-Z]/d' | sed -n ${n}p |
		    awk '{print $11}' | cut -d/ -f3`
		size=`cat /tmp/bpkg2.tmp | grep $size_pkg | awk '{print $5}'`
		mtime=`cat /tmp/bpkg2.tmp | grep $size_pkg |
		    awk '{print $6, $7, $8}'`
		echo "$e -> $size_pkg `convert $size` [$mtime]"
		n=`expr $n + 1`
	done
	rm -f /tmp/bpkg*.tmp
	exit 0
}

pkg_clean()
{
	root
	echo "Cleaning /usr/ports/distfiles/*"
	rm -rf /usr/ports/distfiles/* 1>/dev/null 2>&1
	echo "Done."
	exit 0
}

pkg_clean2()
{
	root
	echo "Cleaning /usr/ports/distfiles/* and /usr/ports/*/*/work"
	rm -rf /usr/ports/distfiles/* 1>/dev/null 2>&1
	rm -rf /usr/ports/*/*/work 1>/dev/null 2>&1
	echo "Done."
	exit 0
}

pkg_which2()
{
	grep "$pkg"$ /var/db/pkg/*/+CONTENTS |
	    awk -F 'pkg/' '{ print $2 }' |
	    sed '/@dirrm/ d ; /@unexec/ d ; /@comment/ d ;
	    s/\/+CONTENTS//g ; s/:@comment .*ORIGIN//g' |
	    awk -F: '{ print $1 ":\t" $2 }'
	exit 0
}

pkg_backup()
{
	local z pkg_install e i

	root
	z=`pkg_info -xoQ $pkg 2>/dev/null`

	if [ -z "$z" ]; then
        	echo "bpkg: no installed ports match pattern(s)"
		exit 0
	fi

	pkg_install=`pkg_info -xoQ ^$pkg | cut -d: -f1 | awk '{ORS=" " ;print}'`
	echo "Creating backup package(s) "$pkg_install"in /usr/ports/packages/bpkg"
	[ ! -d /usr/ports/packages/bpkg ] && mkdir -p /usr/ports/packages/bpkg
	cd /usr/ports/packages/bpkg

	for e in $pkg; do
		i=`pkg_info -xoQ ^$e | cut -d: -f1`
		pkg_create -xb $i 1>/dev/null 2>&1
	done
	echo "Done."
	exit 0
}	

pkg_backup_all()
{
	local e

	root
	echo "Creating backup packages in /usr/ports/packages/bpkg"
	[ ! -d /usr/ports/packages/bpkg ] && mkdir -p /usr/ports/packages/bpkg
	cd /usr/ports/packages/bpkg

	for e in `pkg_info -Ea`; do
		pkg_create -b "$e" 1>/dev/null 2>&1
	done
	echo "Done."
	exit 0
}

pkg_time()
{
	/bin/ls -lUTt /var/db/pkg/ | grep -v total |
	    awk -F' ' '{ print $6, $7, $8, $9, "\t" $10 }' |
	    cut -d/ -f1,5
	exit 0
}

make_showc()
{
	local e info pwd i key re
	re=`uname -r | cut -d. -f1`

	for e in $pkg; do
		info_fn

		if [ -z "$info" ]; then
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			for i in $pwd; do
				echo "*** not installed ***"
				echo "$i: `grep COMMENT \
				    /usr/ports/$i/Makefile | cut -d= -f2`"
				echo -n "Do you want to display config options recursively? [n] "
				read key
				case $key in
				y) cd /usr/ports/$i &&
				    make showconfig-recursive ;;
				*) cd /usr/ports/$i && make showconfig ;
				esac
				echo ""
			done
		else
			for i in $info; do
				echo "*** installed ***"
				pkg_info -xI $i
				echo -n \
				"Do you want to display config options recursively? [n] "
				read key
				case $key in
				y) cd /usr/ports/$i &&
				    make showconfig-recursive ;;
				*) cd /usr/ports/$i && make showconfig ;;
				esac
				echo ""
			done
		fi
	done
	exit 0
}

make_rmc()
{
	local e info pwd i key re

	root
	re=`uname -r | cut -d. -f1`
	for e in $pkg; do
		info_fn
	
		if [ -z "$info" ]; then
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			for i in $pwd; do
				echo "*** not installed ***"
				echo "$i: `grep COMMENT \
				    /usr/ports/$i/Makefile | cut -d= -f2`"
				echo -n \
				"Do you want to remove config options recursively? [n] "
				read key
				case $key in
				y) cd /usr/ports/$i && \
				    make rmconfig-recursive ;;
				*) cd /usr/ports/$i && make rmconfig ;;
				esac
				echo ""
			done
		else
			for i in $info; do
				echo "*** installed ***"
				pkg_info -xI $i
				echo -n \
				"Do you want to remove config options recursively? [n] "
				read key
				case $key in
				y) cd /usr/ports/$i &&
				    make rmconfig-recursive ;;
				*) cd /usr/ports/$i && make rmconfig ;;
				esac
				echo ""
			done
		fi
	done
	exit 0
}

make_config()
{
	local e info pwd i key re

	root
	re=`uname -r | cut -d. -f1`
	for e in $pkg; do
		info_fn

		if [ -z "$info" ]; then
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			for i in $pwd; do
				echo "*** not installed ***"
				echo "$i: `grep COMMENT \
				    /usr/ports/$i/Makefile | cut -d= -f2`"
				echo -n \
				"Do you want to set config options recursively? [n] "
				read key
				case $key in
				y) cd /usr/ports/$i &&
				    make config-recursive ;;
				*) cd /usr/ports/$i && make config ;;
				esac
				echo ""
			done
		else
			for i in $info; do
				echo "*** installed ***"
				pkg_info -xI $i
				echo -n \
				"Do you want to set config options recursively? [n] "
				read key
				case $key in
				y) cd /usr/ports/$i &&
				    make config-recursive ;;
				*) cd /usr/ports/$i && make config ;;
				esac
				echo ""
			done
		fi
	done
	exit 0
}

pkg_freshports()
{
	local browser pwd

	browser=`ls /usr/local/bin/w3m 2>/dev/null ||
	    ls /usr/local/bin/lynx 2>/dev/null ||
	    ls /usr/local/bin/links 2>/dev/null`
	if [ -z "$browser" ]; then
		echo "bpkg: text WWW browser required"
	else
		echo "Loading..."
		pwd=`cd /usr/ports && make search path=/$pkg$ | grep -w Path |
		    sed -n 1p | cut -d/ -f4,5`
		$browser http://www.freshports.org/$pwd
	fi
	exit 0
}

show_work()
{
	local e info pwd i re

	if [ ! -x /usr/local/sbin/portmaster ]; then
		echo "bpkg: you don't have portmaster installed.  Exiting."
		exit 1
	fi

	re=`uname -r | cut -d. -f1`
	for e in $pkg; do
		info_fn
		if [ -z "$info" ]; then
			pwd=`grep "/$e|" /usr/ports/INDEX-$re | cut -d'|' -f2 |
			    cut -d/ -f4,5`
			for i in $pwd; do
				/usr/local/sbin/portmaster --show-work $i
			done
		else
			for i in $info; do
				/usr/local/sbin/portmaster --show-work $i
			done
		fi
	done
	exit 0
}

make_ldd()
{
	local e info i pkg bin o origin

	for e in $pkg; do
		info_fn
	
		if [ -z "$info" ]; then
			echo "bpkg: no installed ports match $e"
		else
			echo "*** installed ***"
			pkg_info -xI $info
			echo ""
			echo "List of dynamic object dependencies:"
			bin=`pkg_info -L $info |
			    awk '/\/usr\/local\/bin/ \
			    /\/usr\/local\/sbin/ \
			    /usr\/local\/libexec/'`
			for o in $bin; do
				/usr/bin/ldd -a $o
			done
		fi
	done
	exit 0
}

make_ldd_all()
{
	root
	local e libs i

	echo "List of all missing dynamic object dependencies:"

	find /bin 2>/dev/null > /tmp/ldd_all
	find /sbin 2>/dev/null >> /tmp/ldd_all
	find /usr/bin 2>/dev/null >> /tmp/ldd_all
	find /usr/sbin 2>/dev/null >> /tmp/ldd_all
	find /usr/libexec 2>/dev/null >> /tmp/ldd_all
	find /usr/local/bin 2>/dev/null >> /tmp/ldd_all
	find /usr/local/sbin 2>/dev/null >> /tmp/ldd_all
	find /usr/local/libexec 2>/dev/null >> /tmp/ldd_all

	for e in `cat /tmp/ldd_all`; do
		libs=`/usr/bin/ldd -a $e 2>/dev/null | grep found`
		if [ ! -z "$libs" ]; then
			grep "$e"$ /var/db/pkg/*/+CONTENTS |
			    awk -F 'pkg/' '{ print $2 }' |
			    sed '/@dirrm/ d ; /@unexec/ d ;
			    /@comment/ d ; s/\/+CONTENTS//g ;
			    s/:@comment .*ORIGIN//g' |
			    awk -F: '{ print $1 ":\t" $2 }'
			echo "$libs"
			for i in `echo $libs | cut -d' ' -f1 |
			    cut -d. -f1,2`; do
				grep "$i". /var/db/pkg/*/+CONTENTS |
				    awk -F 'pkg/' '{ print $2 }' |
				    sed '/@dirrm/ d ; /@unexec/ d ;
				    /@comment/ d ; s/\/+CONTENTS//g ;
				    s/:@comment .*ORIGIN//g' |
				    awk -F: '{ print $2 "\t [" $1 "]" }'
			done
			echo ""
		fi
	done
	rm /tmp/ldd_all
	exit 0
}

convert()
{
	local kilo kiloint mega megaint giga gigaint

	kilo=`echo "scale=1; $1/1024" | bc`
	kiloint=`echo "$1/1024" | bc`

	mega=`echo "scale=1; $kilo/1024" | bc`
	megaint=`echo "$kilo/1024" | bc`

	giga=`echo "scale=1; $mega/1024" | bc`
	gigaint=`echo "$mega/1024" | bc`

	if [ $kiloint -lt 1 ]; then
		echo "$1 bytes"
	elif [ $megaint -lt 1 ]; then
		echo "${kilo}K"
	elif [ $gigaint -lt 1 ]; then
		echo "${mega}M"
	else
		echo "${giga}G"
	fi
}	

help()
{
	echo "bpkg version `pkg_info -xI bpkg | cut -d- -f2 |
	    cut -d' ' -f1`"
	echo ""
	echo "Usage: bpkg [-abDdEeFfgIijkLMmOopQqrswz] pkg-name ..."
	echo "       bpkg [-BCchltvZ]"
	echo ""
	echo "Please see the bpkg(8) man page for more information"
	exit 0
}

# End of functions
########################################################################

while
getopts a:b:D:d:E:e:F:f:g:I:i:j:k:L:M:m:O:o:p:Q:q:r:s:w:z:BCchltvZ opts
do
	case $opts in
		a) pkg=$OPTARG
		   check_index
		   show_work ;;
		b) pkg=$OPTARG
		   pkg_backup ;;
		D) pkg=$OPTARG
		   check_index
		   pkg_distinfo ;;
		d) pkg=$OPTARG
		   check_index
		   pkg_info-x ;;
		E) pkg=$OPTARG
		   pkg_currentftp ;;
		e) pkg=$OPTARG
		   check_index
		   make_config ;;
		F) pkg=$OPTARG
		   check_index
		   pkg_freshports ;;
		f) pkg=$OPTARG
		   check_index
		   pkg_info-xf ;;
		g) pkg=$OPTARG
		   pkg_info-xg ;;
		I) pkg=$OPTARG
		   check_index
		   make_search ;;
		i) pkg=$OPTARG
		   check_index
		   pkg_info-xI ;;
		j) pkg=$OPTARG
		   check_index
		   make_search_info ;;
		k) pkg=$OPTARG
		   pkg_info-xk ;;
		L) pkg=$OPTARG
		   pkg_info-xL ;;
		M) pkg=$OPTARG
		   pkg_info-xD ;;
		m) pkg=$OPTARG
		   check_index
		   pkg_makefile ;;
		O) pkg=$OPTARG
		   check_index
		   make_rmc ;;
		o) pkg=$OPTARG
		   check_index
		   make_showc ;;
		p) pkg=$OPTARG
		   check_index
		   pkg_path ;;
		Q) pkg=$OPTARG
		   pkg_stableftp ;;
		q) pkg=$OPTARG
		   pkg_releaseftp ;;
		r) pkg=$OPTARG
		   check_index
		   pkg_info-xr ;;
		s) pkg=$OPTARG
		   pkg_info-xs ;;
		w) pkg=$OPTARG
		   pkg_which2 ;;
		z) pkg=$OPTARG
		   make_ldd ;;
		B) pkg_backup_all ;;
		C) pkg_clean2 ;;
		c) pkg_clean ;;
		h) help ;;
		l) pkg_inf ;;
		t) pkg_time ;;
		v) pkg_ver ;;
		Z) make_ldd_all ;;
		*) help ;;
	esac
done

help
