From 4b7b7f56f1cde7e317fce378d0d0e5c83686ff72 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 26 Mar 2007 14:33:31 +0000 Subject: unposted: more random calendar system fixes and improvements --- ChangeLog | 12 +++ Doc/Zsh/calsys.yo | 52 +++++++++++-- Functions/Calendar/calendar | 9 ++- Functions/Calendar/calendar_edit | 21 +++++ Functions/Calendar/calendar_lockfiles | 16 +++- Functions/Calendar/calendar_scandate | 139 +++++++++++++++------------------- Functions/Calendar/calendar_showdate | 32 +++++++- 7 files changed, 189 insertions(+), 92 deletions(-) create mode 100644 Functions/Calendar/calendar_edit diff --git a/ChangeLog b/ChangeLog index c0404b199..039cac75c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2007-03-26 Peter Stephenson + + * 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 + ", 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 * 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 -- cgit 1.4.1