1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-27 04:40:59 +01:00

unposted: more random calendar system fixes and improvements

This commit is contained in:
Peter Stephenson 2007-03-26 14:33:31 +00:00
parent 7072c10ae2
commit 4b7b7f56f1
7 changed files with 189 additions and 92 deletions

View file

@ -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> 2007-03-25 Peter Stephenson <p.w.stephenson@ntlworld.com>
* usres/11333: Completion/Unix/Command/_ssh: users-hosts * usres/11333: Completion/Unix/Command/_ssh: users-hosts

View file

@ -309,8 +309,8 @@ subsect(Calendar system functions)
startitem() startitem()
findex(calendar) findex(calendar)
xitem(tt(calendar) [ tt(-dDsv) ] [ tt(-C) var(calfile) ] [ -n var(num) ] [ tt(-S) var(showprog) ] [ [ var(start) ] var(end) ])( 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(-dDrsv) ] [ tt(-C) var(calfile) ] [ -n var(num) ] [ tt(-S) var(showprog) ] [ var(start) ])( item(tt(calendar -r) [ tt(-adDrsv) ] [ tt(-C) var(calfile) ] [ -n var(num) ] [ tt(-S) var(showprog) ] [ var(start) ])(
Show events in the calendar. Show events in the calendar.
With no arguments, show events from the start of today until the end of With no arguments, show events from the start of today until the end of
@ -338,6 +338,10 @@ tt(~/.zshrc) file.
Options: Options:
startitem() startitem()
item(tt(-a))(
Show all items in the calendar, regardless of the tt(start) and
tt(end).
)
item(tt(-C) var(calfile))( item(tt(-C) var(calfile))(
Explicitly specify a calendar file instead of the value of Explicitly specify a calendar file instead of the value of
the tt(calendar-file) style or the the default tt(~/calendar). 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 calendar file up the old one as it is already locked. These options will
not usually be needed by users. 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) 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 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 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 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 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 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. 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 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). 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) findex(calendar_sort)
item(tt(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 Attempt to lock the files given in the argument. To prevent
problems with network file locking this is done in an ad hoc fashion 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 by attempting to create a symbolic link to the file with the name
var(file)tt(.lockfile). Otherwise, however, the function is not var(file)tt(.lockfile). No other system level functions are used
specific to the calendar system. Three attempts are made to lock for locking, i.e. the file can be accessed and modified by any
the file before giving up. 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 The files locked are appended to the array tt(lockfiles), which should
be local to the caller. be local to the caller.
If all files were successully, status zero is returned, else status one. 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) findex(calendar_read)
item(tt(calendar_read))( item(tt(calendar_read))(
@ -660,6 +693,11 @@ enditem()
texinode(Calendar Bugs)()(Calendar Utility Functions)(Calendar Function System) texinode(Calendar Bugs)()(Calendar Utility Functions)(Calendar Function System)
sect(Bugs) 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 tt(calendar_delete) function.
There is no localization support for dates and times, nor any support 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 Relative periods of months and years do not take into account the variable
number of days. number of days.
Recurrent events are not yet supported.
The tt(calendar_show) function is currently hardwired to use tt(xmessage) The tt(calendar_show) function is currently hardwired to use tt(xmessage)
for displaying alerts on X Window System displays. This should be for displaying alerts on X Window System displays. This should be
configurable and ideally integrate better with the desktop. configurable and ideally integrate better with the desktop.

View file

@ -5,7 +5,7 @@ local line restline REPLY REPLY2 userange pruned nobackup datefmt
local calendar donefile sched newfile warnstr mywarnstr newdate local calendar donefile sched newfile warnstr mywarnstr newdate
integer time start stop today ndays y m d next=-1 shown done nodone 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 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 calendar_entries calendar_addlines
local -a times calopts showprog lockfiles match mbegin mend local -a times calopts showprog lockfiles match mbegin mend
@ -96,6 +96,11 @@ while [[ ${argv[opti+1]} = -* ]]; do
########################### ###########################
# Options without arguments # Options without arguments
########################### ###########################
(a)
# Show all entries
(( showall = 1 ))
;;
(d) (d)
# Move out of date items to the done file. # Move out of date items to the done file.
(( done = 1 )) (( done = 1 ))
@ -279,7 +284,7 @@ fi
fi fi
fi fi
(( shown = 0 )) (( shown = 0 ))
if (( t >= start && (remaining || t <= stop || icount < showcount) )) if (( showall || (t >= start && (remaining || t <= stop || icount < showcount)) ))
then then
$showprog $start $stop "$line" $showprog $start $stop "$line"
(( icount++ )) (( icount++ ))

View file

@ -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

View file

@ -3,7 +3,7 @@
local file lockfile msgdone local file lockfile msgdone
# Number of attempts to lock a file. Probably not worth stylising. # 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 # The lockfile name is not stylised: it has to be a fixed
# derivative of the main fail. # derivative of the main fail.
@ -18,7 +18,19 @@ for file; do
msgdone="${lockfile}: waiting to acquire lock" msgdone="${lockfile}: waiting to acquire lock"
zle -M $msgdone zle -M $msgdone
fi 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 done
if [[ -n $msgdone ]]; then if [[ -n $msgdone ]]; then
zle -M ${msgdone//?/ } zle -M ${msgdone//?/ }

View file

@ -341,8 +341,8 @@ if (( relative == 0 )); then
date_found=1 date_found=1
;; ;;
# Look for DAY[th/st/rd] MNAME[,] YEAR # Look for DAY[th/st/nd/rd] MNAME[,] YEAR
((#bi)${~dspat}(<1-31>)(|th|st|rd)[[:space:]]##${~monthpat}(|,)[[:space:]]##((19|20)[0-9][0-9])*) ((#bi)${~dspat}(<1-31>)(|th|st|nd|rd)[[:space:]]##${~monthpat}(|,)[[:space:]]##((19|20)[0-9][0-9])*)
day=$match[2] day=$match[2]
mname=$match[4] mname=$match[4]
year=$match[6] year=$match[6]
@ -350,8 +350,8 @@ if (( relative == 0 )); then
date_found=1 date_found=1
;; ;;
# Look for MNAME DAY[th/st/rd][,] YEAR # Look for MNAME DAY[th/st/nd/rd][,] YEAR
((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|rd)(|,)[[:space:]]##((19|20)[0-9][0-9])*) ((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|nd|rd)(|,)[[:space:]]##((19|20)[0-9][0-9])*)
mname=$match[2] mname=$match[2]
day=$match[3] day=$match[3]
year=$match[6] year=$match[6]
@ -359,8 +359,8 @@ if (( relative == 0 )); then
date_found=1 date_found=1
;; ;;
# Look for DAY[th/st/rd] MNAME; assume current year # Look for DAY[th/st/nd/rd] MNAME; assume current year
((#bi)${~dspat}(<1-31>)(|th|st|rd)[[:space:]]##${~monthpat}(|,)([[:space:]]##*|)) ((#bi)${~dspat}(<1-31>)(|th|st|nd|rd)[[:space:]]##${~monthpat}(|,)([[:space:]]##*|))
day=$match[2] day=$match[2]
mname=$match[4] mname=$match[4]
strftime -s year "%Y" $EPOCHSECONDS strftime -s year "%Y" $EPOCHSECONDS
@ -368,8 +368,8 @@ if (( relative == 0 )); then
date_found=1 date_found=1
;; ;;
# Look for MNAME DAY[th/st/rd]; assume current year # Look for MNAME DAY[th/st/nd/rd]; assume current year
((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|rd)(|,)([[:space:]]##*|)) ((#bi)${~dspat}${~monthpat}[[:space:]]##(<1-31>)(|th|st|nd|rd)(|,)([[:space:]]##*|))
mname=$match[2] mname=$match[2]
day=$match[3] day=$match[3]
strftime -s year "%Y" $EPOCHSECONDS strftime -s year "%Y" $EPOCHSECONDS
@ -378,8 +378,8 @@ if (( relative == 0 )); then
;; ;;
# Now it gets a bit ambiguous. # Now it gets a bit ambiguous.
# Look for DAY[th/st/rd][/]MONTH[/ ,]YEAR # Look for DAY[th/st/nd/rd][/]MONTH[/ ,]YEAR
((#bi)${~dspat}(<1-31>)(|th|st|rd)/(<1-12>)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*) ((#bi)${~dspat}(<1-31>)(|th|st|nd|rd)/(<1-12>)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*)
day=$match[2] day=$match[2]
month=$match[4] month=$match[4]
year=$match[7] year=$match[7]
@ -387,8 +387,8 @@ if (( relative == 0 )); then
date_found=1 date_found=1
;; ;;
# Look for MONTH[/]DAY[th/st/rd][/ ,]YEAR # Look for MONTH[/]DAY[th/st/nd/rd][/ ,]YEAR
((#bi)${~dspat}(<1-12>)/(<1-31>)(|th|st|rd)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*) ((#bi)${~dspat}(<1-12>)/(<1-31>)(|th|st|nd|rd)((|,)[[:space:]]##|/)((19|20)[0-9][0-9])*)
month=$match[2] month=$match[2]
day=$match[3] day=$match[3]
year=$match[7] year=$match[7]
@ -597,7 +597,9 @@ if (( relative )); then
line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]} line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]}
time_found=1 time_found=1
fi 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] nth=$match[2]
test=${(L)${${match[4]##${~schars}#}%%${~schars}#}[1,3]} test=${(L)${${match[4]##${~schars}#}%%${~schars}#}[1,3]}
wday=${dayarr[(I)$test]} wday=${dayarr[(I)$test]}
@ -618,81 +620,66 @@ if (( relative )); then
# whereas the day of the month calculated so far is... # whereas the day of the month calculated so far is...
strftime -s day2 "%d" $reldate strftime -s day2 "%d" $reldate
# so we need to compensate by... # so we need to compensate by...
(( reladd += (day - day2) * daysecs )) (( newadd += (day - day2) * daysecs ))
fi fi
fi fi
if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(w|wk|week|weekly)${~repat} ]]; then if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(w|wk|week|weekly)${~repat} ]]; then
[[ -z $match[2] ]] && match[2]=1 [[ -z $match[2] ]] && match[2]=1
(( newadd = relsign * 7 * daysecs * ${match[2]} )) (( 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 ))
line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]} line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]}
time_found=1 time_found=1
fi fi
if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(d|dy|day|daily)${~repat} ]]; then if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(d|dy|day|daily)${~repat} ]]; then
[[ -z $match[2] ]] && match[2]=1 [[ -z $match[2] ]] && match[2]=1
(( newadd = relsign * daysecs * ${match[2]} )) (( 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 ))
line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]} line=${line[1,$mbegin[2]-1]}${line[$mend[4]+1,-1]}
time_found=1 time_found=1
fi 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 if [[ $line = (#bi)${~dspat}(<->|)[[:space:]]#(h|hr|hour|hourly)${~repat} ]]; then
[[ -z $match[2] ]] && match[2]=1 [[ -z $match[2] ]] && match[2]=1
(( reladd += relsign * 60 * 60 * ${match[2]} )) (( reladd += relsign * 60 * 60 * ${match[2]} ))

View file

@ -8,15 +8,39 @@ integer optr replyset
zstyle -s ':datetime:calendar_showdate:' date-format datefmt || zstyle -s ':datetime:calendar_showdate:' date-format datefmt ||
datefmt="%a %b %d %H:%M:%S %Z %Y" datefmt="%a %b %d %H:%M:%S %Z %Y"
while [[ $argv[$OPTIND] != +* ]] && getopts "r" opt; do # Memo to myself: both + and - are documented as giving relative
case $opt in # times, so it's not a good idea to rewrite this to use getopts.
(r) # 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 REPLY=0
optr=1 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 esac
done done
shift $(( OPTIND - 1 ))
(( optr )) || local REPLY (( optr )) || local REPLY