#!/bin/bash
# Key cutter for SRCE
# Copyright (C) 2005-2007 Tim Post
# Permission to copy and modify granted under the terms of
# version 3 of the GNU General Public License, or (at your option)
# any later version.

# Does what it says, says what it does
die()
{
	echo "$*" >&2
	safe_exit EXIT_FAILURE
}

safe_exit()
{
	local mode="$1"

	# If called with EXIT_FAILURE and $FORCE is true, we don't exit.
	# If called with $EXIT_FAILURE, we exit no matter the value of $FORCE
	# Simple work-around so that we can die if we really need to, even if
	# --force was passed. Note, EXIT_FAILURE and $EXIT_FAILURE are different :)

	[ -z "$EXIT_FAILURE" ] || [ "$EXIT_FAILURE" = "" ] && EXIT_FAILURE="1"

	[ "$FORCE" = "yes" ] && [ "$mode" != "$EXIT_FAILURE" ] && return 0

	[ ! $1 ] && safe_exit EXIT_SUCCESS

	case $mode in
		0|EXIT_SUCCESS)
			# if EXIT_SUCCESS undefined, just exit and rely on the shell
			# to invoke the proper EXIT_ macro
			[ -z "$EXIT_SUCCESS" ] || [ "$EXIT_SUCCESS" = "" ] && {
				exit
			}
			exit $EXIT_SUCCESS
			;;
		*)
			# Exit with whatever EXIT_FAILURE is defined as, or with
			# whatever argument was passed to safe_exit()
			[ $mode = "EXIT_FAILURE" ] && exit $EXIT_FAILURE
			exit $mode
			;;

	esac
}

# Cuts the tumblers (lines in the key)
make_tumbler()
{
	local n=1
	local tumbler=""
	local grooves="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

	while [ $n -le $tumblers ]; do
		tumbler="$tumbler${grooves:$(($RANDOM%${#grooves})):1}"
		let n+=1
	done

	echo "$tumbler"
	unset tumbler
}

# Shows help (but does not exit)
usage()
{
	cat << EOF

${PROGNAME} ver ${VERSION} - Cut a key for use with SRCE
Usage: ${PROGNAME} [options] <keyfile>, where [options] are:

  -l, --key-length      | Length of the key (in lines) default: ${length}
  -c, --cipher-length   | Length of the key lines (in chars) default: ${tumblers}
  -f, --keyfile         | Implicitly specify the file to write (for older shells)
  -s, --stdout          | Print the key to stdout instead of (or in addition to)
                        | a keyfile.
  -h, --help            | This help screen.
  -v, --version         | Show version information and exit.

Exits with a non-zero status on failure, zero on success.
See ${PROGNAME}(1) for more information.

Bug reports should go to ${MAINTAINER} <${MAINTAINER_EMAIL}>

EOF
}

# Main program

# Globals
VERSION="1.0.5"
MAINTAINER="Tim Post"
MAINTAINER_EMAIL="tinkertim@gmail.com"
PROGNAME=$(basename $0)

# tweaks
EXIT_FAILURE="1"
# If undefined, will just 'exit' and rely on the shell to
# invoke the proper EXIT_SUCCESS macro, defined by the system.
#EXIT_SUCCESS="0"

[ $# = 0 ] && {
	usage
	safe_exit $EXIT_FAILURE
}

# Default Values
length=32
tumblers=32
to_stdout=0
keyfile=""

TEMP=$(getopt -o l:c:f:shv --long key-length:,cipher-length:,keyfile:,stdout,help,version -- "$@")
[ $? != 0 ] && safe_exit EXIT_FAILURE
eval set -- "$TEMP"
while true; do
	case $1 in
		-l|--key-length)
			length="$2"
			shift 2
			continue
			;;
		-c|--cipher-length)
			tumblers="$2"
			shift 2
			continue
			;;
		-s|--stdout)
			to_stdout=1
			shift
			continue
			;;
		-h|--help)
			usage
			safe_exit EXIT_SUCCESS
			;;
		-v|--version)
			echo "${VERSION}"
			safe_exit EXIT_SUCCESS
			;;
		-f|--keyfile)
			keyfile="$2"
			shift 2
			continue
			;;
		--)
			break;
			;;
	esac
done

[ -z "$keyfile" ] && {
	eval set -- "$*"
	shift
	[ -z $1 ] && [ $to_stdout -le 0 ] && {
		usage
		die "ERROR: No keyfile specified (or direction to stdout)"
	}
	keyfile=$1
}

[ -n "$keyfile" ] && {
	touch ${keyfile} || die "Can not write key : ${keyfile} (permissions ok?)"
	cat /dev/null > ${keyfile}
}

i=1
counter=0

while [ "$i" -lt "$length" ]; do
	tmp=$(make_tumbler)

	[ $to_stdout -lt 1 ] && {
		if [ $counter = 31 ]; then
			printf "\n"
			counter=0;
		else
			printf "."
			let "counter += 1"
		fi
	}

	[ -n "$keyfile" ] && echo "$tmp" >> ${keyfile}
	[ $to_stdout -gt 0 ] && echo "$tmp"

	let "i += 1"
done

[ -n "$keyfile" ] && printf "\n\nKey %s Written\n\n" "$keyfile"

safe_exit EXIT_SUCCESS
