#!/bin/sh
#
# qpkg - query portage package system for various information
#
# Copyright (c) Vitaly Kushneriuk <vitaly_kushneriuk@yahoo.com>
# This program is distributed under the terms of GPL version 2.
#
# Maintainer: Brandon Low <lostlogic@gentoo.org>
#
# $Header: /var/cvsroot/gentoo-x86/app-admin/gentoolkit/files/scripts/qpkg,v 1.19 2003/03/21 12:37:18 mholzer Exp $
ID='$Id: qpkg,v 1.19 2003/03/21 12:37:18 mholzer Exp $'
VERSION=0.`echo ${ID} | cut -d\  -f3`

PROG=`basename ${0}`

# Parse args
verb=0
group="*"
while [ ${#} -gt 0 ]
do
	a=${1}
	shift
	case "${a}" in

	-h|--help)
		usage=y
		break
		;;

	-i|--info)
		info=y
		;;

	-d|--dups)
		dups=y
		inst=y
		;;

	-q|--query-deps)
		query=y
		;;

	-s|--slot)
		slot=y
		;;

	-f|--find-file)
		ffind=y
		inst=y
		;;

	-fp|--find-pattern)
		ffind=y
		fpat=y
		inst=y
		;;

	-I|--installed)
		inst=y
		;;

	-U|--uninstalled)
		uninst=y
		;;

	-g|--group)
		group=$1
		shift
		;;

	-l|--list)
		list=y
		inst=y
		;;

	-ct|--check-time|-tc|--time-check)
		tcheck=y
		inst=y
		;;

	-cm|--check-md5|-mc|--md5-check)
		mcheck=y
		inst=y
		;;

	-c|--check)
		mcheck=y
		tcheck=y
		inst=y
		;;

	-v|--verbose)
		let $((verb++))
		;;

	-vv)
		let $((verb++))
		let $((verb++))
		;;

	-nc|--no-color|--nocolor|--no-colors|--nocolors)
		nocolor=y
		;;

	-*)
		echo -e ${CY}${PROG}${NO}:${YL} Invalid option ${RD}$a 1>&2
		usage=y
		break
		;;
	*)
		if [ -n "${arg}" ]; then
			echo -e ${CY}${PROG}: ${YL}Only one argument supported
			usage=y
			break
		fi
		# Kill a trailing slash
		arg=${a%/}
		;;
		
	esac
done

#This is a dumb way to handle things, take it out next time
T="\t"

#Set up colors
if [ ! "${nocolor}" ]; then
	NO="\x1b[0;0m"
	BR="\x1b[0;01m"
	CY="\x1b[36;01m"
	RD="\x1b[31;01m"
	GR="\x1b[32;01m"
	YL="\x1b[33;01m"
	BL="\x1b[34;01m"
	STAR=" *"
elif [ ! "${inst}" ] && [ ! "${uninst}" ]; then
	STAR=" *"
fi


# check for option conflicts
if [ "${inst}" -a "${uninst}" \
	-o \( "${ffind}" -o "${list}" -o "${tcheck}" -o "${mcheck}" \) \
	-a "${uninst}"  ]; then
	echo -e ${CY}${PROG}${NO}:${YL} conflicting options/modes${NO}
	usage=y
fi

if [ "${usage}" ]; then
	echo -e "${CY}${PROG} v. ${VERSION}${NO}

${CY}${PROG}${NO} is GenToolKit's \"query package\" tool, using it, you can
find packages owning files on your filesystem, check the integrity
of installed packages, and do other queries against installed or
uninstalled packages.

${BR}Usage:
${T}${CY}${PROG}${NO} [${BR}options${NO}] [${YL}pkgname${NO}] [${BL}-g${YL} group${NO}] [${BL}-f${YL} <file>${NO}|${BL}-fp${YL} <patern>${NO}]
${T}${CY}${PROG}${NO} ${BL}--dups${NO} [${BL}--slot${NO}]
${T}${CY}${PROG}${NO} ${BL}--help${NO}

${BR}Duplicate Locating:
  ${BL}-d,  --dups${NO}${T}${T}print packages that have multiple versions installed
  ${BL}-s,  --slot${NO}${T}${T}make ${BL}-d${NO} SLOT only print dups of the same SLOT

${BR}Package Selection:
  ${BL}-f,  --find-file${NO}${T}finds package that owns file <file>
  ${BL}-fp, --find-pattern${NO}${T}finds to package that owns file matching *<pattern>*
  ${BL}-I,  --installed${NO}${T}Include${YL} only${NO} installed packages
  ${BL}-U,  --uninstalled${NO}${T}Include${YL} only${NO} uninstalled packages
  ${BL}-g,  --group${NO}${T}${T}Find by group (can be combined with other searches)

${BR}Information Selection:
  ${BL}-l,  --list${NO}${T}${T}List package content
  ${BL}-i,  --info${NO}${T}${T}Get package description and home page.
  ${BL}-ct, --check-time${NO}
  ${BL}-tc, --time-check${NO}${T}Verify package files timestamps
  ${BL}-cm, --check-md5${NO}
  ${BL}-mc, --md5-check${NO}${T}Verify package files md5
  ${BL}-c,  --check${NO}${T}${T}Verify mtimes${YL} and${NO} md5.
  ${BL}-q,  --query-deps${NO}${T}display all installed packages 
${T}${T}${T}depending on selected packages

${BR}Operation Modifiers:
  ${BL}-nc, --no-color${NO}${T}don't use colors
  ${BL}-v,  --verbose${NO}${T}Be more verbose [ can be repeated twice ]
  ${BL}-vv${NO}${T}${T}${T}Same as ${BL}-v -v${NO}

${YL}Notes${NO}: 
${YL}*${NO} ${BL}-f${NO}, ${BL}-fp, ${BL}-d${NO}, ${BL}-l${NO}, ${BL}-ct${NO}, ${BL}-cm${NO}, and ${BL}-c${NO} apply only to installed packages.
${YL}*${NO} Short options may not be combined on the command-line, yet.
${YL}*${NO} The operation of some flags has been changed by the
  stripping of version numbers from some output to see
  the version numbers play with ${BL}-v${NO} and ${BL}-vv${NO}.
${YL}*${NO} When using${BL} -f${NO} with ${BL}-l${NO} or ${BL}--check.. -v${NO} options, only
  matching files will be displayed, unless ${BL}-v${NO} is doubled, 
  (yet more verbose) or ${BL}-vv${NO} is used.
${YL}*${NO} When using${BL} -q${NO}, it is important to note that the querying of deps is NOT
  a complete check, because qpkg is not advanced enough (nor can it reasonably
  made so) to check complete deps with versions.  Please use ${GR}depclean${NO} or
  ${GR}emerge --dep-clean${NO} to more completely check the dependency sanity of your
  system.

${YL}Examples${NO}:
  ${PROG} --dups		print duplicates oldest first
  ${PROG} --dups -v	.. with versions
  ${PROG}			print list of all packages
  ${PROG} porta -I		print versions of installed portage
  ${PROG} porta -i		.. + versions in portage tree + descriptions 
			and homepages
  ${PROG} gawk -c -v	check integrity all installed versions of gawk
			the older will have \"damaged\" files.
  ${PROG} -f /bin/ls	print package(s) that own /bin/ls
"
	exit
fi

#For the --dups switch only
if [ "${dups}" ]; then
	#First dig out the list of packages with duplicates
	find /var/db/pkg -iname "*${arg}*.ebuild" 2> /dev/null > /tmp/qpkg.lst
	dups=`cat /tmp/qpkg.lst | cut -f7 -d/ |
		sed -e 's:\.ebuild$::; s:-r[0-9]*$::; s:-[^-]*$::; /^$/d' |
		sort | 
		uniq -d`

	#Next get all the exact versions
	duppak=`cat /tmp/qpkg.lst | fgrep "${dups}"`

	#Now cut that down to the directory name so we can be smart
	dirs=`sed -e 's:/[^/]*$::' /tmp/qpkg.lst`

	#Go through each package's DB and create a sortable file
	#to play with
	declare -i defcount=`cat /var/cache/edb/counter`
	for DIR in ${dirs}
	do	#Package COUNTER
		NUM=`cat "${DIR}/COUNTER" 2> /dev/null`
		[ -z "${NUM}" ] && NUM=defcount
		#Package slot if requested
		[ ${slot} ] && SLOT=`cat "${DIR}/SLOT"`
		#Package fullname
		PKG=`ls --color=no -1 ${DIR}/*.ebuild|cut -f5,7 -d"/"`
		#Package basename
		NAME=`echo "${PKG}"|sed -e 's:\.ebuild$::; s:-r[0-9]\+$::; s:-[0-9].*$::'`
		echo "${NUM} ${PKG} ${NAME}${SLOT}"
	#Finish loop, and sort that nice sortable file based on 
	#installation order, and then based on package basename
	#bash hates me so I decided to use a temp file
	done |sort -t" " -k3 -k1g,2|uniq -D -f2 > /tmp/qpkg.lst
	duppak=`cat /tmp/qpkg.lst`
	rm /tmp/qpkg.lst

	#If max verbosity is set output with full path to each ebuild
	if [ "${verb}" -gt 1 ]; then
		echo -n "${duppak}"|cut -f2 -d" "| \
				    sed -e "s:^:${BL}/var/db/pkg/${BR}:" \
					-e "s:\(/\)\([^/]*\)\(.ebuild\):\1${CY}\2${NO}\1\2\3:"

	#If normal verbosity output package group, package name and package version
	elif [ "${verb}" -gt 0 ]; then
		echo -n "${duppak}"|cut -f2 -d" "| \
				    sed -e "s:\(^[^/]*/\)\(.*\)\(\.ebuild\):${BR}\1${CY}\2${NO}:"

	#Otherwise just output package group and package name
	else
		echo -n "${duppak}"|cut -f2 -d" "| \
				    sed -e "s:-r[0-9]\+$::" \
					-e "s:-[0-9].*$::" \
					-e "s:\(^[^/]*/\)\(.*\):${BR}\1${CY}\2${NO}:"|uniq
	fi
	exit
fi

# get list of ebuilds to work on
if [ "${ffind}" ]; then
	# file find mode - list all ebuilds for 
	# package/CONTENTS containing <arg>
	if [ "${fpat}" ]; then
		dirs=`ls /var/db/pkg/${group}/*/CONTENTS \
		| xargs grep -l "${arg}" \
		| xargs --no-run-if-empty -n 1 dirname`
	else
		dirs=`ls /var/db/pkg/${group}/*/CONTENTS \
		| xargs grep -l " ${arg}\( .*\)*$" \
		| xargs --no-run-if-empty -n 1 dirname`
	fi
	ipak=`(
	for d in ${dirs} -;do
		[ "-" = "$d" ] && break
		ls ${d}/*.ebuild
	done)`
else
	# normal mode - list ebuilds for ebuild name containing <arg>

	# installed packages
	if [ ! "${uninst}" ]; then
		ipak=`find /var/db/pkg/ -iname "*.ebuild" 2>/dev/null`
		if [[ ${group} != "*" ]]; then
		    ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep ${group}`
		fi
		if [ ${arg} ]; then
		    ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep ${arg}`
		fi
	fi
	# not installed packages (yet:-)
	if [ ! "${inst}" ]; then
		upak=`find /usr/portage/ -iname "*.ebuild" 2>/dev/null|grep -v --regex="/usr/portage/[^/]*\.ebuild"`
		if [[ ${group} != "*" ]]; then
		    upak=`echo ${upak}|sed -e "s: :\n:g"|grep ${group}`
		fi
		if [ ${arg} ]; then
		    upak=`echo ${upak}|sed -e "s: :\n:g"|grep ${arg}`
		fi
	fi
fi

X="\([^/]*\)"

for p in ${ipak} ${upak} -;do
	[ "${p}" = "-" ] && break

	# cut common prefix from ebuild name and mark installed/uninstalled packages
	# Note: iii/uuu will be replaced by the pipe at the end
	n=`echo $p | sed -e "s:^/var/db/pkg/${X}/${X}/${X}.ebuild:iii \1/\3:" \
		-e "s:^/usr/portage/${X}/${X}/${X}\.ebuild:uuu \1/\3:"`
	d=`dirname ${p}`
	echo ${n}
	if [ ${verb} -gt 1 ];then
		echo "vvv    ${p}"
	fi
	
	if [ "${info}" ]; then
		home=`grep HOMEPAGE ${p}| cut -d\" -f2`
		desc=`grep DESCRIPTION ${p}| cut -d\" -f2`
		echo -e "${T}${BL}${desc}${NO} [ ${YL}${home}${NO} ]"
	fi
	
	if [ "${query}" ]; then
		echo -e "${BL}DEPENDED ON BY:${NO}"
		package="`echo ${n}|sed -e 's:-r[0-9]\+$::' \
					-e 's:-[0-9].*$::' \
					-e 's:^iii ::' \
					-e 's:^uuu ::'`"
		place="`echo ${n}|cut -f1 -d' '`"
		[[ "${place}" == "iii" ]] && color="${GR}" || color="${RD}"
		grep -R "${package}" /var/db/pkg/*/*/RDEPEND | \
		cut -f5,6 -d"/" | sed -e "s:^:\t${color}:;s:$:${NO}:" | sort | uniq
#		gawk -F "/" '{printf("${place}\n\t%s/%s${NO}",$5,$6)}' | sort | uniq
	fi

	# cat package content, remove obj/sym/dir, md5 and mtime when not verbose
	# display only match in file-find mode unless extra verbose
	if [ "${list}" ]; then
		echo -e ${BL}CONTENTS:${NO}

		if [ ${verb} -gt 1 ]; then
			cat ${d}/CONTENTS
		else
			if [ "${ffind}" ]; then
				if [ "${fpat}" ]; then
					grep "${arg}" $d/CONTENTS
				else
					grep " ${arg}\( .*\)*$" $d/CONTENTS
				fi
			else
				cat $d/CONTENTS
			fi |
			if [ ${verb} -gt 0 ]; then
				cat
			else
				sed -e "s:\(^obj \)\([^ ]*\)\(.*$\):\1${BR}\2${NO}:;
				        s:\(^sym \)\([^ ]*\)\( -> \)\([^ ]*\)\(.*$\):\1${CY}\2${NO}\3\4:;
				        s:\(^dir \)\([^ ]*\)\(.*$\):\1${YL}\2${NO}:"
			fi
		fi

		echo
		
	# check files mtime and md5, display summary at the end
	elif [ "${tcheck}" -o "${mcheck}" ]; then
		# counters
		fe=0
		fs=0
		# read the CONTENTS file and check md5 and mtime if needed
		# process only matching files in find-file mode unless extra verbose
		cat ${d}/CONTENTS | 
			if [ "${ffind}" -a ${verb} -lt 2 ];then 
				if [ "${fpat}" ]; then
					grep "${arg}"
				else
					grep " ${arg} "
				fi
			else
				cat
			fi |
		(
		while read -a line
		do
			fs=$((fs + 1))
			
			unset md5
			unset _md5
			unset mtime
			unset _mtime
			unset err

			name=${line[1]}
			
			missing=
			[ ! -e ${name} ] && missing=1
			
			# colorize name and compute mtime/md5
			if [ "obj" = ${line[0]} ]; then
				[ -e ${name} ] && {
					[ "${tcheck}" ] && mtime=${line[3]}
					[ "${tcheck}" ] && _mtime=`date -r ${name} +%s`

					[ "${mcheck}" ] && md5=${line[2]}
					[ "${mcheck}" ] && _md5=`md5sum ${name}|cut -f1 -d" "`
				}

				name=${BR}${name}${NO}

			elif [ "sym" = ${line[0]} ]; then
				name=${CY}${name}${NO}

			elif [ "dir" = ${line[0]} ]; then
				name=${YL}${name}${NO}
			fi
			
			# compare
			if [ "$missing" ]; then
				err=1
				name="${name} ${RD}!not exist!${NO}"
			fi
			if [ "${md5}" != "${_md5}" ]; then
				#If the md5 fails the first time check it with
				#everything changed to lowercase :-D
				md5=`echo "${md5}"|tr A-Z a-z`
				if [ "${md5}" != "${_md5}" ]; then
					err=1
					name="${name} ${RD}!md5!${NO}"
				fi
			fi
			if [ "${mtime}" != "${_mtime}" ]; then
				err=1
				name="${name} ${RD}!mtime!${NO}"
			fi

			[ ${verb} -gt 1 ] && echo -e ${name}
			[[ ${verb} -eq 1 ]] && [[ $err -eq 1 ]] && echo -e ${name}

			fe=$((fe + err))
		done
		if [ "$fe" = "0" ]; then
			echo -e ${YL}$fe${CY}/$fs${NO}
		else
			echo -e ${RD}$fe${CY}/$fs${NO}
		fi
		echo
		)
	fi
	
done | (
	if [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o ${verb} -gt 0 \) ]; then
		sed -e "s:-r[0-9]\+$::" -e "s:-[0-9][^-]*$::"|sort -k2|uniq -1
	elif [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o ${verb} -lt 2 \) ]; then
		sort -k2|uniq -1
	else
		cat
	fi | sed \
		-e "s:^iii ${X}/${X}:${BR}\1/${CY}\2${STAR}${NO}:" \
		-e "s:^uuu ${X}/${X}:${BR}\1/${YL}\2${NO}:" \
		-e "s:^vvv \(.*\)$:${BL}\1${NO}:" \
		-e "s:^obj ::;s:^sym ::;s:^dir ::"

)
