#!/bin/bash
#
# original script by Jim Pick <jim@jimpick.com>, GPL'd of course
# many changes and enhancements by Craig Sanders <cas@taz.net.au>
#
# hacked by cas to use case instead of if/elif/fi
# hacked by cas to add '-ls' option.  also added error checking for
# -L and -ls options.
# hacked by cas to add '-conf' and '-lsconf' options.
# hacked by cas to add '-md5sum' and '-md5check' options.
# hacked by cas to add '-man' option.
# hacked by cas to add '-s' option (requires grep-dctrl).
# hacked by cas to support multiple string/package arguments
# hacked by cas to add simplistic emulation of 'dpkg -l'
#
# hacked 1999 Kevin Ryde to add wildcards to package names
# and 2008 for md5sum lost -v option
# http://www.geocities.com/user42_kevin/dloc/index.html
#

DLOCATEDB=/var/lib/dlocate/dlocatedb
DPKGLIST=/var/lib/dlocate/dpkg-list
DPKG_INFO=/var/lib/dpkg/info

LOCATE=/usr/bin/locate
# if locate.old exists, slocate must be installed.  use locate.old instead.
[ -e /usr/bin/locate.old ] && LOCATE=/usr/bin/locate.old


# List the files packages in the arg list (wildcards allowed).
#
dlocate-L() {
    for i in "$@"
    do
	set $DPKG_INFO/$i.list
	if [ -e "$1" ]
	then 
	    cat "$@"
	else
	    echo Package \"$i\" not installed. 1>&2
	fi
    done
}

# Print contents of <package>.$PART for packages in the arg list
# (wildcards allowed).
#
dlocate-part() {
    for i in "$@"
    do
	set $DPKG_INFO/$i.list
	if [ -e "$1" ]
	then
	    for j in "$@"
	    do
		package=`basename "$j" .list`
		p="$DPKG_INFO/$package.$PART"
		if [ -e "$p" ]
		then 
		    cat "$p"
		else
		    echo Package \"$package\" has no $PART. 1>&2
		fi
	    done
	else
	    echo Package \"$i\" not installed. 1>&2
	fi
    done
}


if [ $# = 0 ]
then
	OPTION="--help"
else
	case "$1" in
	-*) OPTION="$1"; shift ;;
	*)  OPTION="--" ;;
	esac
fi


case "$OPTION" in
    -h|-H|--help)
	cat <<__EOF__
Usage: dlocate [option] [string...]

Options:
    (no option) string    list all records that match
    -S        string      list records where files match
    -L        package     list all files in package
    -l        string      almost-emulation of 'dpkg -l'
    -s        package     print package's status
    -ls       package     'ls -ldF' of all files in package
    -du       package     'du -sck' of all files in package
    -conf     package     list conffiles in package
    -lsconf   package     'ls -ldF' of conffiles in package
    -md5sum   package     list package's md5sums (if any)
    -md5check package     check package's md5sums (if any)
    -man      package     list package's man pages (if any)

  The -L, -s, and -S commands are roughly analagous to the
  equivalent dpkg commands.  Shell style wildcards are allowed
  for package names and -S searches (but not -l).
__EOF__
	exit 1
	;;

    -l)
	# With no arguments, print everything.
	#
	cat <<__EOF__
Desired=Unknown/Install/Remove/Purge
| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed
|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)
||/ Name            Version        Description
+++-===============-==============-============================================
__EOF__
	pat="$1"
	shift
	while [ $# != 0 ]
	do
	    pat="$pat\\|$1"
	    shift
	done
	grep -- "$pat" $DPKGLIST
	exit
	;;
esac


if [ $# = 0 ]
then
    echo Package name or string required. 1>&2
    exit 1
fi


case "$OPTION" in
    -L)
	dlocate-L "$@"
	;;

    -ls)
	dlocate-L "$@" | xargs ls -ldF
	;;

    -du)
	# Symlinks and directories are excluded.
	# Could hit ARG_MAX here, but it's the easiest way to get the du total.
	#
	du -sck $(dlocate-L "$@" | xargs ls -1dF | grep -v '@$\|/$' )
	;;

    -man)
	dlocate-L "$@" | \
	    sed -n -e 's/\.gz$//' \
		   -e '/man.*\/.*[0-9]/s/^.*\/\([^\/]*\)\.\(.*\)/\2 \1/p'
	;;

    -md5sum)
	PART=md5sums
	dlocate-part "$@"
	;;

    -md5check)
	# Adding / to the filenames is better than "cd /" because the messages
	# printed by md5sum will then have a full path and won't risk confusing
	# the user.
	#
	PART=md5sums
	dlocate-part "$@" | awk '{print $1 "  /" $2}' | md5sum -c /dev/stdin
	;;

    -conf)
	PART=conffiles
	dlocate-part "$@"
	;;

    -lsconf)
	PART=conffiles
	dlocate-part "$@" | xargs ls -ldF
	;;

    -S)
	# Passing all the arguments to one spawned copy of locate seems to be
	# only very slightly faster than running it repeatedly (with locate
	# 4.1 at least).
	#
	# When patterns overlap, locate can print the same record twice.
	# No attempt is made to do anything about that.
	#
	declare -a a=("$@")
	i=0
	while [ $i -lt $# ]
	do
	    case "${a[$i]}" in
		/*) a[$i]="*: ${a[$i]}"   ;;
		*)  a[$i]="*: *${a[$i]}*" ;;
	    esac
	    i=$(($i+1))
	done
	$LOCATE -d $DLOCATEDB "${a[@]}"
	;;

    -s)
	# grep-dctrl doesn't take shell style wildcards for the pattern to
	# match, so expand first by looking for <package>.list files.
	# Unfortunately this means only installed packages can be shown,
	# but that doesn't matter too much because there's not really any
	# useful info on uninstalled packages.
	#
	# The pattern built up for grep-dctrl means the search is done with
	# only one pass over /var/lib/dpkg/status, which is useful if that
	# file is big or grep-dctrl is slow.
	#
	pat=""
	for i in "$@"
	do
	    set $DPKG_INFO/$i.list
	    if [ -e "$1" ]
	    then
		for j in "$@"
		do
		    [ -n "$pat" ] && pat="$pat\\|"
		    pat="$pat^`basename $j .list`\$"
		done
	    else
		echo Package \"$i\" not installed. 1>&2
	    fi
	done
	[ -n "$pat" ] && grep-dctrl -rP "$pat" /var/lib/dpkg/status
	;;

    --)
	$LOCATE -d $DLOCATEDB -- "$@"
	;;

    *)
	echo Unknown option \"$OPTION\"
	exit 1
	;;
esac
