about summary refs log tree commit diff
path: root/Functions/Calendar/calendar
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2006-12-01 10:23:06 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2006-12-01 10:23:06 +0000
commit6b1b34d1da6b0db599c026e17df011ad6c6b3a30 (patch)
tree3559a344ebd62a8b0af17c70b9b0fdaeae5e2299 /Functions/Calendar/calendar
parentab8b8026dcc17f7c3d8dcfba7dba046b1ac7c42b (diff)
downloadzsh-6b1b34d1da6b0db599c026e17df011ad6c6b3a30.tar.gz
zsh-6b1b34d1da6b0db599c026e17df011ad6c6b3a30.tar.xz
zsh-6b1b34d1da6b0db599c026e17df011ad6c6b3a30.zip
c.f. 23023: new calendar function system.
Diffstat (limited to 'Functions/Calendar/calendar')
-rw-r--r--Functions/Calendar/calendar356
1 files changed, 356 insertions, 0 deletions
diff --git a/Functions/Calendar/calendar b/Functions/Calendar/calendar
new file mode 100644
index 000000000..124fd9786
--- /dev/null
+++ b/Functions/Calendar/calendar
@@ -0,0 +1,356 @@
+emulate -L zsh
+setopt extendedglob
+
+# standard ctime date/time format
+local ctime="%a %b %d %H:%M:%S %Z %Y"
+
+local line REPLY REPLY2 userange pruned
+local calendar donefile sched newfile warnstr mywarnstr
+integer time start stop today ndays y m d next=-1 shown done nodone
+integer verbose warntime mywarntime t tsched i rstat remaining
+integer showcount icount
+local -a calendar_entries
+local -a times calopts showprog lockfiles match mbegin mend
+
+zmodload -i zsh/datetime || return 1
+zmodload -i zsh/zutil || return 1
+
+autoload -U calendar_{read,scandate,show,lockfiles}
+
+# Read the calendar file from the calendar-file style
+zstyle -s ':datetime:calendar:' calendar-file calendar || calendar=~/calendar
+newfile=$calendar.new.$HOST.$$
+zstyle -s ':datetime:calendar:' done-file donefile || donefile="$calendar.done"
+# Read the programme to show the message from the show-prog style.
+zstyle -a ':datetime:calendar:' show-prog showprog ||
+  showprog=(calendar_show)
+# Amount of time before an event when it should be flagged.
+# May be overridden in individual entries
+zstyle -s ':datetime:calendar:' warn-time warnstr || warnstr="0:05"
+
+if [[ -n $warnstr ]]; then
+  if [[ $warnstr = <-> ]]; then
+    (( warntime = warnstr ))
+  elif ! calendar_scandate -ar $warnstr; then
+    print >&2 \
+      "warn-time value '$warnstr' not understood; using default 5 minutes"
+    warnstr="5 mins"
+    (( warntime = 5 * 60 ))
+  else
+    (( warntime = REPLY ))
+  fi
+fi
+
+[[ -f $calendar ]] || return 1
+
+# We're not using getopts because we want +... to refer to a
+# relative time, not an option, and allow some other additions
+# like handling -<->.
+integer opti=0
+local opt optrest optarg
+
+while [[ ${argv[opti+1]} = -* ]]; do
+  (( opti++ ))
+  opt=${argv[opti][2]}
+  optrest=${argv[opti][3,-1]}
+  [[ -z $opt || $opt = - ]] && break
+  while [[ -n $opt ]]; do
+    case $opt in
+      ########################
+      # Options with arguments
+      ########################
+      ([CnS])
+      if [[ -n $optrest ]]; then
+	optarg=$optrest
+	optrest=
+      elif (( opti < $# )); then
+	optarg=$argv[++opti]
+	optrest=
+      else
+	print -r "$0: option -$opt requires an argument." >&2
+	return 1
+      fi
+      case $opt in
+	(C)
+	# Pick the calendar file, overriding style and default.
+	calendar=$optarg
+	;;
+
+	(n)
+	# Show this many remaining events regardless of date.
+	showcount=$optarg
+	if (( showcount <= 0 )); then
+	  print -r "$0: option -$opt requires a positive integer." >&2
+	  return 1
+	fi
+	;;
+
+	(S)
+	# Explicitly specify a show programme, overriding style and default.
+	# Colons in the argument are turned into space.
+	showprog=(${(s.:.)optarg})
+	;;
+      esac
+      ;;
+
+      ###########################
+      # Options without arguments
+      ###########################
+      (d)
+      # Move out of date items to the done file.
+      (( done = 1 ))
+      ;;
+
+      (D)
+      # Don't use done; needed with sched
+      (( nodone = 1 ))
+      ;;
+
+      (r)
+      # Show all remaining options in the calendar, i.e.
+      # respect start time but ignore end time.
+      # Any argument is treated as a start time.
+      (( remaining = 1 ))
+      ;;
+
+      (s)
+      # Use the "sched" builtin to scan at the appropriate time.
+      sched=sched
+      (( done = 1 ))
+      ;;
+
+      (v)
+      # Verbose
+      verbose=1
+      ;;
+
+      (<->)
+      # Shorthand for -n <->
+      showcount=$opt
+      ;;
+
+      (*)
+      print "$0: unrecognised option: -$opt" >&2
+      return 1
+      ;;
+    esac
+    opt=$optrest[1]
+    optrest=$optrest[2,-1]
+  done
+done
+calopts=($argv[1,opti])
+shift $(( opti ))
+
+# Use of donefile requires explicit or implicit option request, plus
+# no explicit -D.  It may already be empty because of the style.
+(( done && !nodone )) || donefile=
+
+if (( $# > 1 || ($# == 1 && remaining) )); then
+  if [[ $1 = now ]]; then
+    start=$EPOCHSECONDS
+  elif [[ $1 = <-> ]]; then
+    start=$1
+  else
+    if ! calendar_scandate -a $1; then
+      print "$0: failed to parse date/time: $1" >&2
+      return 1
+    fi
+    start=$REPLY
+  fi
+  shift
+else
+  # Get the time at which today started.
+  y=${(%):-"%D{%Y}"} m=${(%):-"%D{%m}"} d=${(%):-"%D{%d}"}
+  strftime -s today -r "%Y/%m/%d" "$y/$m/$d"
+  start=$today
+fi
+# day of week of start time
+strftime -s wd "%u" $start
+
+if (( $# && !remaining )); then
+  if [[ $1 = +* ]]; then
+    if ! calendar_scandate -ar ${1[2,-1]}; then
+      print "$0: failed to parse relative time: $1" >&2
+      return 1
+    fi
+    (( stop = start + REPLY ))
+  elif [[ $1 = <-> ]]; then
+    stop=$1
+  else
+    if ! calendar_scandate -a $1; then
+      print "$0: failed to parse date/time: $1" >&2
+      return 1
+    fi
+    stop=$REPLY
+  fi
+  if (( stop < start )); then
+    strftime -s REPLY $ctime $start
+    strftime -s REPLY2 $ctime $stop
+    print "$0: requested end time is before start time:
+  start: $REPLY
+  end: $REPLY2" >&2
+    return 1
+  fi
+  shift
+else
+  # By default, show 2 days.  If it's Friday (5) show up to end
+  # of Monday (4) days; likewise on Saturday show 3 days.
+  # If -r, this is calculated but not used.  This is paranoia,
+  # to avoid an unusable value of stop; but it shouldn't get used.
+  case $wd in
+    (5)
+    ndays=4
+    ;;
+
+    (6)
+    ndays=3
+    ;;
+
+    (*)
+    ndays=2
+    ;;
+  esac
+  stop=$(( start + ndays * 24 * 60 * 60 ))
+fi
+
+if (( $# )); then
+  print "Usage: $0 [ start-date-time stop-date-time ]" >&2
+  return 1
+fi
+
+autoload -Uz matchdate
+
+[[ -n $donefile ]] && rm -f $newfile
+
+if (( verbose )); then
+  print -n "start: "
+  strftime $ctime $start
+  print -n "stop: "
+  if (( remaining )); then
+    print "none"
+  else
+    strftime $ctime $stop
+  fi
+fi
+
+# start of block for following always to clear up lockfiles.
+{
+  if [[ -n $donefile ]]; then
+    # Attempt to lock both $donefile and $calendar.
+    # Don't lock $newfile; we've tried our best to make
+    # the name unique.
+    calendar_lockfiles $calendar $donefile || return 1
+  fi
+
+  calendar_read $calendar
+  for line in $calendar_entries; do
+    # This call sets REPLY to the date and time in seconds since the epoch,
+    # REPLY2 to the line with the date and time removed.
+    calendar_scandate -as $line || continue
+    (( t = REPLY ))
+
+    # Look for specific warn time.
+    pruned=${REPLY2#(|*[[:space:],])WARN[[:space:]]}
+    (( mywarntime = warntime ))
+    mywarnstr=$warnstr
+    if [[ $pruned != $REPLY2 ]]; then
+      if calendar_scandate -ars $pruned; then
+	(( mywarntime = REPLY ))
+	mywarnstr=${pruned%%"$REPLY2"}
+      fi
+    fi
+
+    if (( verbose )); then
+      print "Examining: $line"
+      print -n "  Date/time: "
+      strftime $ctime $t
+      if [[ -n $sched ]]; then
+	print "  Warning $mywarntime seconds ($mywarnstr) before"
+      fi
+    fi
+    (( shown = 0 ))
+    if (( t >= start && (remaining || t <= stop || icount < showcount) ))
+    then
+      $showprog $start $stop "$line"
+      (( shown = 1, icount++ ))
+    elif [[ -n $sched ]]; then
+      (( tsched = t - mywarntime ))
+      if (( tsched >= start && tsched <= stop)); then
+	$showprog $start $stop "due in ${mywarnstr}: $line"
+      fi
+    fi
+    if [[ -n $sched ]]; then
+      if (( t - mywarntime > EPOCHSECONDS )); then
+	# schedule for a warning
+	(( tsched = t - mywarntime ))
+      else
+	# schedule for event itself
+	(( tsched = t ))
+      fi
+      if (( (tsched > EPOCHSECONDS || ! shown) &&
+	    (next < 0 || tsched < next) )); then
+	(( next = tsched ))
+      fi
+    fi
+    if [[ -n $donefile ]]; then
+      if (( t <= EPOCHSECONDS && shown )); then
+	# Done and dusted.
+	# TODO: handle repeated times from REPLY2.
+	if ! print -r $line >>$donefile; then
+	  if (( done != 3 )); then
+	    (( done = 3 ))
+	    print "Failed to append to $donefile" >&2
+	  fi
+	elif (( done != 3 )); then
+	  (( done = 2 ))
+	fi
+      else
+	# Still not over.
+	if ! print -r $line >>$newfile; then
+	  if (( done != 3 )); then
+	    (( done = 3 ))
+	    print "Failed to append to $newfile" >&2
+	  fi
+	elif (( done != 3 )); then
+	  (( done = 2 ))
+	fi
+      fi
+    fi
+  done
+
+  if [[ -n $sched ]]; then
+    if [[ $next -ge 0 ]]; then
+      # Remove any existing calendar scheduling.
+      # Luckily sched doesn't delete its schedule in a subshell.
+      sched | while read line; do
+	if [[ $line = (#b)[[:space:]]#(<->)[[:space:]]##*[[:space:]]'calendar -s'* ]]; then
+	  # End of pipeline run in current shell, so delete directly.
+	  sched -1 $match[1]
+	fi
+      done
+      $sched $next calendar "${calopts[@]}" $next $next
+    else
+      $showprog $start $stop \
+"No more calendar events: calendar not rescheduled.
+Run \"calendar -s\" again if you add to it."
+    fi
+  fi
+
+  if (( done == 2 )); then
+    if ! mv $calendar $calendar.old; then
+      print "Couldn't back up $calendar to $calendar.old.
+New calendar left in $newfile." >&2
+      (( rstat = 1 ))
+    elif ! mv $newfile $calendar; then
+      print "Failed to rename $newfile to $calendar.
+Old calendar left in $calendar.old." >&2
+      (( rstat = 1 ))
+    fi
+  elif [[ -n $donefile ]]; then
+    rm -f $newfile
+  fi
+} always {
+  (( ${#lockfiles} )) && rm -f $lockfiles
+}
+
+return $rstat