Posted to tcl by msiism at Tue Jun 12 23:18:11 GMT 2018view pretty

#!/bin/bash
#
# VHG
#
# A versatile virtual hourglass
#
# Version 1.0
#
#
# Copyright Michael Siegel 2018
#
# <LICENSE NOTE> (GPL 3)

# --- SHELL CONFIGURATION ---


# --- ENVIRONMENT CHECKS ---

# check for sound playing utilities?
  # aplay? (WAV)
  # mpeg
  # ogg

# --- VARIABLE DECLARATIONS ---

cmd_name="${0##*/}"  # Get basename of this script
version="1.0"

cmd_info="A versatile virtual hourglass"
use_info=""  # Add usage info
help_info="Run '$cmd_name -h' for help"
sw_info=""  # Add info on switches
help="$cmd_name $version\n$cmd_info\n\n$use_info\n\n$sw_info"

# An array containing all available switches
sw_list=(-h --help -V --version -t --time -m --message -s --sound)
sw_count="${#sw_list[@]}"  # Number of elements in $sw_list

t=  # time value
m=  # alarm message (can start with - or --)
# Defining a default message here and using that as an argument to f_alarm in
# Main when no message was set would make more than half of the code in f_alarm
# obsolete. But it's kind of illogical and bad style.
s=  # Will be set to 1 if -s is specified so vhg will play a sound on alarm.
    # Do not set this to any value. Parameter handling relies on $s being
    # not set at the beginning.
s_file=~/.vhg/alarm.wav  # Location of alarm sound file (Do not quote, or ~
                         # won't be expanded.)

# --- FUNCTIONS ---

function f_t_check {
  # Perform string checks on the specified time value and set 't' conditonally

  local t_spec="$1"

  if [[ "$t_spec" != +([[:digit:]])*([d|h|m|s]) ]]  # Wrong...
  # If $1 is NOT one or more (+()) digit(s), optionally followed by one of the
  # letters d, h, m or s (*() is "zero or more")
  then
    printf "$cmd_name: Invalid argument\n" >&2
    exit 1
 else
   t="$t_spec"
 fi
}

function f_t_conv {
  # Convert the specified time value into a word expression that will be used
  # as the default alarm message

  local t_spec="$1"
  local t_dig=  # Digit part of the specified time
  local t_unit=  # Unit part of the specified time
  local t_be=   # Form of be to use in the message (is or are)

  case "$t_spec" in
    *d)  # Time specified in days
      t_dig="${t_spec%d}"  # Get the digit part of the specified time
      if [ "$t_dig" -gt 1 ]  # If the number is greater than 1
      then
        t_unit="days"
        t_be="are"
      else
        t_unit="day"
        t_be="is"
      fi
    ;;
    *h)  # Time specified in hours
      t_dig="${t_spec%h}"  # Get the digit part of the specified time
      if [ "$t_dig" -gt 1 ]  # If the number is greater than 1
      then
        t_unit="hours"
        t_be="are"
      else
        t_unit="hour"
        t_be="is"
      fi
    ;;
    *m)  # Time specified in minutes
      t_dig="${t_spec%m}"  # Get the digit part of the specified time
      if [ "$t_dig" -gt 1 ]  # If the number is greater than 1
      then
        t_unit="minutes"
        t_be="are"
      else
        t_unit="minute"
        t_be="is"
      fi
    ;;
    *s|*)  # Time specified in second (30s or 30, for example)
      t_dig="${t_spec%s}"  # Get the digit part of the specified time
      if [ "$t_dig" -gt 1 ]  # If the number is greater than 1
      then
        t_unit="seconds"
        t_be="are"
      else
        t_unit="second"
        t_be="is"
      fi
    ;;
  esac

  printf "$t_dig $t_unit $t_be up!"
}

function f_alarm {
  # Print an alarm message on the CLI or via Xmessage, conditionally.
  # Also play a sound if -s is specified.

  local m_spec="$m"
  local m_default="$(f_t_conv "$t")"

  if [ ! "$m_spec" ]
  then
    m_spec="$m_default"
  fi

  if [ "$s" == 1 ]
  then
    aplay "$s_file" > /dev/null 2>&1 &
    # if you don't redirect the output, aplay will print to stdout, bringing
    # itself back into the foreground and block the shell until you hit enter
    # check for aplay up in the environment checks section
  fi

  # Select output module
  if [ "$DISPLAY" ]  # If the script is run from X
  then
    if [ "$(type -p tkbash)" ]
    then
      ./gui/vhg-tk "$m_spec"  # Use tkbash
    else
      ./gui/vhg-x "$m_spec"  # Use Xmessage
    fi
  else
    printf "%s\n" "$cmd_name: $m_spec"
  fi
}


# --- PARAMETER PROCESSING ---

# display help content on user fuck-up

if [ "$#" == 0 ]  # If the script is run without parameters
then
  printf "%b\n" "$help"
  exit
else
  # Process parameters in order of complexity
  i="$#"
  while [ "$#" -gt 0 ]
  do
    if [ "$#" -eq "$i" ]
    # Process basic switches only if they occur first and let them fall victim to
    # general error handling otherwise.
    then
      case "$1" in
        -h|--help)
          printf "%b\n" "$help"
          exit
        ;;
        -V|--version)
          printf "%s\n" "$cmd_name $version"
          exit
        ;;
      esac
    fi
    case "$1" in
      -s|--sound)
        if [ "$s" ]  # If 's' has already been set
        then
          printf "$cmd_name: -s already specified\n" >&2
          exit 1
        else
          shift
          s=1
        fi
      ;;
      -t|--time)
        if [ "$t" ]  # If 't' has already been set
        then
          printf "$cmd_name: -t already specified\n" >&2
          exit 1
        else
          shift
          if [ "$#" -eq 0 ]  # If there is no following parameter
          then
            printf "$cmd_name: Missing argument\n" >&2
            # printf "$help_info\n"
            exit 1
          else
            # Check if $1 is a known/valid switch
            valid=0
            for((c=0; c<"$sw_count"; c++))  # Loop through the array of switches
            do
              if [ "$1" == "${sw_list[$c]}" ]  # If array element matches $1
              then
                valid=1
                break
              fi
            done
            if [ "$valid" == 1 ]  # If $1 is a known switch
            then
              printf "$cmd_name: Missing argument\n" >&2
              exit 1
            else
              f_t_check "$1"  # Sets the t variable
              shift
            fi
          fi
        fi
      ;;
      -m|--message)
        if [ "$m" ]  # If 'm' has already been set
        then
          printf "$cmd_name: -m already specified\n" >&2
          exit 1
        else
          shift
          if [ "$#" -eq 0 ]  # If there is no following parameter
          then
            printf "$cmd_name: Missing argument\n" >&2
            exit 1
          else
            # Check if $1 is a known/valid switch
            valid=0
            for((c=0; c<"$sw_count"; c++))
            do
              if [ "$1" == "${sw_list[$c]}" ]  # If array element matches $1
              then
                valid=1
                break
              fi
            done
            if [ "$valid" == 1 ]
            then
              printf "$cmd_name: Missing argument\n" >&2
              exit 1
            else
              m="$1"  # Set $1 as the alarm message
              shift
            fi
          fi
        fi
      ;;
      *)
        printf "$cmd_name: Invalid argument\n" >&2
        # like, e.g., --help sepcified at the second position
        exit 1
      ;;
    esac
  done
fi

# --- MAIN ---

if [ "$t" ]
then
  # Run sleep and f_alarm as one job in the background so the shell won't be
  # blocked.
  sleep "$t" && f_alarm &
  # The above will print sth out, bring the job back to the foreground and
  # therefore block the shell until the user hits enter.
else  # No time value specified
  printf "$cmd_name: Missing argument\n" >&2
fi