summaryrefslogtreecommitdiff
path: root/Functions/Calendar/calendar_add
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2010-06-14 13:01:41 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2010-06-14 13:01:41 +0000
commit14dde084755a8b15004d59bb6be5cc7a3726a8bf (patch)
tree067f4ebff5e399fb560c710b798a4e3421f771ea /Functions/Calendar/calendar_add
parent4c1a3a89f0ade5be2330ce688cd3c3c649667f9a (diff)
downloadzsh-14dde084755a8b15004d59bb6be5cc7a3726a8bf.tar.gz
zsh-14dde084755a8b15004d59bb6be5cc7a3726a8bf.zip
28038: improved handling of recurring events in calendar system
Diffstat (limited to 'Functions/Calendar/calendar_add')
-rw-r--r--Functions/Calendar/calendar_add178
1 files changed, 132 insertions, 46 deletions
diff --git a/Functions/Calendar/calendar_add b/Functions/Calendar/calendar_add
index eded25b2a..c06deda3a 100644
--- a/Functions/Calendar/calendar_add
+++ b/Functions/Calendar/calendar_add
@@ -7,14 +7,19 @@
# entry before the first existing entry with a later date and time.
emulate -L zsh
-setopt extendedglob
+setopt extendedglob # xtrace
local context=":datetime:calendar_add:"
+local vdatefmt="%Y%m%dT%H%M%S"
+local d='[[:digit:]]'
-local calendar newfile REPLY lastline opt
-local -a calendar_entries lockfiles reply
-integer my_date done rstat nolock nobackup new_recurring old_recurring
-local -A reply parse_new parse_old recurring_uids
+local calendar newfile REPLY lastline opt text occur
+local -a calendar_entries lockfiles reply occurrences
+integer my_date done rstat nolock nobackup new_recurring
+integer keep_my_uid
+local -A reply parse_new parse_old
+local -a match mbegin mend
+local my_uid their_uid
autoload -U calendar_{parse,read,lockfiles}
@@ -47,7 +52,6 @@ if ! calendar_parse $addline; then
fi
parse_new=("${(@kv)reply}")
(( my_date = $parse_new[time] ))
-[[ -n $parse_new[rpttime] ]] && (( new_recurring = 1 ))
if zstyle -t $context reformat-date; then
local datefmt
zstyle -s $context date-format datefmt ||
@@ -55,12 +59,24 @@ if zstyle -t $context reformat-date; then
strftime -s REPLY $datefmt $parse_new[time]
addline="$REPLY $parse_new[text1]"
fi
+if [[ -n $parse_new[rptstr] ]]; then
+ (( new_recurring = 1 ))
+ if [[ $parse_new[rptstr] = CANCELLED ]]; then
+ (( done = 1 ))
+ elif [[ $addline = (#b)(*[[:space:]\#]RECURRENCE[[:space:]]##)([^[:space:]]##)([[:space:]]*|) ]]; then
+ # Use the updated recurrence time
+ strftime -s REPLY $vdatefmt ${parse_new[schedrpttime]}
+ addline="${match[1]}$REPLY${match[3]}"
+ else
+ # Add a recurrence time
+ [[ $addline = *$'\n' ]] || addline+=$'\n'
+ strftime -s REPLY $vdatefmt ${parse_new[schedrpttime]}
+ addline+=" # RECURRENCE $REPLY"
+ fi
+fi
# $calendar doesn't necessarily exist yet.
-local -a match mbegin mend
-local my_uid their_uid
-
# Match a UID, a unique identifier for the entry inherited from
# text/calendar format.
local uidpat='(|*[[:space:]])UID[[:space:]]##(#b)([[:xdigit:]]##)(|[[:space:]]*)'
@@ -87,14 +103,112 @@ fi
calendar_read $calendar
if [[ -n $my_uid ]]; then
- # Pre-scan to find recurring events with a UID
+ # Pre-scan to events with the same UID
for line in $calendar_entries; do
calendar_parse $line || continue
+ parse_old=("${(@kv)reply}")
# Recurring with a UID?
- if [[ -n $reply[rpttime] && $line = ${~uidpat} ]]; then
- # Yes, so record this as a recurring event.
+ if [[ $line = ${~uidpat} ]]; then
their_uid=${(U)match[1]}
- recurring_uids[$their_uid]=$reply[time]
+ if [[ $their_uid = $my_uid ]]; then
+ # Deal with recurrences and also some add some
+ # extra magic for cancellation.
+
+ # See if we have found a recurrent version
+ if [[ -z $parse_old[rpttime] ]]; then
+ # No, so assume this is a straightforward replacement
+ # of a non-recurring event.
+
+ # Is this a cancellation of a non-recurring event?
+ # Look for an OCCURRENCE in the form
+ # OCCURRENCE 20100513T110000 CANCELLED
+ # although we don't bother looking at the date/time---
+ # it's one-off, so this should already be unique.
+ if [[ $new_recurring -eq 0 && \
+ $parse_new[text1] = (|*[[:space:]\#])"OCCURRENCE"[[:space:]]##([^[:space:]]##[[:space:]]##CANCELLED)(|[[:space:]]*) ]]; then
+ # Yes, so skip both the old and new events.
+ (( done = 1 ))
+ fi
+ # We'll skip this UID when we encounter it again.
+ continue
+ fi
+ if (( new_recurring )); then
+ # Replacing a recurrence; there can be only one.
+ # TBD: do we replace existing occurrences of the
+ # replaced recurrent event? I'm guessing not, but
+ # if we keep the UID then maybe we should.
+ #
+ # TBD: ick, suppose we're cancelling an even that
+ # we added to a recurring sequence but didn't replace
+ # the recurrence. We might get RPT CANCELLED for this.
+ # That would be bad. Should invent better way of
+ # cancelling non-recurring event.
+ continue
+ else
+ # The recorded event is recurring, but the new one is a
+ # one-off event. If there are embedded OCCURRENCE declarations,
+ # use those.
+ #
+ # TBD: We could be clever about text associated
+ # with the occurrence. Copying the entire text
+ # of the meeting seems like overkill but people often
+ # add specific remarks about why this occurrence was
+ # moved/cancelled.
+ #
+ # TBD: one case we don't yet handle is cancelling
+ # something that isn't replacing a recurrence, i.e.
+ # something we added as OCCURRENCE XXXXXXXXTXXXXXX <when>.
+ # If we're adding a CANCELLED occurrence we should
+ # look to see if it matches <when> and if so simply
+ # delete that occurrence.
+ #
+ # TBD: one nasty case is if the new occurrence
+ # occurs before the current scheduled time. As we
+ # never look backwards we'll miss it.
+ text=$addline
+ occurrences=()
+ while [[ $text = (#b)(|*[[:space:]\#])"OCCURRENCE"[[:space:]]##([^[:space:]]##[[:space:]]##[^[:space:]]##)(|[[:space:]]*) ]]; do
+ occurrences+=($match[2])
+ text="$match[1] $match[3]"
+ done
+ if (( ! ${#occurrences} )); then
+ # No embedded occurrences. We'll manufacture one
+ # that doesn't refer to an original recurrence.
+ strftime -s REPLY $vdatefmt $my_date
+ occurrences=("XXXXXXXXTXXXXXX $REPLY")
+ fi
+ # Add these occurrences, checking if they replace
+ # an existing one.
+ for occur in ${(o)occurrences}; do
+ REPLY=${occur%%[[:space:]]*}
+ # Only update occurrences that refer to genuine
+ # recurrences.
+ if [[ $REPLY = [[:digit:]](#c8)T[[:digit:]](#c6) && $line = (#b)(|*[[:space:]\#])(OCCURRENCE[[:space:]]##)${REPLY}[[:space:]]##[^[:space:]]##(|[[:space:]]*) ]]; then
+ # Yes, update in situ
+ line="${match[1]}${match[2]}$occur${match[3]}"
+ else
+ # No, append.
+ [[ $line = *$'\n' ]] || line+=$'\n'
+ line+=" # OCCURRENCE $occur"
+ fi
+ done
+ # The line we're adding now corresponds to the
+ # original event. We'll skip the matching UID
+ # in the file below, however.
+ addline=$line
+ # We need to work out which event is next, so
+ # reparse.
+ if calendar_parse $addline; then
+ parse_new=("${(@kv)reply}")
+ (( my_date = ${parse_new[time]} ))
+ if zstyle -t $context reformat-date; then
+ zstyle -s $context date-format datefmt
+ strftime -s REPLY $datefmt $parse_new[time]
+ addline="$REPLY $parse_new[text1]"
+ fi
+ fi
+ fi
+ fi
fi
done
fi
@@ -107,39 +221,11 @@ fi
print -r -- $addline
(( done = 1 ))
fi
- if [[ -n $parse_old[rpttime] ]]; then
- (( old_recurring = 1 ))
- else
- (( old_recurring = 0 ))
- fi
- if [[ -n $my_uid && $line = ${~uidpat} ]]; then
+ # We've already merged any information on the same UID
+ # with our new text, probably.
+ if [[ $keep_my_uid -eq 0 && -n $my_uid && $line = ${~uidpat} ]]; then
their_uid=${(U)match[1]}
- if [[ $my_uid = $their_uid ]]; then
- # Deal with recurrences, being careful in case there
- # are one-off variants that don't replace recurrences.
- #
- # Bug 1: "calendar" still doesn't know about one-off variants.
- # Bug 2: neither do I; how do we know which occurrence
- # it replaces?
- # Bug 3: the code for calculating recurrences is awful anyway.
-
- if (( old_recurring && new_recurring )); then
- # Replacing a recurrence; there can be only one.
- continue
- elif (( ! new_recurring )); then
- # Not recurring. See if we have previously found
- # a recurrent version
- [[ -n $recurring_uids[$their_uid] ]] && (( old_recurring = 1 ))
- # No, so assume this is a straightforward replacement
- # of a non-recurring event.
- (( ! old_recurring )) && continue
- # It's recurring, but if this is a one-off at the
- # same time as the previous one, replace anyway.
- [[ -z $parse_old[$rpttime] ]] &&
- (( ${parse_new[time]} == ${parse_old[time]} )) &&
- continue
- fi
- fi
+ [[ $my_uid = $their_uid ]] && continue
fi
if [[ $parse_old[time] -eq $my_date && $line = $addline ]]; then
(( done )) && continue # paranoia: shouldn't happen
@@ -157,7 +243,7 @@ New calendar left in $newfile." >&2
fi
fi
else
- print -r -- $line >$newfile
+ (( done )) || print -r -- $addline >$newfile
fi
if (( !rstat )) && ! mv $newfile $calendar; then