#! /bin/sh

# cmdseq https://tratt.net/laurie/src/cmdseq/
#
# Usage: cmdseq [-d <count dir>] <count1> <cmd1> [... <countn> <cmdn>]
#
# Every time this utility is called it executes one of cmd1 ... cmdn. It
# selects the command by remembering how many times this particular sequence
# of commands has been called. First, cmd1 is executed countn times; cmd2 (if it
# exists) count2 times; and cmdn countn times. It then cycles back to the
# beginning.


# A list of possible hash commands. There is no consistency across Unices as to
# what these are called, nor their command line parameters. In practise, we seem
# to be able to get away with piping a string in via stdin and truncating the
# hash to something of half-reasonable length.

HASH_CMDS="sha256 sha256sum md5 md5sum"


# See if we can find a usable hash command

hash_cmd=""
for cnd_hash_cmd in $HASH_CMDS; do
    which $cnd_hash_cmd >/dev/null 2> /dev/null
    if [ $? -eq 0 ]; then
        hash_cmd=$cnd_hash_cmd
        break
    fi
done
if [ "$hash_cmd" = "" ]; then
    echo "No usable hash command found. Install sha256 / md5." >&2
    exit 1
fi


usage() {
    echo "Usage: cmdseq [-d <count dir>] <count1> <cmd1> [... <countn> <cmdn>]" >&2
    exit 1
}

countdir="/tmp/"
while getopts ":hd:" f
do
    case "$f" in
        d)   countdir="$OPTARG";;
        h)   usage;;
        [?]) usage;;
    esac
done

# Since commands come in (count, cmd) pairs, they must be an even number

shift $((OPTIND-1))
if [ $(($# % 2)) -ne 0 ]; then
    usage
fi

# There must be at least one (count, cmd) pair

if [ $# -eq 0 ]; then
    usage
fi

# Work out where the count file is (or should be)

countp=${countdir}/cmdseq.`echo $@ | $hash_cmd | cut -c 1-16`
if [ ! -f $countp ]; then
    cur=0
else
    cur=$((`head -n 1 $countp` + 1))
fi

# Execute the appropriate command

firstcmd=$2
count=0
while [ $# -gt 0 ]; do
    c=`expr $1`
    if [ $? -gt 1 ]; then
        echo "Invalid count '$1'" >&2
        exit 1
    fi
    if [ $c -lt 1 ]; then
        echo "Invalid count '$1'" >&2
        exit 1
    fi
    nextcount=$((count + c))
    if [ $cur -lt $nextcount ]; then
        cmd=$2
        break
    fi
    count=$nextcount
    shift
    shift
done

# If we ran past the end, then we need to wrap back to the beginning
if [ $# -eq 0 ]; then
    cmd=$firstcmd
    cur=0
fi

$cmd
ec=$?

umask 077 # The count file will only be r/w by the user executing cmdseq
echo $cur > $countp
exit $ec
