#! /bin/bash

digests="sha1 md5 sha256 sha384 sha512 dss1"

progpath=$0
while [ -L "$progpath" ]; do
    progpath=$(readlink "$progpath")
done

program=$(basename "$0")
target=$(basename "$progpath")
dir=$(dirname "$progpath")

hx=1 # Help listing or problem with arguments
cx=2 # Failed digest comparison
ax=3 # Invalid or unsupported digest algorithm
sx=4 # Problem exit from setup()

usage () {
    echo Usage: $program '<filename>' '[test-value]' >&2
    echo "\twhere <filename> is required and test-value is" >&2
    echo "\tan optional hex digest value to test against." >&2
    echo "\tExits with" >&2
    echo "\t\t$hx if the number of arguments is wrong;" >&2
    echo "\t\t$cx if the digest comparison fails;" >&2
    echo "\t\t$ax if an unsupported algorithm is requested." >&2
    echo
    echo or: $program --digests >&2
    echo "\twhich will echo the supported algorithms." >&2
    echo
    echo or: $program --setup >&2
    echo "\twhich tries to ensure that hard links are set up for all digest algorithms." >&2
    echo "\tExits with $sx if NOT all links are available after execution." >&2
    echo
    echo "\tNote that the user running this option must have write permission in the" >&2
    echo "\tdirectory containing the invoked program. Soft links are followed" >&2
    echo "\tto determine the actual directory containing the program." >&2
    echo "\tAlternatively, you may manually set up your own softlinks in any directory." >&2
    echo
    echo "\tSome of the executables may already exist on your system. On OS X," >&2
    echo "\tfor example, the executable /sbin/md5 is present. Which one is found" >&2
    echo "\tdepends on your PATH." >&2
    echo
    echo or: "$program -<anything>" >&2
    echo "\tPrint this help text." >&2
    echo

    exit 1
}

setup() {
    ok=0
    for algo in $digests; do
	algopath="$dir/$algo"
	if [ -e "$algopath" ]; then
	    if [ ! -x "$algopath" ]; then
		echo "$algopath" exists but is not executable! >&2
		ok=$sx
	    else
		echo Executable "$algopath" already exists. >&2
	    fi
	else
	    if ln "$progpath" "$algopath"; then
		echo Executable "$algopath" created. >&2
	    else
		echo Failed to link "$algopath" to "$progpath" >&2
		ok=$sx
	    fi
	fi
    done
    exit $ok
}

case "$1" in
    -*\? | -*help)
	usage
	;;
    --d*g*s*)
	echo $digests
	exit 0
	;;
    --setup)
	setup
	;;
    -*)    # Catch all hyphen args. Put here so new hyphen args can be added.
	usage
	;;
esac

if [ $# -lt 1 -o $# -gt 2 ]; then
    usage
fi

file="$1"
testval=$(echo "$2" | tr 'A-Z' 'a-z')

if [ "${digests/*$program*/OK}" != OK ]; then
    echo "$program" not one of $digests >&2
    exit $ax
fi

ex=0
result=$(openssl dgst -"$program" "$file")
digest=$(expr "$result" : '.* \([0-9A-Fa-f][0-9A-Fa-f]*\)$' | tr 'A-Z' 'a-z')
if [ x = "x$testval" ]; then
    echo $file = $digest >&2
else
    if [ $digest = $testval ]; then
	echo "$file OK"$'\n'"$digest EQUALS"$'\n'"$testval" >&2
    else
	echo "$file FAIL"$'\n'"$digest NOT EQUAL TO"$'\n'"$testval" >&2
	ex=$cx
    fi
fi
exit $ex
