summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--Doc/Zsh/calsys.yo52
-rw-r--r--Functions/Calendar/calendar9
-rw-r--r--Functions/Calendar/calendar_edit21
-rw-r--r--Functions/Calendar/calendar_lockfiles16
-rw-r--r--Functions/Calendar/calendar_scandate139
-rw-r--r--Functions/Calendar/calendar_showdate32
7 files changed, 189 insertions, 92 deletions
diff --git a/ChangeLog b/ChangeLog
index c0404b199..039cac75c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2007-03-26  Peter Stephenson  <pws@csr.com>
+
+	* unposted (follow-on from 23228): Doc/Zsh/calsys.yo,
+	Functions/Calendar/calendar{,_edit,lockfiles,scandate,showdate}:
+	New calendar_edit (looks up file to edit and locks it);
+	calendar -a option; calendar_showdate -f fmt option;
+	make calendar_lockfiles use zsh/select to get higher resolution
+	timer to jitter delay; apply the summer time fix to
+	"<month>, <nth> <frob>day" as well; allow "2nd" as an
+	ordinal (also 1nd and 3nd, 4nd, ... since we aren't interested
+	in checking good English).
+
 2007-03-25  Peter Stephenson  <p.w.stephenson@ntlworld.com>
 
 	* usres/11333: Completion/Unix/Command/_ssh: users-hosts
diff --git a/Doc/Zsh/calsys.yo b/Doc/Zsh/calsys.yo
index 57902cbbb..f837a04fe 100644
--- a/Doc/Zsh/calsys.yo
+++ b/Doc/Zsh/calsys.yo
@@ -309,8 +309,8 @@ subsect(Calendar system functions)
 
 startitem()
 findex(calendar)
-xitem(tt(calendar) [ tt(-dDsv) ] [ tt(-C) var(calfile) ] [ -n var(num) ] [ tt(-S) var(showprog) ] [ [ var(start) ] var(end) ])(
-item(tt(calendar -r) [ tt(-dDrsv) ] [ tt(-C) var(calfile) ] [ -n var(num) ] [ tt(-S) var(showprog) ] [ var(start) ])(
+xitem(tt(calendar) [ tt(-adDsv) ] [ tt(-C) var(calfile) ] [ -n var(num) ] [ tt(-S) var(showprog) ] [ [ var(start) ] var(end) ])(
+item(tt(calendar -r) [ tt(-adDrsv) ] [ tt(-C) var(calfile) ] [ -n var(num) ] [ tt(-S) var(showprog) ] [ var(start) ])(
 Show events in the calendar.
 
 With no arguments, show events from the start of today until the end of
@@ -338,6 +338,10 @@ tt(~/.zshrc) file.
 Options:
 
 startitem()
+item(tt(-a))(
+Show all items in the calendar, regardless of the tt(start) and
+tt(end).
+)
 item(tt(-C) var(calfile))(
 Explicitly specify a calendar file instead of the value of
 the tt(calendar-file) style or the the default tt(~/calendar).
@@ -418,8 +422,19 @@ option tt(-L) indicates that tt(calendar_add) does not need to lock the
 calendar file up the old one as it is already locked.  These options will
 not usually be needed by users.
 )
+findex(calendar_edit)
+item(tt(calendar_edit))(
+This calls the user's editor to edit the calendar file.  The editor
+is given by the variable tt(VISUAL), if set, else the variable tt(EDITOR).
+If the calendar scheduler was running, then after editing the file
+tt(calendar -s) is called to update it.
+
+This function locks out the calendar system during the edit.
+Hence it should be used to edit the calendar file if there is any
+possibility of a calendar event occurring meanwhile.
+)
 findex(calendar_showdate)
-item(tt(calendar_showdate) [ tt(-r) ] var(date-spec ...))(
+item(tt(calendar_showdate) [ tt(-r) ] [ tt(-f) var(fmt) ] var(date-spec ...))(
 The given var(date-spec) is interpreted and the corresponding date and
 time printed.  If the initial var(date-spec) begins with a tt(PLUS()) or
 tt(-) it is treated as relative to the current time; var(date-spec)s after
@@ -427,8 +442,16 @@ the first are treated as relative to the date calculated so far and
 a leading tt(PLUS()) is optional in that case.  This allows one to
 use the system as a date calculator.  For example, tt(calendar_showdate '+1
 month, 1st Friday') shows the date of the first Friday of next month.
+
 With the option tt(-r) nothing is printed but the value of the date and
 timein seconds since the epoch is stored in the parameter tt(REPLY).
+
+With the option tt(-f) var(fmt) the given date/time conversion format
+is passed to tt(strftime); see notes on the tt(date-format) style below.
+
+In order to avoid ambiguity with negative relative date specifications,
+options must occur in separate words; in other words, tt(-r) and tt(-f)
+should not be combined in the same word.
 )
 findex(calendar_sort)
 item(tt(calendar_sort))(
@@ -568,14 +591,24 @@ item(tt(calendar_lockfiles))(
 Attempt to lock the files given in the argument.  To prevent
 problems with network file locking this is done in an ad hoc fashion
 by attempting to create a symbolic link to the file with the name
-var(file)tt(.lockfile).  Otherwise, however, the function is not
-specific to the calendar system.  Three attempts are made to lock
-the file before giving up.
+var(file)tt(.lockfile).  No other system level functions are used
+for locking, i.e. the file can be accessed and modified by any
+utility that does not use this mechanism.  In particular, the user is not
+prevented from editing the calendar file at the same time unless
+tt(calendar_edit) is used.
+
+Three attempts are made to lock the file before giving up.  If the module
+tt(zsh/zselect) is available, the times of the attempts are jittered so that
+multiple instances of the calling function are unlikely to retry at the
+same time.
 
 The files locked are appended to the array tt(lockfiles), which should
 be local to the caller.
 
 If all files were successully, status zero is returned, else status one.
+
+This function may be used as a general file locking function, although
+this will only work if only this mechanism is used to lock files.
 )
 findex(calendar_read)
 item(tt(calendar_read))(
@@ -660,6 +693,11 @@ enditem()
 texinode(Calendar Bugs)()(Calendar Utility Functions)(Calendar Function System)
 sect(Bugs)
 
+As the system is based entirely on shell functions (with a little support
+from the tt(zsh/datetime) module) the mechanisms used are not as robust as
+those provided by a dedicated calendar utility.  Consequently the user
+should not rely on the shell for vital alerts.
+
 There is no tt(calendar_delete) function.
 
 There is no localization support for dates and times, nor any support
@@ -668,8 +706,6 @@ for the use of time zones.
 Relative periods of months and years do not take into account the variable
 number of days.
 
-Recurrent events are not yet supported.
-
 The tt(calendar_show) function is currently hardwired to use tt(xmessage)
 for displaying alerts on X Window System displays.  This should be
 configurable and ideally integrate better with the desktop.
diff --git a/Functions/Calendar/calendar b/Functions/Calendar/calendar
index ea81c7ae7..08903d04c 100644
--- a/Functions/Calendar/calendar
+++ b/Functions/Calendar/calendar
@@ -5,7 +5,7 @@ local line restline REPLY REPLY2 userange pruned nobackup datefmt
 local calendar donefile sched newfile warnstr mywarnstr newdate
 integer time start stop today ndays y m d next=-1 shown done nodone
 integer verbose warntime mywarntime t tcalc tsched i rstat remaining
-integer showcount icount repeating repeattime resched
+integer showcount icount repeating repeattime resched showall
 local -a calendar_entries calendar_addlines
 local -a times calopts showprog lockfiles match mbegin mend
 
@@ -96,6 +96,11 @@ while [[ ${argv[opti+1]} = -* ]]; do
       ###########################
       # Options without arguments
       ###########################
+      (a)
+      # Show all entries
+      (( showall = 1 ))
+      ;;
+
       (d)
       # Move out of date items to the done file.
       (( done = 1 ))
@@ -279,7 +284,7 @@ fi
       fi
     fi
     (( shown = 0 ))
-    if (( t >= start && (remaining || t <= stop || icount < showcount) ))
+    if (( showall || (t >= start && (remaining || t <= stop || icount < showcount)) ))
     then
       $showprog $start $stop "$line"
       (( icount++ ))
diff --git a/Functions/Calendar/calendar_edit b/Functions/Calendar/calendar_edit
new file mode 100644
index 000000000..e31decb76
--- /dev/null
+++ b/Functions/Calendar/calendar_edit
@@ -0,0 +1,21 @@
+local editor=${VISUAL:-${EDITOR:-vi}}
+local line calendar
+local -a lockfiles
+
+integer cal_running
+
+sched | while read line; do
+  [[ $line = *" calendar -s "<->" "<-> ]] && (( cal_running = 1 ))
+done
+
+zstyle -s ':datetime:calendar:' calendar-file calendar || calendar=~/calendar
+
+{
+  calendar_lockfiles $calendar || return 1
+
+  eval $editor \$calendar
+} always {
+  (( ${#lockfiles} )) && rm -f $lockfiles
+}
+
+(( cal_running )) && calendar -s
diff --git a/Functions/Calendar/calendar_lockfiles b/Functions/Calendar/calendar_lockfiles
index 58ee42114..054b3f74b 100644
--- a/Functions/Calendar/calendar_lockfiles
+++ b/Functions/Calendar/calendar_lockfiles
@@ -3,7 +3,7 @@
 
 local file lockfile msgdone
 # Number of attempts to lock a file.  Probably not worth stylising.
-integer lockattempts=3
+integer lockattempts=3 loadtried
 
 # The lockfile name is not stylised: it has to be a fixed
 # derivative of the main fail.
@@ -18,7 +18,19 @@ for file; do
       msgdone="${lockfile}: waiting to acquire lock"
       zle -M $msgdone
     fi
-    sleep 1
+    if (( ! loadtried )); then
+      zmodload -i zsh/zselect 2>/dev/null
+      (( loadtried = 1 ))
+    fi
+    if zmodload -e zsh/zselect; then
+      # This gives us finer grained timing (100th second).
+      # Randomize the sleep between .1 and 1 second so that
+      # we are much less likely to have multiple instances
+      # retrying at once.
+      zselect -t $(( 10 + RANDOM * 90 / 32768 ))
+    else
+      sleep 1
+    fi
   done
   if [[ -n $msgdone ]]; then
     zle -M ${msgdone//?/ }
diff --git a/Functions/Calendar/calendar_scandate b/Functions/Calendar/calendar_scandate
index 5867db1bb..38647f84e 100644
--- a/Functions/Calendar/calendar_scandate
+++ b/Functions/Calendar/calendar_scandate
@@ -341,8 +341,8 @@ if (( relative == 0 )); then
   date_found=1
   ;;
 
-  # Look for DAY[th/st/rd] MNAME[,] YEAR
-  ((#bi)${~dspat}(<1-31>)(|th|st|rd)[[:space:]]##${~monthpat}(|,)[[:space:]]##((19|20)[0-9][0-9])*)
+  # Look for DAY[th/st/nd/rd] MNAME[,] YEAR
+  ((#bi)${~dspat}(<1-31>)(|th|st|nd|rd)[[:space:]]##${~monthpat}(|,)[[:space:]]##((19|20)[0-9][0-9])*)
   day=$match[2]
   mname=$match[4]
   year=$match[6]
@@ -350,8 +350,8 @@ if (( relative == 0 )); then
   date_found=1
   ;;
 
-  # Look for MNAME DAY[th/st/rd][,] YEAR
-  ((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|rd)(|,)[[:space:]]##((19|20)[0-9][0-9])*)
+  # Look for MNAME DAY[th/st/nd/rd][,] YEAR
+  ((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|nd|rd)(|,)[[:space:]]##((19|20)[0-9][0-9])*)
   mname=$match[2]
   day=$match[3]
   year=$match[6]
@@ -359,8 +359,8 @@ if (( relative == 0 )); then
   date_found=1
   ;;
 
-  # Look for DAY[th/st/rd] MNAME; assume current year
-  ((#bi)${~dspat}(<1-31>)(|th|st|rd)[[:space:]]##${~monthpat}(|,)([[:space:]]##*|))
+  # Look for DAY[th/st/nd/rd] MNAME; assume current year
+  ((#bi)${~dspat}(<1-31>)(|th|st|nd|rd)[[:space:]]##${~monthpat}(|,)([[:space:]]##*|))
   day=$match[2]
   mname=$match[4]
   strftime -s year "%Y" $EPOCHSECONDS
@@ -368,8 +368,8 @@ if (( relative == 0 )); then
   date_found=1
   ;;
 
-  # Look for MNAME DAY[th/st/rd]; assume current year
-  ((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|rd)(|,)([[:space:]]##*|))
+  # Look for MNAME DAY[th/st/nd/rd]; assume current year
+  ((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|nd|rd)(|,)([[:space:]]##*|))
   mname=$match[2]
   day=$match[3]
   strftime -s year "%Y" $EPOCHSECONDS
@@ -378,8 +378,8 @@ if (( relative == 0 )); then
   ;;
 
   # Now it gets a bit ambiguous.
-  # Look for DAY[th/st/rd][/]MONTH[/ ,]YEAR
-  ((#bi)${~dspat}(<1-31>)(|th|st|rd)/(<1-12>)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*)
+  # Look for DAY[th/st/nd/rd][/]MONTH[/ ,]YEAR
+  ((#bi)${~dspat}(<1-31>)(|th|st|nd|rd)/(<1-12>)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*)
   day=$match[2]
   month=$match[4]
   year=$match[7]
@@ -387,8 +387,8 @@ if (( relative == 0 )); then
   date_found=1
   ;;
 
-  # Look for MONTH[/]DAY[th/st/rd][/ ,]YEAR
-  ((#bi)${~dspat}(<1-12>)/(<1-31>)(|th|st|rd)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*)
+  # Look for MONTH[/]DAY[th/st/nd/rd][/ ,]YEAR
+  ((#bi)${~dspat}(<1-12>)/(<1-31>)(|th|st|nd|rd)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*)
   month=$match[2]
   day=$match[3]
   year=$match[7]
@@ -597,7 +597,9 @@ if (( relative )); then
      line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]}
      time_found=1
   fi
-  if [[ $relative = 2 && $line = (#bi)${~dspat_noday}(<->)(th|rd|st)(${~daypat})(|${~schars}*) ]]; then
+  # For the next three items we accumulate adjustments in "newadd".
+  # See note below for why they are special.
+  if [[ $relative = 2 && $line = (#bi)${~dspat_noday}(<->)(th|rd|nd|st)(${~daypat})(|${~schars}*) ]]; then
      nth=$match[2]
      test=${(L)${${match[4]##${~schars}#}%%${~schars}#}[1,3]}
      wday=${dayarr[(I)$test]}
@@ -618,81 +620,66 @@ if (( relative )); then
        # whereas the day of the month calculated so far is...
        strftime -s day2 "%d" $reldate
        # so we need to compensate by...
-       (( reladd += (day - day2) * daysecs ))
+       (( newadd += (day - day2) * daysecs ))
      fi
   fi
   if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(w|wk|week|weekly)${~repat} ]]; then
      [[ -z $match[2] ]] && match[2]=1
-     (( newadd = relsign * 7 * daysecs * ${match[2]} ))
-     if (( relative == 2 )); then
-       # See explanation of this correction under days, below.
-       strftime -s h1 "%H" $(( relative_start + reladd ))
-       strftime -s h2 "%H" $(( relative_start + reladd + newadd ))
-       (( hd = h2 - h1 ))
-       # and of course we might go past midnight...
-       if (( hd > 12 )); then
-	   (( hd -= 24 ))
-       elif (( hd < -12 )); then
-	   (( hd += 24 ))
-       fi
-       (( newadd -= hd * 3600 ))
-     fi
-     (( reladd += newadd ))
+     (( newadd += relsign * 7 * daysecs * ${match[2]} ))
      line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]}
      time_found=1
   fi
   if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(d|dy|day|daily)${~repat} ]]; then
      [[ -z $match[2] ]] && match[2]=1
-     (( newadd = relsign * daysecs * ${match[2]} ))
-     if (( relative == 2 )); then
-       # You thought a day was always the same time?  Ho, ho, ho.
-       # If the clocks go forward or back, we can gain or lose
-       # an hour.  Check this by seeing what the hour is before
-       # and after adding the number of days.  If it changes,
-       # remove the difference.
-       # 
-       # We need this correction for weeks, too, as above.
-       # (We could apply corrections for weeks and days together,
-       # in fact, but I've left it a little more modular).
-       # We don't need it for years and months because we calculated
-       # those by actually looking at the calendar for a given
-       # time of day, so the adjustment came out in the wash.
-       # We don't need it for hours or smaller periods because
-       # presumably if a user asks for something in 3 hours time
-       # they don't mean 4 hours if the clocks went back and
-       # 2 hours if they went forward.  At least, I think so.
-       # Consider:
-       #   % calendar_showdate +2d,1hr
-       #   Sun Mar 25 00:37:00 GMT 2007
-       #   % calendar_showdate +2d,2hr
-       #   Sun Mar 25 02:37:09 BST 2007
-       # At first sight that looks wrong because the clock appears
-       # to jump two hours.  (Yes, it took me all of 9 seconds to
-       # edit the line.)  But actually it's only jumped the hour
-       # you asked for, because one is in GMT and the other in BST.
-       # In principle you could say the same thing about days:
-       # Sun Mar 25 00:00:00 GMT 2007  and  Mon Mar 26 01:00:00 BST 2007
-       # are a day apart.  But usually if you say "same time next Tuesday"
-       # you mean "when the clock says the same time, even if someone
-       # has nipped in and adjusted it in the mean time", although
-       # for some reason you don't usually bother saying that.
-       #
-       # Hope that's clear.
-       strftime -s h1 "%H" $(( relative_start + reladd ))
-       strftime -s h2 "%H" $(( relative_start + reladd + newadd ))
-       (( hd = h2 - h1 ))
-       # and of course we might go past midnight...
-       if (( hd > 12 )); then
-	   (( hd -= 24 ))
-       elif (( hd < -12 )); then
-	   (( hd += 24 ))
-       fi
-       (( newadd -= hd * 3600 ))
-     fi
-     (( reladd += newadd ))
+     (( newadd += relsign * daysecs * ${match[2]} ))
      line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]}
      time_found=1
   fi
+  if (( relative == 2 && newadd )); then
+    # You thought a day was always the same time?  Ho, ho, ho.
+    # If the clocks go forward or back, we can gain or lose
+    # an hour.  Check this by seeing what the hour is before
+    # and after adding the number of days.  If it changes,
+    # remove the difference.
+    #
+    # We need this correction for days (including days of a given
+    # month) and weeks.
+    # We don't need it for years and months because we calculated
+    # those by actually looking at the calendar for a given
+    # time of day, so the adjustment came out in the wash.
+    # We don't need it for hours or smaller periods because
+    # presumably if a user asks for something in 3 hours time
+    # they don't mean 4 hours if the clocks went back and
+    # 2 hours if they went forward.  At least, I think so.
+    # Consider:
+    #   % calendar_showdate +2d,1hr
+    #   Sun Mar 25 00:37:00 GMT 2007
+    #   % calendar_showdate +2d,2hr
+    #   Sun Mar 25 02:37:09 BST 2007
+    # At first sight that looks wrong because the clock appears
+    # to jump two hours.  (Yes, it took me all of 9 seconds to
+    # edit the line.)  But actually it's only jumped the hour
+    # you asked for, because one is in GMT and the other in BST.
+    # In principle you could say the same thing about days:
+    # Sun Mar 25 00:00:00 GMT 2007  and  Mon Mar 26 01:00:00 BST 2007
+    # are a day apart.  But usually if you say "same time next Tuesday"
+    # you mean "when the clock says the same time, even if someone
+    # has nipped in and adjusted it in the mean time", although
+    # for some reason you don't usually bother saying that.
+    #
+    # Hope that's clear.
+    strftime -s h1 "%H" $(( relative_start + reladd ))
+    strftime -s h2 "%H" $(( relative_start + reladd + newadd ))
+    (( hd = h2 - h1 ))
+    # and of course we might go past midnight...
+    if (( hd > 12 )); then
+      (( hd -= 24 ))
+    elif (( hd < -12 )); then
+      (( hd += 24 ))
+    fi
+    (( newadd -= hd * 3600 ))
+  fi
+  (( reladd += newadd ))
   if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(h|hr|hour|hourly)${~repat} ]]; then
      [[ -z $match[2] ]] && match[2]=1
      (( reladd += relsign * 60 * 60 * ${match[2]} ))
diff --git a/Functions/Calendar/calendar_showdate b/Functions/Calendar/calendar_showdate
index 69588da4f..a8985513c 100644
--- a/Functions/Calendar/calendar_showdate
+++ b/Functions/Calendar/calendar_showdate
@@ -8,15 +8,39 @@ integer optr replyset
 zstyle -s ':datetime:calendar_showdate:' date-format datefmt ||
   datefmt="%a %b %d %H:%M:%S %Z %Y"
 
-while [[ $argv[$OPTIND] != +* ]] && getopts "r" opt; do
-  case $opt in
-    (r)
+# Memo to myself: both + and - are documented as giving relative
+# times, so it's not a good idea to rewrite this to use getopts.
+# We need to detect the small number of options this can actually
+# handle.
+while [[ $1 = -r || $1 = -- || $1 = -f* ]]; do
+  case $1 in
+    (-r)
+    shift
     REPLY=0
     optr=1
     ;;
+
+    (-f*)
+    if [[ $1 = -f?* ]]; then
+      datefmt=$1[3,-1]
+      shift
+    else
+      shift
+      if [[ -z $1 || $1 != *%* ]]; then
+	print "$0: -f requires a date/time specification" >&2
+	return 1
+      fi
+      datefmt=$1
+      shift
+    fi
+    ;;
+
+    (--)
+    shift
+    break
+    ;;
   esac
 done
-shift $(( OPTIND - 1 ))
 
 (( optr )) || local REPLY