#!/bin/ksh93 # @(#)timer 1.13 2005/01/19 # timer - ASCII visual timer (plus pop-up) # # this program displays a ruled progress bar and some other stats, # and visually counts off a given time; by default, it will beep # three times when the time is complete; please see the summary # and usage info for more details; # # Note: if you do not have the MIT Athena `Zephyr Notification Service' # installed, you will need to edit some lines below. # # Copyright (c) 2004 by Daniel E. Singer. All rights reserved. # Permission is granted to reproduce and distribute this program # with the following conditions: # 1) This copyright notice and the author identification below # must remain in the program and in any copies. # 2) Any modifications to the program must be clearly identified # in the source file. # # Written by Daniel E. Singer, Duke Univ. Dept of Computer Science, August 2003 # # For updates, please see: # http://www.cs.duke.edu/~des/scripts/ # ftp://ftp.cs.duke.edu/pub/des/scripts/ # # Also, see the article at: # http://www.unixreview.com/documents/s=8989/ur0405b/ # # Contact: # Daniel Singer # # Looping through the code # Counting second by second # Is my tea ready? # prog=${0##*/} # program basename integer tot_secs=60 # default number of seconds # # to disable popups (eg, you don't have the zephyr system), make # popup_cmd unset or null; you can substitute a different notification # technique by altering popup_cmd and the do_popup function # zwrite='zwrite' popup_cmd="$zwrite" # these can be altered via environment variables popup_text_color="${POPUP_COLOR:-firebrick}" popup_msg="${POPUP_MSG:- *** DONE ***\n}" optstr=":dhmq${popup_cmd:+z}" usage=" Usage: $prog [-dmq${popup_cmd:+z}] [time] $prog -h (help) " summary_opts="\ -d - count down instead of up; -h - print help message, then exit; -m - count with minutes, instead of just seconds; -q - quiet/quicker, no beeps at the end;${popup_cmd:+ -z - sends a zephyr when done;} time - a number (seconds), or a number followed by \"s\" (seconds) or \"m\" (minutes); default is $tot_secs seconds; Environment variables: RULER - ruler graphic${popup_cmd:+ POPUP_MSG - replacement message for the popup box POPUP_COLOR - popup text color} " summary=" \`$prog' is a simple, tty-based (ie, ASCII), low-precision timer. It displays and updates a progress bar, time elapsed, and percent complete. It is useful for short-term reminders, like timing tea. Example: $prog${popup_cmd:+ -z} 3m" dflag= # command line -d flag mflag= # command line -m flag qflag= # command line -q flag zflag= # command line -z flag # # this function produces a popup message when the timer is done; # if you do not have the MIT Athena `Zephyr Notification Service', you can # replace this with something else, or comment out popup_cmd (above) to # disable popups # function do_popup { color=$popup_text_color line="$popup_msg" print "@large{@bold{@color{$color}$line}}" | $popup_cmd $LOGNAME >&- 2>&- } # # make sure we can find the tput command, since it is vital, and it # might not be on some systems # if ! whence tput >&- ; then print "\a$prog: cannot find \"tput\" command." >&2 exit 1 fi # # make sure we can find the popup command # if [[ -n "$popup_cmd" ]] && ! whence "$popup_cmd" >&- ; then print "\a$prog: cannot find \"$popup_cmd\"." >&2 exit 1 fi # # process command line options and arguments # opt_error= while getopts "$optstr" option ; do case "$option" in d) dflag=1 ;; h) print "$summary" print "$usage" print "$summary_opts" exit 0 ;; m) mflag=1 ;; q) qflag=1 ;; z) if [[ $popup_cmd ]]; then zflag=1 else opt_error=1 fi ;; '?') opt_error=1 esac done shift $(( OPTIND - 1 )) if [[ $opt_error ]]; then print "\a$prog: option syntax error." >&2 print "$usage" >&2 exit 1 fi # # check remaining arguments # case "$#" in 0) ;; 1) time_arg="$1" ;; *) print "\a$prog: argument syntax error." >&2 print "$usage" >&2 exit 1 esac # # adjust the time arg; # if less than or equal to zero, then bail out, we don't want # to have to mess with division by zero and such # if [[ -z $time_arg ]]; then : do nothing elif [[ $time_arg == +([0-9])?([sS]) ]]; then (( tot_secs = ${time_arg%@([sS])} )) elif [[ $time_arg == +([0-9])?(.*([0-9]))@([mM]) ]]; then (( tot_secs = ${time_arg%@([mM])} * 60 )) else # ! good secs print "\a$prog: argument syntax error." >&2 print "$usage" >&2 exit 1 fi (( tot_secs <= 0 )) && exit 0 [[ $mflag ]] && typeset m_tot_time=$( printf "%d:%02d" $(( tot_secs / 60 )) $(( tot_secs % 60 )) ) # # some variations on the ruler; # it might come from the environment... # #ruler="|----+----+----+----+----@----+----+----+----+----|" # 50 #ruler="|----+o---+-o--+--o-+---o+----@----+o---+-o--+--o-+---o+----|" # 60 #ruler="[-----o-----o-----o-----o-----@-----o-----o-----o-----o-----]" # 60 ruler="[=====o=====o=====o=====o=====@=====o=====o=====o=====o=====]" # 60 ruler="${RULER:-[=====o=====o=====o=====o=====@=====o=====o=====o=====o=====]}" # 60 integer rwidth=$(( ${#ruler} - 1 )) # ruler width integer msg_pos=$(( rwidth + 1 )) # where the stats will go integer swidth=${#tot_secs} # seconds width [[ $mflag ]] && (( swidth = ${#m_tot_time} )) # # need an easy way to redraw the current area of the ruler, so # we copy it into an array of single characters # integer i for (( i=0; i <= rwidth; ++i )); do r_list[i]=${ruler:i:1} done # # get number of text rows in window, we'll want to place the ruler near # the bottom of the window # integer lines=$(( $( tput lines ) - 2 )) # # list of cursor positioning strings, derived from terminfo # for (( i=0; i <= rwidth+2; ++i )); do cp_list[i]=$( tput cup $lines $i ) done # # the cursor will rotate thru this list of representations as the # seconds tick off # set -A tumble '|' '/' '-' '\' # # starting the seconds at -1 means that the ruler will be displayed # at "0" for one second upon startup; this actually adds a second # to the total time, but it improves the visual impact # integer secs=-1 integer cpos=0 old_pos # # position the cursor # usage: go position # function go { print -n "${cp_list[$1]}" } # # display the time elapsed and percentage done # usage: pr_stats # function pr_stats { typeset ts=$tot_secs typeset s=$secs [[ $dflag ]] && s=$(( tot_secs - secs )) if [[ $mflag ]] then ts=$m_tot_time s=$( printf "%d:%02d" $(( s / 60 )) $(( s % 60 )) ) fi go $msg_pos printf " %*s/%s %3s%%" $swidth "$s" "$ts" "$(( secs*100/tot_secs ))" } # # position to the bottom of the screen and print the ruler; # a little extra scrolling is provided to help clear the display # area # function setup { print go 0 print -- "\n$ruler" } trap setup CONT trap 'go 0; print -n -- "$ruler"; go $cpos; print -n "!\n"; kill -STOP $$' TSTP setup # # count seconds, keep display updated # while true; do (( ++secs )) pr_stats # keep track of position so it can be redrawn as the cursor # tumbles and eventually moves on (( old_pos = cpos )) # current position is a ratio of current time to total time, # adjusted for ruler width (( cpos = ( secs * rwidth ) / tot_secs )) # repair the ruler? if (( old_pos != cpos )); then go $old_pos print -n -- "${r_list[old_pos]}" fi go $cpos # exit loop? # if time is up, then fix up the display and break out if (( secs == tot_secs )); then print -n -- "${r_list[cpos]}" pr_stats go $cpos break fi # do the cursor print -n -- "${tumble[secs % 4]}" go $cpos sleep 1 done # # do the popup # if [[ $zflag ]]; then do_popup fi # # if quiet, don't do the beeps and cursor movements at the end # if [[ ! $qflag ]]; then # print three beeps, while advancing the cursor print -n "\a" sleep 1 go $(( cpos + 1 )) print -n "\a" sleep 1 go $(( cpos + 2 )) print -n "\a" sleep 1 fi # # move the cursor down so that the prompt does not overwrite the # ruler and for spacing # print print exit