#!/bin/bash

# ***************************************************************************
# *   Copyright (C) 2005 by Stefano Stabellini                              *
# *   stefano@stabellini.net                                                *
# *   http://www.stabellini.net                                             *
# *                                                                         *
# *   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.             *
# ***************************************************************************/

#add a package dependency to the slack-required
#PACKETDEP is like glibc-2.3.5-i486-5

#converts glibc-2.3.5-i486-5 to 2.3.5-i486-5
completetoversion(){
	NUMFIELDS=`echo "$1" | awk -F - '{print NF}'`
	if test $NUMFIELDS -ge 4
	then
		echo ${1#${1%-*-*-*}-}
	fi
}

#converts glibc-2.3.5-i486-5 to glibc
completetoname(){
	NUMFIELDS=`echo "$1" | awk -F - '{print NF}'`
	if test $NUMFIELDS -ge 4
	then
		echo ${1%-*-*-*}
	fi
}

#input variables
#PACKETDEP complete name of a package dependency
#PACKET short name of the building package
#EXCLUDE short or complete name of dependencies that don't have to be considered
#BUILDDIR /tmp/package-name
#
#output files
#/tmp/tempfile
depadd(){
	local i=0
	local j=0
	for iPACKETDEP in $PACKETDEP
	do
		PACKETREQ="`completetoname $iPACKETDEP`"

		if test $iPACKETDEP && test $PACKETREQ && test $PACKETREQ != $PACKET
		then
			EXCLUDEFLAG=0
			for tt in $EXCLUDE
			do
				if test $tt = $iPACKETDEP || test $tt = $PACKETREQ
				then
					EXCLUDEFLAG=1
				fi
			done
								
			if test $EXCLUDEFLAG -eq 0
			then
			
				#echo "packdep1: $PACKETDEP"
				
				if test $V
				then
					VERSIONREQ="`completetoversion $iPACKETDEP`"
				fi
				
				#echo $PACKETREQ
				
				if test -f $BUILDDIR/install/slack-required
				then
					if test -z "`grep $PACKETREQ $BUILDDIR/install/slack-required`"
					then
						if test $V
						then
							OUTPUT[$i]="$PACKETREQ >= $VERSIONREQ"
						else
							OUTPUT[$i]="$PACKETREQ"
						fi
						i=$(($i + 1))
					fi
				elif test $V
					then
						OUTPUT[$i]="$PACKETREQ >= $VERSIONREQ"
						i=$(($i + 1))
					else
						OUTPUT[$i]="$PACKETREQ"
						i=$(($i + 1))
				fi
			fi
		fi
	done
	if test $i -gt 1 && test $ZENDEP -eq 0
	then
		i=$(($i - 1))
		while test $j  -lt $i
		do
			echo -n "${OUTPUT[$j]} | " >> /tmp/tempfile
			j=$(($j + 1))
		done
		echo "${OUTPUT[$j]}" >> /tmp/tempfile
	elif test $i -eq 1
	then
		echo "${OUTPUT[0]}" >> /tmp/tempfile
	elif test $i -gt 1 && test $ZENDEP -eq 1
	then
		echo "${OUTPUT[0]}" >> /tmp/tempfile
	fi
}


#input variables
#REALFILE an absolute filename that could be a symbolic link
#
#output variables
#PACKETDEP the package in witch the resolved REALFILE is contained
realfile(){
	#echo "realfile: $REALFILE"
	
	while test -L $REALFILE
	do
		DIRTEMP=`dirname $REALFILE`
		cd $DIRTEMP
		BASETEMP=`basename $REALFILE`
		REALFILE=`\ls -la $BASETEMP | awk '{print$NF}'`
		#REALFILE=`echo $REALFILE | awk '{print$2}'`
	done
	case $REALFILE in
	.*)
		DIRTEMP=`dirname $REALFILE`
		cd $DIRTEMP
		BASETEMP=`basename $REALFILE`
		REALFILE=`\ls -la $BASETEMP | awk '{print$NF}'`
	;;
	*);;
	esac
	
	if ! [[ $REALFILE == /* ]]
	then
		REALDIR="`pwd`"
		FINALDIR=""
		while test $REALDIR != /
		do
			if test -L $REALDIR
			then
				DIRDIR=`\ls -la $REALDIR | awk '{print$NF}'`
				case $DIRDIR in
				/*) 
					REALDIR="$DIRDIR"
				;;
				..*)
					DIRTEMP=`dirname $REALDIR`
					cd "$DIRTEMP"
					cd "$DIRDIR"
					REALDIR="`pwd`"
				;;
				*)
					DIRTEMP=`dirname $REALDIR`
					REALDIR="$DIRTEMP/$DIRDIR"
				;;
				esac
			else
				DIRTEMP=`basename $REALDIR`
				FINALDIR="$DIRTEMP/$FINALDIR"
				REALDIR=`dirname $REALDIR`
			fi
		done
		
		if test `pwd` != /var/log/packages
		then
			REALFILE="$FINALDIR$REALFILE"
		fi
	fi
	
	case $REALFILE in
	/*)
		REALFILE=${REALFILE:1}
		;;
	*);;
	esac
	
	cd /var/log/packages
	PACKETDEP=`grep $REALFILE * | cut -d : -f 1 | uniq`
	
	if test -z "$PACKETDEP"
	then
		case $REALFILE in
		lib*)
			REALFILE="`basename "/$REALFILE"`"
			PACKETDEP=`grep $REALFILE * | cut -d : -f 1 | uniq`
			;;
		*);;
		esac
	fi
}

help(){
	echo "usage: ./requiredbuilder <options> <package-dir or package-tgz>"
	echo ""
	echo "where options are:"
	echo "-v		writes version controls"
	echo "-c		checks every single file in the directory tree instead of following the FHS"
	echo "-b		only scans binary files (no scripts at all)"
	echo "-p		doesn't search for perl dependencies"
	echo "-s <dir>	writes a copy of the slack-required to <dir>"
	echo "-y		yes to all questions"
	echo "-n		only prints to stdout, implies -y"
	echo "-z		only prints to stdout a comma separated list of dependencies, implies -y"
}


#################################################################################################
#Start
#################################################################################################


#parameters check
if test $# -lt 1
then
	help
	exit 1
fi

NOSR=0
ZENDEP=0
FHS=1
ONLYBIN=0
# Parse options:
while test $# -gt 1
do
  if [ "$1" = "-v" ]; then
    V=1
    shift 1
  elif [ "$1" = "-y" ]; then
    ALLYES=1
    shift 1
  elif [ "$1" = "-c" ]; then
    FHS=0
    shift 1
  elif [ "$1" = "-b" ]; then
    ONLYBIN=1
    PERLNO=1
    shift 1
  elif [ "$1" = "-n" ]; then
    NOSR=1
    ALLYES=1
    shift 1
  elif [ "$1" = "-p" ]; then
    PERLNO=1
    shift 1
  elif [ "$1" = "-z" ]; then
    ZENDEP=1
    ALLYES=1
    shift 1
  elif [ "$1" = "-s" ]; then
    SOURCE=1
    shift 1
    SBDIR="$1"
    shift 1
  else
    help
    exit 1
  fi
done

case "$1" in
/*) ARG="$1";;
*) ARG="$(pwd)/$1";;
esac

if test -f "$1"
then
	if test -d /tmp/requiredbuilder
	then
		rm -rf /tmp/requiredbuilder
	fi
	mkdir /tmp/requiredbuilder
	cd /tmp/requiredbuilder
	tar xzf "$ARG"
	BUILDDIR=/tmp/requiredbuilder
	INPUTFILE=1
else
	INPUTFILE=0
	BUILDDIR="$ARG"
fi

cd /var/log/packages
> /tmp/tempfile
> /tmp/tempperl
> /tmp/tempperl2

if test ! -f /etc/requiredbuilder.conf
then
	echo "The file /etc/requiredbuilder.conf is missing"
	exit 1
fi
EXCLUDE="`cat /etc/requiredbuilder.conf | sed /^$/d | sed /#/d`"

if test -d "$BUILDDIR" && test ! -d "$BUILDDIR"/install
then
	mkdir "$BUILDDIR"/install
fi
	
if test -f "$BUILDDIR"/install/slack-desc
then
	PACKET="`cat "$BUILDDIR"/install/slack-desc | sed /^$/d | sed /#/d | sed /^" "/d | head -n 1 | cut -d : -f 1`"
elif test `expr match "$BUILDDIR" /tmp/package-` -eq 13
then
	PACKET=`basename $BUILDDIR`
	PACKET=${PACKET#package-}
fi
if test -z $PACKET
then
	PACKET="none"
fi

#for every file in the package
	LIST=`find $BUILDDIR -name '*lib*.so*'`
	BUILDDIR="${BUILDDIR%/}"
	if test $FHS -eq 1
	then
		if test $ONLYBIN -eq 1
		then
			TEMPLIST=`find $BUILDDIR -type f \! -regex $BUILDDIR'/etc/.*' \! -regex $BUILDDIR'/install/.*' \! -regex $BUILDDIR'/usr/doc/.*' \! -regex $BUILDDIR'/usr/share/.*' \! -regex $BUILDDIR'/usr/man/.*' -exec file '{}' \; | grep ELF | cut -f 1 -d ":"`
		else
			TEMPLIST=`find $BUILDDIR -type f \! -regex $BUILDDIR'/etc/.*' \! -regex $BUILDDIR'/install/.*' \! -regex $BUILDDIR'/usr/doc/.*' \! -regex $BUILDDIR'/usr/share/.*' \! -regex $BUILDDIR'/usr/man/.*'`
		fi
	else
		if test $ONLYBIN -eq 1
		then
			TEMPLIST=`find $BUILDDIR -exec file '{}' \; | grep ELF | cut -f 1 -d ":"`
		else
			TEMPLIST=`find $BUILDDIR`
		fi
	fi
	for FILE in $TEMPLIST
	do

#get the filetype using "file"
		#echo "file: $FILE"
		if test $ONLYBIN -eq 0
		then
			FILETYPE=`file -b "$FILE"`
			case $FILE in
			*perl*.pm)
				if test "`echo $FILETYPE | grep text`"
				then
					FILETYPE="perl script"
				fi;;
			*);;
			esac	
			PACKETDEP=""
		fi
		
#if it is an ELF bin
		if test $ONLYBIN -eq 1 || test "`echo $FILETYPE | grep ELF`"
		then

#get the dependencies using "ldd"
#for every dependency check in which packet is included
			ldd "$FILE" 2> /dev/null | while read DEP
			do
				PACKETDEP=""	
				#echo "ldddep: $DEP"
				
				if test -z "`echo $DEP | grep "=> /"`" || test `echo $DEP | wc -w` -lt 4
				then
					if test "`echo $DEP | grep "not found"`"
					then
						TTDEP=`echo $DEP | awk '{print$1}'`
						TTIN=`expr index $TTDEP .so`
						TTIN=$(($TTIN + 2))
						TTRES=${TTDEP:0:$TTIN}
						if test -z "`echo $LIST | grep "/$TTRES"`"
						then
							echo "ldd $FILE: $DEP" >&2
						fi
					fi
					continue
				elif test `echo $DEP | wc -w` -eq 4
				then
					REALFILE=`echo $DEP | awk '{print$3}'`
				fi
				
				realfile

#then add it to the slack-required
				depadd

			done

#get dependencies using strings
			strings "$FILE" |  grep -E '^/(usr/)?bin' | while read DEP
			do
				if test "$DEP" && test -f "$DEP" && [[ $DEP != *sh ]]
				then
					REALFILE="$DEP"
					realfile
					depadd
				fi
			done

#if it is a perl script add the perl dependency
		elif test "`echo $FILETYPE | grep perl`"
		then
			FOUNDPERL=1
			if test ! $PERLNO
			then
				echo $FILE | perl.req >> /tmp/tempperl
			fi
			PACKETDEP=`ls perl-* | cut -f 1 | head -n 1`
			depadd
			
#if it is a python script add the python dependency
		elif test "`echo $FILETYPE | grep python`"
		then
			PACKETDEP=`ls python-* | cut -f 1 | head -n 1`
			depadd
		fi
	done
	
#check for perl dependencies
	if test ! $PERLNO && test $FOUNDPERL
	then	
		PERLLIST=`find /usr/lib/perl*`
		for PERLFILE in $PERLLIST
		do
			if test -f $PERLFILE
			then
				echo $PERLFILE >> /tmp/tempperl2
				echo $PERLFILE | perl.prov >> /tmp/tempperl2
			fi
		done
		cat /tmp/tempperl | while read i
		do
			REALFILE="`grep -B 1 "$i" /tmp/tempperl2 | head -n 1`"
			if test "$REALFILE"
			then
				realfile
			fi
			depadd
		done
	fi
	
#add all the dependecies found to the slack-required
	cat /tmp/tempfile | sort | uniq > /tmp/tempfile2
	if test "`cat /tmp/tempfile2`"
	then
		if test "$ALLYES"
		then
			if test $NOSR -eq 1
			then
				cat /tmp/tempfile2
			elif test $ZENDEP -eq 1
			then
				if test -f "$BUILDDIR"/install/slack-required
				then
					cat /tmp/tempfile2 >> "$BUILDDIR"/install/slack-required
					cat "$BUILDDIR"/install/slack-required | awk '{print$1}' | xargs -r -iZ echo -n "Z," | sed -e "s/,$//"
				else
					cat /tmp/tempfile2 | xargs -r -iZ echo -n "Z," | sed -e "s/,$//"
				fi
			else
				echo "The following dependencies has been found:"
				cat /tmp/tempfile2
				cat /tmp/tempfile2 >> "$BUILDDIR"/install/slack-required
			fi
		else
			echo "Do you want to add these dependencies to the slack-required?"
			echo "Reply yes or no:"
			echo ""
			
			exec 6<&0
			exec < /tmp/tempfile2
			while read i
			do
				j=""
				echo "$i"
				echo -n "Do you want to add this dependency? [yes] "
				read j <&6
				if test -z "$j" || test "$j" == yes || test "$j" == y || test "$j" == Yes || test "$j" == Y
				then
					echo "$i" >> "$BUILDDIR"/install/slack-required
				fi
			done
			exec 0<&6 6<&-

		fi
	fi

if test "$SOURCE" && test $SOURCE -eq 1
then
	cp "$BUILDDIR"/install/slack-required "$SBDIR" &>/dev/null
fi

if test $INPUTFILE -eq 1 && test /tmp/requiredbuilder/install/slack-required -nt "$ARG"
then
	cd "$BUILDDIR"
	tar czf "$ARG" ./
	cd /tmp
	rm -rf /tmp/requiredbuilder
fi

rm /tmp/tempfile
rm /tmp/tempfile2
rm /tmp/tempperl &> /dev/null
rm /tmp/tempperl2 &> /dev/null