/home/wpollock1/public_html/restricted/ShellScripting/todo

#!/bin/bash
# todo - a Script to manage a "to do" list.
# This version supports a menu driven interface if called with no
# command line arguments.
#
# The (current) todo list/databse file format is one task per line.
#
# TODO: add a timestamp field, add a priority field (and then
#       display tasks in priority order).  Keep completed tasks
#       in the database file, with another timestamp showing when
#       the task was completed.  (And a field to show it is done.
#
# Initially written 4/2007 by Wayne Pollock, Tampa Florida USA
#---------------------------version--------------------------
# $Id: todo,v 1.2 2007/04/12 08:43:29 wpollock Exp $
#-------------------------change-log-------------------------
# $Log: todo,v $
# Revision 1.2  2007/04/12 08:43:29  wpollock
# Final version (unless bugs are found!)
# In this version command line options were added,
# help was split into full help and brief usage messages.
#

PATH=/bin:/usr/bin

FILE="$HOME/.todo"
PROGNAME="${0##*/}"
VERSION=$(echo $Revision: 1.2 $ \
   |awk '{ $1 = "'"$PROGNAME"' version "; $NF = ""; print}')

# Define shell functions to use:

add()
{
    if [ -n "$MENU" ]
    then
        printf "(Add)    Enter new task to add (hit enter to cancel): "
        read TASK
        [ -z "$TASK" ] && return
    else
        TASK="$1"
    fi

    if [ -z "$TASK" ]
    then
        printf '*** You must specify a task to add. ***\n'
        return 1
    fi

    if [ ! -e "$FILE" ]
    then > "$FILE"
         chmod 600 "$FILE"
    fi

    echo "$TASK" >> $FILE

    if [ -n "$MENU" ]  # if interactive always display list
    then
        printf '\n\t List Updated:\n'
        view
    fi
}

view()
{
echo
if [ -r $FILE -a -s $FILE ]
then
    nl -w3 -ba -s'.  ' $FILE
else
    printf '*** There is nothing to do. ****\n'
fi
echo
}

show_help()
{
    cat <<-EndOfHelp

    $PROGNAME manages a simple "to do" list of tasks.
    If invoked with no command line arguments (other than -d)
    a menu-driven interactive user interface is provided.  From
    this menu you can view the list, add to the (end of the) list,
    remove an task (by its position) from the list, or display
    this help message.  Otherwise the action specified by
    the option provided is done.

    The to-do list is stored in a dot file in the user's home directory,
    with the simple format of one task per line.
EndOfHelp
usage
}

usage()
{
    cat <<-EndOfUsage

    USAGE:
       $PROGNAME [ -d ] [ -l ] [ -a TASK | -r NUM ]
       $PROGNAME [ -d ] [ -h | -v ]
     Options:
        -d, --debug       Turn on debugging mode.
        -a, --add="TASK"  Add the TASK string to the end of the list.
        -r, --remove=NUM  Remove the NUM-th task from the list.
        -l,--list         Display list of to-do tasks to standard output.
        -h, --help        Display this help message.
        -v, --version     Show author and version information.

    If the -l option is combined with either -a or -r, the modified list
    will be displayed.

EndOfUsage
}

remove()
{
if [ ! -r $FILE -o ! -s $FILE ]
then
    printf '*** There are no tasks to remove. ****\n'
    return 1
fi

if [ -z "$MENU" ]
then
    ITEM="$1"
fi

while :
do
    if [ -n "$MENU" ]
    then
        view
        printf '(Remove) Enter task number to remove '
        printf '(hit enter or "q" to cancel): '
        read ITEM
        [ -z "$ITEM" -o "$ITEM" = 'q' ] && return
    fi

    # Make sure $ITEM is a number, and between 1 and $NUM_TASKS:
    if ! expr "$ITEM" : '[[:digit:]][[:digit:]]*$' >/dev/null
    then printf "\t*** \"$ITEM\" is not a positive number! ***\n"
         [ -n "$MENU" ] && continue  || return 1
    fi

    NUM_TASKS=$(wc -l < $FILE)
    if [ "$ITEM" -lt 1 -o "$ITEM" -gt "$NUM_TASKS" ]
    then printf "\t*** You must enter a number between 1 and $NUM_TASKS! ***\n"
         [ -n "$MENU" ] && continue  || return 1
    fi

    TMP=$(mktemp)
    trap 'rm $TMP' 1 2 3 15
    if ! sed -e "${ITEM}d" "$FILE" >$TMP
    then printf "\t*** Couldn't remove task \"$ITEM\" from list. ***\n" >&2
         [ -n "$MENU" ] && continue  || return 1
    else cp $TMP "$FILE"
         rm $TMP
         if [ -n "$MENU" ]  # if interactive always display list
         then
             printf '\n\t List Updated:\n'
             view
         fi
         return
    fi
done
}

menu()
{
    # tput clear  # erase the screen
    MENU=1
    PS3='Enter number from menu ('q' to quit, hit enter to redraw menu): '
    select CMD in 'Add' 'Remove' 'View' 'Help'
    do
        case "$CMD" in
        Add)    add ;;
        Remove) remove ;;
        View)   view ;;
        Help)   show_help ;;
        *)      case "$REPLY" in
                q*|Q*)  exit 0 ;;
                *)      usage ;;
                esac ;;
        esac
    done
}

# Parse command line:
ARGS=$(getopt -n $PROGNAME -o dhla:r:v \
   -l debug,help,list,add:,remove:,version -- "$@")

# if the getopt detected an error, display help and quit:
if [ $? -ne 0 ]
then
    usage
    exit 1;
fi

# Reset the positional parameters:
# Note the Gnu getopt used adds quotes to the values.  To remove them
# we need to use eval as shown.
eval set -- "$ARGS"

MENU=
AOPT=
LOPT=
ROPT=

until [ "$1" = "--" ]
do
    case "$1" in
        -d|--debug)   set -x ;;
        -h|--help)    show_help; exit 0 ;;
        -l|--list)    LOPT=1 ;;
        -a|--add)     AOPT=1; TASK="$2";  shift ;;
        -r|--remove)  ROPT=1; NUM="$2"; shift ;;
        -v|--version) echo "$VERSION"; exit 0 ;;
    esac
    shift
done

shift  # Remove the '--'.

if [ $# -ne 0 ]
then
    usage
    exit 1
fi

if [ -z "$AOPT" -a -z "$ROPT" -a -z "$LOPT" ]
then
    menu
    exit
fi

if [ -n "$AOPT" -a -n "$ROPT" ]  # both not legal!
then
    usage
    exit 1
fi

if [ -n "$AOPT" ]
then
    TASK="$(eval echo $TASK)"  # remove the quotes added by getopt
    add "$TASK"
elif [ -n "$ROPT" ]
then
    NUM="$(eval echo $NUM)"  # remove the quotes added by getopt
    remove  "$NUM"
fi

# -l option legal by itself, or with -a or -r:
if [ -n "$LOPT" ]
then
    view
fi