about summary refs log tree commit diff
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.tar.xz
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