#!/bin/sh
# typing test
# usage:
#	type [-f input-file]
#	type [fortune options]
# or just
#	type
#
# Displays the input file (or creates one via "fortune"), asks you to type it,
# times you and attempts to count errors and compute a words-per-minute score.
#
# Assumptions: 5 chars is a word, 10 WPM penalty for error.
# Whitespace is ignored when computing the score (well, there
# must be some whitespace between the words) so you can
# break lines wherever you want.   You can also use the normal line-editing
# characters.
#
# TODO:
#   Show you exactly what your errors are.
#   -- maybe use "patch" to apply the diff, but cleverly insert
#      some highlighting?
# 
# Steve Hayman, Indiana U
# sahayman@iuvax.cs.indiana.edu
# 88/08/16

PATH=/bin:/usr/bin:/usr/ucb export PATH
Usage="${0}: Usage: $0 [-f input-file]  or $0 [fortune options]"

# Determine how to echo without newline

if [ `echo -n | wc -c` = "0" ]; then
	nflag=-n
	cesc= 
else
	if [ `echo "\c" | wc -c` = "0" ]; then
		nflag=
		cesc="\c"
	else
		echo "Your echo is weird" 1>&2
		exit 1
	fi
fi

stmp=/tmp/t-s.$$
itmp=/tmp/t-i.$$
diff=/tmp/t-d.$$
in=/tmp/t-in.$$
source=/tmp/t-source.$$
trap exit 1 2 3 15
trap 'rm -f $in $itmp $stmp $source $diff' 0

# A source of pithy sayings.
fortune=/usr/sipb/`/bin/athena/machtype`bin/fortune
if test ! -f $fortune; then
	echo "${0}: Cannot find $fortune - cannot generate examples."
	echo "${0}: Try  $0 -f some-input-file"
	exit 1
fi

chars_per_word=5
penalty_per_error=10


# Default is a fortune.

case "$1" in
"-f")	# Input file supplied.
	shift
	cat ${1?$Usage}
	;;
*)	
	# Pick up a fortune.  Use the supplied command line options,
	# so the user can choose short, long, obscene, whatever.
	
	$fortune ${1+"$@"}
	;;
esac | sed '/^$/d' >$source		# No blank lines, please.

clear
cat $source

cat <<EOF


Type the above, end it with an extra blank line (two carriage returns).
Exact spacing does not matter.  You have five seconds to get ready.

EOF

# the stty's are a lame attempt to disallow typeahead

stty -echo 
for n in 5 4 3 2 1
do
	echo $nflag "$n... $cesc"
	sleep 1
done

# this is supposed to consume typeahead

stty raw; stty -nl echo cooked

# there are supposed to be a couple of control-G's in this line, which
# may not have survived the shar/unshar process

echo "Go!"
echo ""

# This awk script is the the best way I could think of to read up to
# a blank line and quit immediately.  "sed /^$/q" needs to read an extra line,
# "grep -1 -v '^$'" doesn't work.  Could write some little C thing
# if we wanted to be really accurate, timewise.


set ` /bin/time 2>&1 1>$in </dev/tty awk 'NF == 0 { exit } { print }' `
echo "computing typing rate..."

# On BSD systems, time outputs a single line:
#
# $ /bin/time echo foo
# foo
#        0.4 real         0.0 user         0.1 sys
#
# On POSIX.2 systems, time outputs several lines, like this:
#
# $ /bin/time echo foo
#
# real   0.4
# user   0.0
# sys    0.1
#
# This distinguishes between the two cases

case $1 in
*.*)      
	seconds=$1
        ;;
real*)     
	seconds=$2
        ;;
*)
	fmt 1>&2 << EOM
Can't parse the output of your time(1) command.  Check installation.
EOM
	exit 1
        ;;
esac

chars=`wc -c <$in`
words=`echo "scale = 2; $chars / $chars_per_word" | bc`
wpm=` echo "scale = 2;  $words * 60 / $seconds " | bc `

echo "words: $words  seconds: $seconds"
echo "Raw WPM: $wpm"


# translate the source and input so that they're one word per line,
# then "diff" them and try to count the results.
# We ignore all the diff lines that begin with > or < or -, we're
# only interested in the counts ( 1a2, 3,4c5,6, those things.)
# We just count up the line numbers - that makes the basic
# (and imperfect) assumption that there can be no more than one error per word.

tr -s ' \011' '\012' <$in >$itmp
tr -s ' \011' '\012' <$source >$stmp
diff $itmp $stmp >$diff
eval ` sed -e '/^[<->]/d' -e 's/[adc]/ & /' <$diff | awk '
	{
		nleft = split( $1, left, "," )
		nright = split ( $3, right, "," )
		if ( nleft == 1 )
			left[2] = left[1]
		if ( nright == 1 )
			right[2] = right[1]

		if ( $2 == "a" )
			missing += right[2] - right[1] + 1
		else if ( $2 == "d" )
			extra += left[2] - left[1] + 1
		else if ( $2 == "c" )
			wrong += right[2] - right[1] + 1
	}
	END {	
		printf "missing=%d\n", missing
		printf "extra=%d\n", extra
		printf "wrong=%d\n", wrong
		printf "total=%d\n", missing + extra + wrong
	}
	' `
echo "Missing: $missing   Extra: $extra  Wrong: $wrong  Total errors: $total"


awpm=` echo "scale=2; ($words - ($total * $penalty_per_error)) * 60 / $seconds" | bc`


echo "Adjusted WPM: $awpm"
