Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Functions using zsh/datetime



Here are three functions that use the zsh/datetime module; one day they
may appear in Functions/Datetime but just in case I abandon them I
thought I'd better post them in the current fairly rough and not very
well documented state.

asched is an asynchronous version of the sched builtin.  The syntax is
the same.  It doesn't make use of jobs at the moment (jobs get created
anyway in a shell with the monitor option set, but the function uses
PIDs) since they're not that easy to access from a function (there's no
way to get the last job started in the background, unlike the last
process).

matchtime attempts to match a date string against a number of seconds
since the epoch (as returned by zsh/datetime's $EPOCHSECONDS).

calendar uses matchtime to implement the decreasingly standard
external command of the same name, with a few extensions including the
ability to use asched to create messages.

-- 
Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


This message has been scanned for viruses by BlackSpider MailControl - www.blackspider.com
# Asynchronous variant of sched.
# The syntax is the same.
#
# TODO: would be better if we could use job handling directly
# but the programming interface to that isn't great, so currently
# it uses processes (though these will be in jobs if the monitor
# option is set).
emulate -L zsh
setopt extendedglob

zmodload -i zsh/datetime || return 1

integer -g ASCHED_JOB
typeset -ga asched_processes

local strtime proc
integer now=$EPOCHSECONDS hhour hour mminute minute diff time i procwid
local -a match mbegin mend

# Use guesswork to decide whether the process has been run.
for (( i = 1; i <= ${#asched_processes}; i++ )); do
  time=${${asched_processes[$i]##<-> #}%% *}
  if (( now > time )); then
    # it's all over
    asched_processes[$i]=
  fi
done

if [[ $1 = -(#b)(<->) ]]; then
  local killproc="${asched_processes[$match[1]]%% *}"
  if [[ -n $killproc ]]; then
    kill $killproc
    asched_processes[$match[1]]=
  else
    print -r "asched: no such asched job or time already past"
  fi
elif (( $# == 0 )); then
  # the width of the length of the array, for padding...
  procwid=${#:-${#asched_processes}}
  for (( i = 1; i <= ${#asched_processes}; i++ )); do
    proc=${asched_processes[$i]}
    if [[ -n $proc ]]; then
      print "${(l.$procwid.)i} ${proc##<-> #<-> #}"
    fi
  done
  return 0
elif [[ $1 != (#b)(+|)(<->):(<->) || $# -lt 2 ]]; then
  print "Usage: asched [+]HH:MM job
asched [ -<item> ]" >&2
  return 1
fi

shift
local job="$*"

hhour=$match[2]
mminute=$match[3]

if [[ -n $match[1] ]]; then
  # relative
  (( diff = (hhour * 60 + mminute) * 60 ))
else
  # absolute, so work out when now is.
  # assume today, so if it's in the past run job immediately.
  strftime -s hour %H $now
  strftime -s minute %M $now

  (( diff = ((hhour - hour) * 60 + mminute - minute) * 60 ))
  if (( diff < 0 )); then
    # do it in subshell since user expects this not to affect main shell
    (eval $job)
    return 0
  fi
fi

(( ASCHED_JOB++ ))
(( time = now + diff ))
strftime -s strtime "%a %b %d %H:%M:%S" $time

eval "(: asched job $ASCHED_JOB
sleep $diff; $job) &"
# Format: background task, Epoch time, string time, job name.
# The last two are for human consumption.
asched_processes[$ASCHED_JOB]="$! $time $strtime $job"
# Input: a line and an epoch time, which defaults to now.
# Returns: 0 if the line contains today's date, else 1.
# Tries various date formats: see comments below.
# (Should really be called matchdate, in fact.)
#
# TODO: can get confused about DAY MONTH YEAR and MONTH DAY YEAR,
# could use locale as some kind of hint.  (User can help by using
# 1st, 2nd, 3rd, 4th, ... for day, or by using ISOesque
# 2005-12-02.)
#
# If -t is passed, look for a time in the format HH:MM (must be in the 24-hour
# clock at the moment) attached to the date.  If it's greater than or equal
# to the epoch time, set REPLY to HH:MM.  This is intended to be used
# to set a job for the given time.

emulate -L zsh
setopt extendedglob

zmodload -i zsh/datetime || return 1

integer checktime
local opt

while getopts "t" opt; do
  case $opt in
    (t)
    checktime=1
    ;;
  esac
done
shift $(( OPTIND - 1 ))

local line=$1
integer now=${2:-$EPOCHSECONDS} stat=1 patstart patend
local -a match mbegin mend

local year monthnum monthname day hour minute hhour mminute

strftime -s year %Y $now
strftime -s monthnum %m $now
monthnum=${monthnum##0}
strftime -s monthname %b $now
strftime -s day %d $now
day=${day##0}
strftime -s hour %H $now
strftime -s minute %M $now

case $line in
  # Look for DAY[th/st/rd] MONTH[,] YEAR
  (*(#b)((#i)((#s)|[[:blank:]])0#${day}(|rd|st|th)[[:blank:]]##(${monthname}[a-z]#|0#${monthnum})[,[:blank:]]##(${year}|${year[-2,-1]})([[:blank:],\;:.]|(#e)))*)
  stat=0
  ;;

  # Look for MONTH DAY[th/st/rd][,] YEAR
  (*(#b)((#i)((#s)|[[:blank:]])(${monthname}[a-z]#|0#${monthnum})[[:blank:]]##0#${day}(|rd|st|th)[,[:blank:]]##(${year}|${year[-2,-1]})([[:blank:],\;:.]|(#e)))*)
  stat=0
  ;;

  # Look for YEAR[-]MONTH[-]DAY
  (*(#b)((#i)((#s)|[[:blank:]])(${year}|${year[-2,-1]})[-]#(${monthname}[a-z]#|0#${monthnum})[-]#0#${day}([[:blank:],\;:.]|(#e)))*)
  stat=0
  ;;
esac

(( stat != 0 )) && return 1

if (( checktime )); then
  REPLY=
  patstart=$mbegin[1]
  patend=$mend[1]
  if [[ $patstart -gt 1 && $line[1,$patstart-1] = *((#s)|[[:blank:]])(#b)(<->):(<->)[[:blank:],\;:.]# ]]; then
    hhour=$match[1]
    mminute=$match[2]
  elif [[ $patend -lt ${#line} && $line[$patend+1,-1] = [[:blank:],\;:.]#(#b)(<->):(<->)(|[[:blank:]]*) ]]; then
    hhour=$match[1]
    mminute=$match[2]
  fi

  # assume 24-hour clock for now
  if [[ -n $hhour$mminute ]] && (( hour < hhour || (hour == hhour && minute <= mminute ) )); then
    REPLY=${hhour}:${mminute}
  fi
fi

return 0
emulate -L zsh
setopt extendedglob

local line opt REPLY
local calendar sched showprog
integer time
local -a times

zmodload -i zsh/datetime || return 1
zmodload -i zsh/zutil || return 1

# Read the calendar file from the calendar-file style
zstyle -s ':datetime:calendar:' calendar-file calendar || calendar=~/calendar
# Read the programme to show the message from the show-prog style.
# (Requires option -a or -s to be useful.)
zstyle -s ':datetime:calendar:' show-prog showprog || showprog="print -r"

[[ -f $calendar ]] || return 1

while getopts "aC:sS:x" opt; do
  case $opt in
    # Use the "asched" function to generate a message asynchronously.
    (a)
    sched=asched
    ;;

    (C)
    # Pick the calendar file, overriding style and default.
    calendar=$OPTARG
    ;;

    (s)
    # Use the "sched" builtin to generate a message before a prompt.
    sched=sched
    ;;

    (S)
    # Explicitly specify a show programme, overriding style and default.
    showprog=$OPTARG
    ;;

    (x)
    # Use xmessage as the show programme.  Best used with -a.
    showprog=xmessage
    ;;

    (*)
    return 1
    ;;
  esac
done
shift $(( OPTIND - 1 ))

time=$EPOCHSECONDS
# search today and tomorrow
times=($time $(( time + 24*60*60 )))

autoload -Uz matchtime

while read -r line; do
  if matchtime -t $line $times[1]; then
    print -r $line
    if [[ -n $sched && -n $REPLY ]]; then
      # eval needed in place of closures on REPLY and line.
      # i like perl.
      eval "function sched_$REPLY {
	unfunction sched_$REPLY
	$showprog \"Calendar entry is now due: \" ${(q)line} >&$TTY
      }"
      $sched $REPLY sched_$REPLY
    fi
  else
    for time in ${times[2,-1]}; do
      matchtime -t $line $time && print -r $line && break
    done
  fi
done <$calendar

return 0


Messages sorted by: Reverse Date, Date, Thread, Author