#!/bin/sh

# Copyright (c) 2010, 2011 George Danchev <danchev@spnet.net>
# Copyright (c) 2010, 2011, 2019 Thomas Schmitt <scdbackup@gmx.net>
# This script is distributed according to the terms of the GNU GPL v2.
# This should be better rewritten in C at some future point. Ref: pwd code.

# Create a list of checksums encoded in hexadecimal format and print to
# standard output. Checksum may be MD5 or SHA256.

# Format Description
# A line in the emerging file is to be composed as follows:
#
# The checksum of the file content must be encoded in the aprropriate number
# of hex digits.
# [0-9afAF]
#
# Next come two blanks.
#
# The byte size of the file content must be encoded in 12 decimal digits
# or blanks.
#
# Next come two blanks.
#
# The rest of the line up to the newline character is a semi-literal file
# address. Its basename has to be the same as the basename of the data file
# when it is used as one of the input files for the jigdo file generator.

# The semi-literal address and the address mapping define what will be
# listed as file address in the jigdo file.
# The address may bear at its start a literal text that shall be recognized by
# the address mapping (e.g. -jigdo-map) of the jigdo file generator.
# The rest of the address must be usable as file address in both situations:
# When the jigdo file gets generated, and when the jigdo file gets read
# to inflate the template file into the original payload image.
# The address mappings at both occasions can be used to adapt to a change
# of the absolute location of the listed files.
# Between both mappings, the parent directory is represented by a symbolic
# text, like "Debian:".

# A simple strategy to cope with this is to write absolute paths into the
# checksum file, and to use matching absolute paths in the -jigdo-map
# directives. Keep in mind that mapping is purely literal. Symbolic links
# are neither resolved nor can they confuse the mapping.

set -e

SELF=jigdo-gen-md5-list
VER=0.3

OPT_ABSOLUTE=1

# The checksum type to produce: md5 , sha256
checksum_type=md5
hex_length=32
md5_cmd=


# On FreeBSD there is "md5" rather than "md5sum".
# Furthermore, the FreeBSD shell reports missing commands to inherited stderr,
# regardless that the attempt itself has redirected stderr. Thus a sub shell
# is needed to hide the protest.
choose_checksum_cmd() {
	if test "$checksum_type" = "md5"
	then
		if ( md5sum --help ) >/dev/null 2>&1 
		then
			md5_cmd=md5sum
		elif ( md5 -s test ) >/dev/null 2>&1
		then
			md5_cmd=md5
		else
			echo "$0 : Programs md5sum and md5 failed to work" >&2
			exit 2
		fi
	elif test "$checksum_type" = "sha256"
	then
		if ( sha256sum --help ) >/dev/null 2>&1 
		then
			md5_cmd=sha256sum
		elif ( sha256 -s test ) >/dev/null 2>&1
		then
			md5_cmd=sha256
		else
			echo "$0 : Programs sha256sum and sha256 failed to work" >&2
			exit 2
		fi
	fi
}

usage() {
	cat << USAGE
usage: $SELF [option] DIR FILE ...
Print a Jigdo checksum file to stdout. One line per FILE and per file in DIR.
 -m, --md5              produce MD5 checksums (default)
 -s, --sha256           produce SHA256 checksums
 -a, --make-absolute    make absolute paths, avoiding any symlinks (default)
 -l, --keep-literal     leave paths untouched, literally as supplied
 -v, --version          print version
 -h, --help             print help
 -e, --examples         print examples
USAGE
}

examples() {
	cat << EXAMPLES
examples:
 $SELF datadir datafile
 $SELF --keep-literal datadir datafile
 find . -type f | xargs $SELF
 find . -exec $SELF '{}' ';'
EXAMPLES
}

md5list() {
	item="$1"
	if test $OPT_ABSOLUTE -eq 1; then
        	dn=`dirname "$item"`  # dirname
		fn=`basename "$item"` # filename
		od=`pwd -P`           # old dir
		cd "$dn" || exit 1
		item=`pwd -P`/"$fn"   # absolute physical file path, avoiding all symlinks
		cd "$od" || exit 1
	fi
	if test "$md5_cmd" = "md5sum"
	then
		MD5=`md5sum "$item" | awk '{print $1}'`
	elif test "$md5_cmd" = "md5"
	then
		MD5=`md5 -q "$item"`
	elif test "$md5_cmd" = "sha256sum"
	then
		MD5=`sha256sum "$item" | awk '{print $1}'`
	elif test "$md5_cmd" = "sha256"
	then
		MD5=`sha256 -q "$item"`
	else
		echo "$0 : Internal error : Checksum mode unknown : $md5_cmd" >&2
		exit 2
	fi
	SIZ=`ls -ld "$item" | awk '{print $5}'`
	printf '%'"$hex_length"'s  %12s  %s\n' "$MD5" "$SIZ" "$item"
}

walkdir() {
	DR="$1"
	for item in `find "$DR" -type f`
	do
		md5list "$item"
	done
}


# main()
if test "$1" = "" ; then
	usage
	exit 1
fi

for i in "$@"
do
	case "$i" in
	--md5|-m)
		checksum_type=md5
		hex_length=32
		;;
	--sha256|-s)
		checksum_type=sha256
		hex_length=64
		;;
	--make-absolute|-a)
		OPT_ABSOLUTE=1;
		shift;
		;;
	--keep-literal|-l)
		OPT_ABSOLUTE=0;
		shift;
		;;
	--version|-v)
		printf '%s %s\n' "$SELF" "$VER"
		exit 0
		;;
	--help|-h)
		usage
		exit 0
		;;
	--examples|-e)
		examples
		exit 0
#	*)
#		usage
#		exit 1
#		;;
	esac
done
choose_checksum_cmd

for i in "$@"
do
	if echo "$i" | grep '^-' >/dev/null ; then
		dummy=dummy
	elif test -d "$i" ; then
		DR="$i"
                if test $OPT_ABSOLUTE -eq 1; then
			od=`pwd -P`           # old dir
			cd "$DR" || exit 1
			DR=`pwd -P`           # absolute physical dir path, avoiding all symlinks
			cd "$od" || exit 1
		fi
		walkdir "$DR"
	elif test -f "$i" ; then
		FL="$i"
		md5list "$FL"
	else
		usage
		exit 1
	fi;

done

exit 0

