]> code.delx.au - gnu-emacs/blobdiff - lisp/calendar/icalendar.el
New file, from: Anna M. Bigatti <bigatti at dima.unige.it>.
[gnu-emacs] / lisp / calendar / icalendar.el
index fe127be3990298f2a71db294b5584eb5154b96c9..570fb626747ea90e44594c75266769fa285daf8b 100644 (file)
@@ -1,6 +1,6 @@
 ;;; icalendar.el --- iCalendar implementation -*-coding: utf-8 -*-
 
 ;;; icalendar.el --- iCalendar implementation -*-coding: utf-8 -*-
 
-;; Copyright (C) 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
+;; Copyright (C) 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
 
 ;; Author:         Ulf Jasper <ulf.jasper@web.de>
 ;; Created:        August 2002
 
 ;; Author:         Ulf Jasper <ulf.jasper@web.de>
 ;; Created:        August 2002
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;; This package is documented in the Emacs Manual.
 
 
 ;;; Commentary:
 
 ;; This package is documented in the Emacs Manual.
 
+;;   Please note:
+;; - Diary entries which have a start time but no end time are assumed to
+;;   last for one hour when they are exported.
+;; - Weekly diary entries are assumed to occur the first time in the first
+;;   week of the year 2000 when they are exported.
+;; - Yearly diary entries are assumed to occur the first time in the year
+;;   1900 when they are exported.
 
 ;;; History:
 
 
 ;;; History:
 
 ;;    + the parser is too soft
 ;;    + error log is incomplete
 ;;    + nice to have: #include "webcal://foo.com/some-calendar.ics"
 ;;    + the parser is too soft
 ;;    + error log is incomplete
 ;;    + nice to have: #include "webcal://foo.com/some-calendar.ics"
+;;    + timezones, currently all times are local!
 
 ;;  * Export from diary to ical
 ;;    + diary-date, diary-float, and self-made sexp entries are not
 ;;      understood
 
 ;;  * Export from diary to ical
 ;;    + diary-date, diary-float, and self-made sexp entries are not
 ;;      understood
-;;    + timezones, currently all times are local!
 
 ;;  * Other things
 ;;    + clean up all those date/time parsing functions
 
 ;;  * Other things
 ;;    + clean up all those date/time parsing functions
@@ -90,7 +97,7 @@
 
 ;;; Code:
 
 
 ;;; Code:
 
-(defconst icalendar-version 0.10
+(defconst icalendar-version "0.13"
   "Version number of icalendar.el.")
 
 ;; ======================================================================
   "Version number of icalendar.el.")
 
 ;; ======================================================================
   "Format string for importing events from iCalendar into Emacs diary.
 This string defines how iCalendar events are inserted into diary
 file.  Meaning of the specifiers:
   "Format string for importing events from iCalendar into Emacs diary.
 This string defines how iCalendar events are inserted into diary
 file.  Meaning of the specifiers:
+%c Class, see `icalendar-import-format-class'
 %d Description, see `icalendar-import-format-description'
 %l Location, see `icalendar-import-format-location'
 %o Organizer, see `icalendar-import-format-organizer'
 %d Description, see `icalendar-import-format-description'
 %l Location, see `icalendar-import-format-location'
 %o Organizer, see `icalendar-import-format-organizer'
-%s Subject, see `icalendar-import-format-subject'"
+%s Summary, see `icalendar-import-format-summary'
+%t Status, see `icalendar-import-format-status'
+%u URL, see `icalendar-import-format-url'"
   :type 'string
   :group 'icalendar)
 
   :type 'string
   :group 'icalendar)
 
-(defcustom icalendar-import-format-subject
+(defcustom icalendar-import-format-summary
   "%s"
   "%s"
-  "Format string defining how the subject element is formatted.
-This applies only if the subject is not empty! `%s' is replaced
-by the subject."
+  "Format string defining how the summary element is formatted.
+This applies only if the summary is not empty! `%s' is replaced
+by the summary."
   :type 'string
   :group 'icalendar)
 
   :type 'string
   :group 'icalendar)
 
@@ -145,16 +155,32 @@ replaced by the organizer."
   :type 'string
   :group 'icalendar)
 
   :type 'string
   :group 'icalendar)
 
-(defcustom icalendar-duration-correction
-  t
-  "Workaround for all-day events.
-If non-nil the length=duration of iCalendar appointments that
-have a length of exactly n days is decreased by one day.  This
-fixes problems with all-day events, which appear to be one day
-longer than they are."
-  :type 'boolean
+(defcustom icalendar-import-format-url
+  "\n URL: %s"
+  "Format string defining how the URL element is formatted.
+This applies only if the URL is not empty! `%s' is replaced by
+the URL."
+  :type 'string
+  :group 'icalendar)
+
+(defcustom icalendar-import-format-status
+  "\n Status: %s"
+  "Format string defining how the status element is formatted.
+This applies only if the status is not empty! `%s' is replaced by
+the status."
+  :type 'string
+  :group 'icalendar)
+
+(defcustom icalendar-import-format-class
+  "\n Class: %s"
+  "Format string defining how the class element is formatted.
+This applies only if the class is not empty! `%s' is replaced by
+the class."
+  :type 'string
   :group 'icalendar)
 
   :group 'icalendar)
 
+(defvar icalendar-debug nil
+  "Enable icalendar debug messages.")
 
 ;; ======================================================================
 ;; NO USER SERVICABLE PARTS BELOW THIS LINE
 
 ;; ======================================================================
 ;; NO USER SERVICABLE PARTS BELOW THIS LINE
@@ -162,13 +188,10 @@ longer than they are."
 
 (defconst icalendar--weekday-array ["SU" "MO" "TU" "WE" "TH" "FR" "SA"])
 
 
 (defconst icalendar--weekday-array ["SU" "MO" "TU" "WE" "TH" "FR" "SA"])
 
-(defvar icalendar-debug nil ".")
-
 ;; ======================================================================
 ;; all the other libs we need
 ;; ======================================================================
 (require 'calendar)
 ;; ======================================================================
 ;; all the other libs we need
 ;; ======================================================================
 (require 'calendar)
-(require 'appt)
 
 ;; ======================================================================
 ;; misc
 
 ;; ======================================================================
 ;; misc
@@ -193,20 +216,22 @@ buffer."
     (save-current-buffer
       (set-buffer unfolded-buffer)
       (erase-buffer)
     (save-current-buffer
       (set-buffer unfolded-buffer)
       (erase-buffer)
-      (insert-buffer folded-ical-buffer)
+      (insert-buffer-substring folded-ical-buffer)
+      (goto-char (point-min))
       (while (re-search-forward "\r?\n[ \t]" nil t)
         (replace-match "" nil nil)))
     unfolded-buffer))
 
       (while (re-search-forward "\r?\n[ \t]" nil t)
         (replace-match "" nil nil)))
     unfolded-buffer))
 
-(defsubst icalendar--rris (re rp st)
-  "Replace regexp RE with RP in string ST and return the new string.
-This is here for compatibility with XEmacs."
+(defsubst icalendar--rris (&rest args)
+  "Replace regular expression in string.
+Pass ARGS to `replace-regexp-in-string' (Emacs) or to
+`replace-in-string' (XEmacs)."
   ;; XEmacs:
   (if (fboundp 'replace-in-string)
       (save-match-data ;; apparently XEmacs needs save-match-data
   ;; XEmacs:
   (if (fboundp 'replace-in-string)
       (save-match-data ;; apparently XEmacs needs save-match-data
-        (replace-in-string st re rp))
+        (apply 'replace-in-string args))
     ;; Emacs:
     ;; Emacs:
-    (replace-regexp-in-string re rp st)))
+    (apply 'replace-regexp-in-string args)))
 
 (defun icalendar--read-element (invalue inparams)
   "Recursively read the next iCalendar element in the current buffer.
 
 (defun icalendar--read-element (invalue inparams)
   "Recursively read the next iCalendar element in the current buffer.
@@ -268,7 +293,7 @@ it finds"
 ;;  (car (cddr event)))
 
 (defun icalendar--get-event-property (event prop)
 ;;  (car (cddr event)))
 
 (defun icalendar--get-event-property (event prop)
-  "For the given EVENT return the value of the first occurence of PROP."
+  "For the given EVENT return the value of the first occurrence of PROP."
   (catch 'found
     (let ((props (car (cddr event))) pp)
       (while props
   (catch 'found
     (let ((props (car (cddr event))) pp)
       (while props
@@ -279,7 +304,7 @@ it finds"
     nil))
 
 (defun icalendar--get-event-property-attributes (event prop)
     nil))
 
 (defun icalendar--get-event-property-attributes (event prop)
-  "For the given EVENT return attributes of the first occurence of PROP."
+  "For the given EVENT return attributes of the first occurrence of PROP."
   (catch 'found
     (let ((props (car (cddr event))) pp)
       (while props
   (catch 'found
     (let ((props (car (cddr event))) pp)
       (while props
@@ -295,7 +320,7 @@ it finds"
     (while props
       (setq pp (car props))
       (if (eq (car pp) prop)
     (while props
       (setq pp (car props))
       (if (eq (car pp) prop)
-          (setq result (cons (car (cddr pp)) result)))
+          (setq result (append (split-string (car (cddr pp)) ",") result)))
       (setq props (cdr props)))
     result))
 
       (setq props (cdr props)))
     result))
 
@@ -411,12 +436,17 @@ FIXME: multiple comma-separated values should be allowed!"
     ;; isodatetimestring == nil
     nil))
 
     ;; isodatetimestring == nil
     nil))
 
-(defun icalendar--decode-isoduration (isodurationstring)
-  "Return ISODURATIONSTRING in format like `decode-time'.
+(defun icalendar--decode-isoduration (isodurationstring
+                                      &optional duration-correction)
+  "Convert ISODURATIONSTRING into format provided by `decode-time'.
 Converts from ISO-8601 to Emacs representation.  If ISODURATIONSTRING
 specifies UTC time (trailing letter Z) the decoded time is given in
 Converts from ISO-8601 to Emacs representation.  If ISODURATIONSTRING
 specifies UTC time (trailing letter Z) the decoded time is given in
-the local time zone! FIXME: TZID-attributes are ignored....! FIXME:
-multiple comma-separated values should be allowed!"
+the local time zone!
+
+Optional argument DURATION-CORRECTION shortens result by one day.
+
+FIXME: TZID-attributes are ignored....!
+FIXME: multiple comma-separated values should be allowed!"
   (if isodurationstring
       (save-match-data
         (string-match
   (if isodurationstring
       (save-match-data
         (string-match
@@ -440,7 +470,7 @@ multiple comma-separated values should be allowed!"
             (setq days (read (substring isodurationstring
                                         (match-beginning 3)
                                         (match-end 3))))
             (setq days (read (substring isodurationstring
                                         (match-beginning 3)
                                         (match-end 3))))
-            (when icalendar-duration-correction
+            (when duration-correction
               (setq days (1- days))))
            ((match-beginning 4)         ;days and time
             (if (match-beginning 5)
               (setq days (1- days))))
            ((match-beginning 4)         ;days and time
             (if (match-beginning 5)
@@ -607,12 +637,11 @@ takes care of european-style."
                  (setq month day)
                  (setq day x))))
             ( ;; date contains month names -- european-style
                  (setq month day)
                  (setq day x))))
             ( ;; date contains month names -- european-style
-             (and european-calendar-style
-                  (string-match (concat "\\s-*"
-                                        "0?\\([123]?[0-9]\\)[ \t/]\\s-*"
-                                        "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
-                                        "\\([0-9]\\{4\\}\\)")
-                                datestring))
+             (string-match (concat "\\s-*"
+                                   "0?\\([123]?[0-9]\\)[ \t/]\\s-*"
+                                   "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
+                                   "\\([0-9]\\{4\\}\\)")
+                           datestring)
              (setq day (read (substring datestring (match-beginning 1)
                                         (match-end 1))))
              (setq month (icalendar--get-month-number
              (setq day (read (substring datestring (match-beginning 1)
                                         (match-end 1))))
              (setq month (icalendar--get-month-number
@@ -621,12 +650,11 @@ takes care of european-style."
              (setq year (read (substring datestring (match-beginning 3)
                                          (match-end 3)))))
             ( ;; date contains month names -- non-european-style
              (setq year (read (substring datestring (match-beginning 3)
                                          (match-end 3)))))
             ( ;; date contains month names -- non-european-style
-             (and (not european-calendar-style)
-                  (string-match (concat "\\s-*"
-                                        "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
-                                        "0?\\([123]?[0-9]\\),?[ \t/]\\s-*"
-                                        "\\([0-9]\\{4\\}\\)")
-                                datestring))
+             (string-match (concat "\\s-*"
+                                   "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
+                                   "0?\\([123]?[0-9]\\),?[ \t/]\\s-*"
+                                   "\\([0-9]\\{4\\}\\)")
+                           datestring)
              (setq day (read (substring datestring (match-beginning 2)
                                         (match-end 2))))
              (setq month (icalendar--get-month-number
              (setq day (read (substring datestring (match-beginning 2)
                                         (match-end 2))))
              (setq month (icalendar--get-month-number
@@ -672,7 +700,7 @@ would be \"pm\"."
 ;; Export -- convert emacs-diary to icalendar
 ;; ======================================================================
 
 ;; Export -- convert emacs-diary to icalendar
 ;; ======================================================================
 
-;; User function
+;;;###autoload
 (defun icalendar-export-file (diary-filename ical-filename)
   "Export diary file to iCalendar format.
 All diary entries in the file DIARY-FILENAME are converted to iCalendar
 (defun icalendar-export-file (diary-filename ical-filename)
   "Export diary file to iCalendar format.
 All diary entries in the file DIARY-FILENAME are converted to iCalendar
@@ -686,7 +714,7 @@ Finto iCalendar file: ")
 (defalias 'icalendar-convert-diary-to-ical 'icalendar-export-file)
 (make-obsolete 'icalendar-convert-diary-to-ical 'icalendar-export-file)
 
 (defalias 'icalendar-convert-diary-to-ical 'icalendar-export-file)
 (make-obsolete 'icalendar-convert-diary-to-ical 'icalendar-export-file)
 
-;; User function
+;;;###autoload
 (defun icalendar-export-region (min max ical-filename)
   "Export region in diary file to iCalendar format.
 All diary entries in the region from MIN to MAX in the current buffer are
 (defun icalendar-export-region (min max ical-filename)
   "Export region in diary file to iCalendar format.
 All diary entries in the region from MIN to MAX in the current buffer are
@@ -702,20 +730,22 @@ FExport diary data into iCalendar file: ")
         (entry-main "")
         (entry-rest "")
         (header "")
         (entry-main "")
         (entry-rest "")
         (header "")
+        (contents-n-summary)
         (contents)
         (found-error nil)
         (nonmarker (concat "^" (regexp-quote diary-nonmarking-symbol)
         (contents)
         (found-error nil)
         (nonmarker (concat "^" (regexp-quote diary-nonmarking-symbol)
-                           "?")))
+                           "?"))
+        (other-elements nil))
     ;; prepare buffer with error messages
     (save-current-buffer
     ;; prepare buffer with error messages
     (save-current-buffer
-      (set-buffer (get-buffer-create " *icalendar-errors*"))
+      (set-buffer (get-buffer-create "*icalendar-errors*"))
       (erase-buffer))
 
     ;; here we go
     (save-excursion
       (goto-char min)
       (while (re-search-forward
       (erase-buffer))
 
     ;; here we go
     (save-excursion
       (goto-char min)
       (while (re-search-forward
-              "^\\([^ \t\n].*\\)\\(\\(\n[ \t].*\\)*\\)" max t)
+              "^\\([^ \t\n].+\\)\\(\\(\n[ \t].*\\)*\\)" max t)
         (setq entry-main (match-string 1))
         (if (match-beginning 2)
             (setq entry-rest (match-string 2))
         (setq entry-main (match-string 1))
         (if (match-beginning 2)
             (setq entry-rest (match-string 2))
@@ -726,369 +756,39 @@ FExport diary data into iCalendar file: ")
                              (car (cddr (current-time)))))
         (condition-case error-val
             (progn
                              (car (cddr (current-time)))))
         (condition-case error-val
             (progn
-              (cond
-               ;; anniversaries
-               ((string-match
-                 (concat nonmarker
-                         "%%(diary-anniversary \\([^)]+\\))\\s-*\\(.*\\)")
-                 entry-main)
-                (icalendar--dmsg "diary-anniversary %s" entry-main)
-                (let* ((datetime (substring entry-main (match-beginning 1)
-                                            (match-end 1)))
-                       (summary (icalendar--convert-string-for-export
-                                 (substring entry-main (match-beginning 2)
-                                            (match-end 2))))
-                       (startisostring (icalendar--datestring-to-isodate
-                                        datetime))
-                       (endisostring (icalendar--datestring-to-isodate
-                                      datetime 1)))
-                  (setq contents
-                        (concat "\nDTSTART;VALUE=DATE:" startisostring
-                                "\nDTEND;VALUE=DATE:" endisostring
-                                "\nSUMMARY:" summary
-                                "\nRRULE:FREQ=YEARLY;INTERVAL=1"
-                                ;; the following is redundant,
-                                ;; but korganizer seems to expect this... ;(
-                                ;; and evolution doesn't understand it... :(
-                                ;; so... who is wrong?!
-                                ";BYMONTH="
-                                (substring startisostring 4 6)
-                                ";BYMONTHDAY="
-                                (substring startisostring 6 8))))
-                (unless (string= entry-rest "")
-                  (setq contents
-                        (concat contents "\nDESCRIPTION:"
-                                (icalendar--convert-string-for-export
-                                 entry-rest)))))
-               ;; cyclic events
-               ;; %%(diary-cyclic )
-               ((string-match
-                 (concat nonmarker
-                         "%%(diary-cyclic \\([^ ]+\\) +"
-                         "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*\\(.*\\)")
-                 entry-main)
-                (icalendar--dmsg "diary-cyclic %s" entry-main)
-                (let* ((frequency (substring entry-main (match-beginning 1)
-                                             (match-end 1)))
-                       (datetime (substring entry-main (match-beginning 2)
-                                            (match-end 2)))
-                       (summary (icalendar--convert-string-for-export
-                                 (substring entry-main (match-beginning 3)
-                                            (match-end 3))))
-                       (startisostring (icalendar--datestring-to-isodate
-                                        datetime))
-                       (endisostring (icalendar--datestring-to-isodate
-                                      datetime 1)))
-                  (setq contents
-                        (concat "\nDTSTART;VALUE=DATE:" startisostring
-                                "\nDTEND;VALUE=DATE:" endisostring
-                                "\nSUMMARY:" summary
-                                "\nRRULE:FREQ=DAILY;INTERVAL=" frequency
-                                ;; strange: korganizer does not expect
-                                ;; BYSOMETHING here...
-                                )))
-                (unless (string= entry-rest "")
-                  (setq contents
-                        (concat contents "\nDESCRIPTION:"
-                                (icalendar--convert-string-for-export
-                                 entry-rest)))))
-               ;; diary-date -- FIXME
-               ((string-match
-                 (concat nonmarker
-                         "%%(diary-date \\([^)]+\\))\\s-*\\(.*\\)")
-                 entry-main)
-                (icalendar--dmsg "diary-date %s" entry-main)
-                (error "`diary-date' is not supported yet"))
-               ;; float events -- FIXME
-               ((string-match
-                 (concat nonmarker
-                         "%%(diary-float \\([^)]+\\))\\s-*\\(.*\\)")
-                 entry-main)
-                (icalendar--dmsg "diary-float %s" entry-main)
-                (error "`diary-float' is not supported yet"))
-               ;; block events
-               ((string-match
-                 (concat nonmarker
-                         "%%(diary-block \\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)"
-                         " +\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*"
-                         "\\(.*\\)")
-                 entry-main)
-                (icalendar--dmsg "diary-block %s" entry-main)
-                (let* ((startstring (substring entry-main
-                                               (match-beginning 1)
-                                               (match-end 1)))
-                       (endstring (substring entry-main
-                                             (match-beginning 2)
-                                             (match-end 2)))
-                       (summary (icalendar--convert-string-for-export
-                                 (substring entry-main (match-beginning 3)
-                                            (match-end 3))))
-                       (startisostring (icalendar--datestring-to-isodate
-                                        startstring))
-                       (endisostring (icalendar--datestring-to-isodate
-                                      endstring 1)))
-                  (setq contents
-                        (concat "\nDTSTART;VALUE=DATE:" startisostring
-                                "\nDTEND;VALUE=DATE:" endisostring
-                                "\nSUMMARY:" summary))
-                  (unless (string= entry-rest "")
-                    (setq contents
-                          (concat contents "\nDESCRIPTION:"
-                                  (icalendar--convert-string-for-export
-                                   entry-rest))))))
-               ;; other sexp diary entries -- FIXME
-               ((string-match
-                 (concat nonmarker
-                         "%%(\\([^)]+\\))\\s-*\\(.*\\)")
-                 entry-main)
-                (icalendar--dmsg "diary-sexp %s" entry-main)
-                (error "sexp-entries are not supported yet"))
-               ;; weekly by day
-               ;; Monday 8:30 Team meeting
-               ((and (string-match
-                      (concat nonmarker
-                              "\\([a-z]+\\)\\s-+"
-                              "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)"
-                              "\\([ap]m\\)?"
-                              "\\(-0?"
-                              "\\([1-9][0-9]?:[0-9][0-9]\\)"
-                              "\\([ap]m\\)?\\)?"
-                              "\\)?"
-                              "\\s-*\\(.*\\)$")
-                      entry-main)
-                     (icalendar--get-weekday-abbrev
-                      (substring entry-main (match-beginning 1)
-                                 (match-end 1))))
-                (icalendar--dmsg "weekly %s" entry-main)
-                (let* ((day (icalendar--get-weekday-abbrev
-                             (substring entry-main (match-beginning 1)
-                                        (match-end 1))))
-                       (starttimestring (icalendar--diarytime-to-isotime
-                                         (if (match-beginning 3)
-                                             (substring entry-main
-                                                        (match-beginning 3)
-                                                        (match-end 3))
-                                           nil)
-                                         (if (match-beginning 4)
-                                             (substring entry-main
-                                                        (match-beginning 4)
-                                                        (match-end 4))
-                                           nil)))
-                       (endtimestring (icalendar--diarytime-to-isotime
-                                       (if (match-beginning 6)
-                                           (substring entry-main
-                                                      (match-beginning 6)
-                                                      (match-end 6))
-                                         nil)
-                                       (if (match-beginning 7)
-                                           (substring entry-main
-                                                      (match-beginning 7)
-                                                      (match-end 7))
-                                         nil)))
-                       (summary (icalendar--convert-string-for-export
-                                 (substring entry-main (match-beginning 8)
-                                            (match-end 8)))))
-                  (when starttimestring
-                    (unless endtimestring
-                      (let ((time (read
-                                   (icalendar--rris "^T0?" ""
-                                                    starttimestring))))
-                        (setq endtimestring (format "T%06d"
-                                                    (+ 10000 time))))))
-                  (setq contents
-                        (concat "\nDTSTART;"
-                                (if starttimestring
-                                    "VALUE=DATE-TIME:"
-                                  "VALUE=DATE:")
-                                ;; find the correct week day,
-                                ;; 1st january 2000 was a saturday
-                                (format
-                                 "200001%02d"
-                                 (+ (icalendar--get-weekday-number day) 2))
-                                (or starttimestring "")
-                                "\nDTEND;"
-                                (if endtimestring
-                                    "VALUE=DATE-TIME:"
-                                  "VALUE=DATE:")
-                                (format
-                                 "200001%02d"
-                                 ;; end is non-inclusive!
-                                 (+ (icalendar--get-weekday-number day)
-                                    (if endtimestring 2 3)))
-                                (or endtimestring "")
-                                "\nSUMMARY:" summary
-                                "\nRRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY="
-                                day)))
-                (unless (string= entry-rest "")
-                  (setq contents
-                        (concat contents "\nDESCRIPTION:"
-                                (icalendar--convert-string-for-export
-                                 entry-rest)))))
-               ;; yearly by day
-               ;; 1 May Tag der Arbeit
-               ((string-match
-                 (concat nonmarker
-                         (if european-calendar-style
-                             "0?\\([1-9]+[0-9]?\\)\\s-+\\([a-z]+\\)\\s-+"
-                           "\\([a-z]+\\)\\s-+0?\\([1-9]+[0-9]?\\)\\s-+")
-                         "\\*?\\s-*"
-                         "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
-                         "\\("
-                         "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
-                         "\\)?"
-                         "\\s-*\\([^0-9]+.*\\)$" ; must not match years
-                         )
-                 entry-main)
-                (icalendar--dmsg "yearly %s" entry-main)
-                (let* ((daypos (if european-calendar-style 1 2))
-                       (monpos (if european-calendar-style 2 1))
-                       (day (read (substring entry-main
-                                             (match-beginning daypos)
-                                             (match-end daypos))))
-                       (month (icalendar--get-month-number
-                               (substring entry-main
-                                          (match-beginning monpos)
-                                          (match-end monpos))))
-                       (starttimestring (icalendar--diarytime-to-isotime
-                                         (if (match-beginning 4)
-                                             (substring entry-main
-                                                        (match-beginning 4)
-                                                        (match-end 4))
-                                           nil)
-                                         (if (match-beginning 5)
-                                             (substring entry-main
-                                                        (match-beginning 5)
-                                                        (match-end 5))
-                                           nil)))
-                       (endtimestring (icalendar--diarytime-to-isotime
-                                       (if (match-beginning 7)
-                                           (substring entry-main
-                                                      (match-beginning 7)
-                                                      (match-end 7))
-                                         nil)
-                                       (if (match-beginning 8)
-                                           (substring entry-main
-                                                      (match-beginning 8)
-                                                      (match-end 8))
-                                         nil)))
-                       (summary (icalendar--convert-string-for-export
-                                 (substring entry-main (match-beginning 9)
-                                            (match-end 9)))))
-                  (when starttimestring
-                    (unless endtimestring
-                      (let ((time (read
-                                   (icalendar--rris "^T0?" ""
-                                                    starttimestring))))
-                        (setq endtimestring (format "T%06d"
-                                                    (+ 10000 time))))))
-                  (setq contents
-                        (concat "\nDTSTART;"
-                                (if starttimestring "VALUE=DATE-TIME:"
-                                  "VALUE=DATE:")
-                                (format "1900%02d%02d" month day)
-                                (or starttimestring "")
-                                "\nDTEND;"
-                                (if endtimestring "VALUE=DATE-TIME:"
-                                  "VALUE=DATE:")
-                                ;; end is not included! shift by one day
-                                (icalendar--date-to-isodate
-                                 (list month day 1900)
-                                 (if endtimestring 0 1))
-                                (or endtimestring "")
-                                "\nSUMMARY:"
-                                summary
-                                "\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH="
-                                (format "%2d" month)
-                                ";BYMONTHDAY="
-                                (format "%2d" day))))
-                (unless (string= entry-rest "")
-                  (setq contents
-                        (concat contents "\nDESCRIPTION:"
-                                (icalendar--convert-string-for-export
-                                 entry-rest)))))
-               ;; "ordinary" events, start and end time given
-               ;; 1 Feb 2003 Hs Hochzeitsfeier, Dreieich
-               ((string-match
-                 (concat nonmarker
-                         "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)\\s-+"
-                         "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
-                         "\\("
-                         "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
-                         "\\)?"
-                         "\\s-*\\(.*\\)")
-                 entry-main)
-                (icalendar--dmsg "ordinary %s" entry-main)
-                (let* ((startdatestring (icalendar--datestring-to-isodate
-                                         (substring entry-main
-                                                    (match-beginning 1)
-                                                    (match-end 1))))
-                       (starttimestring (icalendar--diarytime-to-isotime
-                                         (if (match-beginning 3)
-                                             (substring entry-main
-                                                        (match-beginning 3)
-                                                        (match-end 3))
-                                           nil)
-                                         (if (match-beginning 4)
-                                             (substring entry-main
-                                                        (match-beginning 4)
-                                                        (match-end 4))
-                                           nil)))
-                       (endtimestring (icalendar--diarytime-to-isotime
-                                       (if (match-beginning 6)
-                                           (substring entry-main
-                                                      (match-beginning 6)
-                                                      (match-end 6))
-                                         nil)
-                                       (if (match-beginning 7)
-                                           (substring entry-main
-                                                      (match-beginning 7)
-                                                      (match-end 7))
-                                         nil)))
-                       (summary (icalendar--convert-string-for-export
-                                 (substring entry-main (match-beginning 8)
-                                            (match-end 8)))))
-                  (unless startdatestring
-                    (error "Could not parse date"))
-                  (when starttimestring
-                    (unless endtimestring
-                      (let ((time
-                             (read (icalendar--rris "^T0?" ""
-                                                    starttimestring))))
-                        (setq endtimestring (format "T%06d"
-                                                    (+ 10000 time))))))
-                  (setq contents (concat
-                                  "\nDTSTART;"
-                                  (if starttimestring "VALUE=DATE-TIME:"
-                                    "VALUE=DATE:")
-                                  startdatestring
-                                  (or starttimestring "")
-                                  "\nDTEND;"
-                                  (if endtimestring "VALUE=DATE-TIME:"
-                                    "VALUE=DATE:")
-                                  (icalendar--datestring-to-isodate
-                                   (substring entry-main
-                                              (match-beginning 1)
-                                              (match-end 1))
-                                   (if endtimestring 0 1))
-                                  (or endtimestring "")
-                                  "\nSUMMARY:"
-                                  summary))
-                  ;; could not parse the date
-                  (unless (string= entry-rest "")
-                    (setq contents
-                          (concat contents "\nDESCRIPTION:"
-                                  (icalendar--convert-string-for-export
-                                   entry-rest))))))
-               ;; everything else
-               (t
-                ;; Oops! what's that?
-                (error "Could not parse entry")))
+              (setq contents-n-summary
+                    (icalendar--convert-to-ical nonmarker entry-main))
+              (setq other-elements (icalendar--parse-summary-and-rest
+                                    (concat entry-main entry-rest)))
+              (setq contents (concat (car contents-n-summary)
+                                     "\nSUMMARY:" (cadr contents-n-summary)))
+              (let ((cla (cdr (assoc 'cla other-elements)))
+                    (des (cdr (assoc 'des other-elements)))
+                    (loc (cdr (assoc 'loc other-elements)))
+                    (org (cdr (assoc 'org other-elements)))
+                    (sta (cdr (assoc 'sta other-elements)))
+                    (sum (cdr (assoc 'sum other-elements)))
+                    (url (cdr (assoc 'url other-elements))))
+                (if cla
+                    (setq contents (concat contents "\nCLASS:" cla)))
+                (if des
+                    (setq contents (concat contents "\nDESCRIPTION:" des)))
+                (if loc
+                    (setq contents (concat contents "\nLOCATION:" loc)))
+                (if org
+                    (setq contents (concat contents "\nORGANIZER:" org)))
+                (if sta
+                    (setq contents (concat contents "\nSTATUS:" sta)))
+                ;;(if sum
+                ;;    (setq contents (concat contents "\nSUMMARY:" sum)))
+                (if url
+                    (setq contents (concat contents "\nURL:" url))))
               (setq result (concat result header contents "\nEND:VEVENT")))
           ;; handle errors
           (error
            (setq found-error t)
            (save-current-buffer
               (setq result (concat result header contents "\nEND:VEVENT")))
           ;; handle errors
           (error
            (setq found-error t)
            (save-current-buffer
-             (set-buffer (get-buffer-create " *icalendar-errors*"))
+             (set-buffer (get-buffer-create "*icalendar-errors*"))
              (insert (format "Error in line %d -- %s: `%s'\n"
                              (count-lines (point-min) (point))
                              (cadr error-val)
              (insert (format "Error in line %d -- %s: `%s'\n"
                              (count-lines (point-min) (point))
                              (cadr error-val)
@@ -1105,17 +805,646 @@ FExport diary data into iCalendar file: ")
           (insert result)
           (insert "\nEND:VCALENDAR\n")
           ;; save the diary file
           (insert result)
           (insert "\nEND:VCALENDAR\n")
           ;; save the diary file
-          (save-buffer))))
+          (save-buffer)
+          (unless found-error
+            (bury-buffer)))))
     found-error))
 
     found-error))
 
+(defun icalendar--convert-to-ical (nonmarker entry-main)
+  "Convert a diary entry to icalendar format.
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (or
+   ;; anniversaries -- %%(diary-anniversary ...)
+   (icalendar--convert-anniversary-to-ical nonmarker entry-main)
+   ;; cyclic events -- %%(diary-cyclic ...)
+   (icalendar--convert-cyclic-to-ical nonmarker entry-main)
+   ;; diary-date -- %%(diary-date ...)
+   (icalendar--convert-date-to-ical nonmarker entry-main)
+   ;; float events -- %%(diary-float ...)
+   (icalendar--convert-float-to-ical nonmarker entry-main)
+   ;; block events -- %%(diary-block ...)
+   (icalendar--convert-block-to-ical nonmarker entry-main)
+   ;; other sexp diary entries
+   (icalendar--convert-sexp-to-ical nonmarker entry-main)
+   ;; weekly by day -- Monday 8:30 Team meeting
+   (icalendar--convert-weekly-to-ical nonmarker entry-main)
+   ;; yearly by day -- 1 May Tag der Arbeit
+   (icalendar--convert-yearly-to-ical nonmarker entry-main)
+   ;; "ordinary" events, start and end time given
+   ;; 1 Feb 2003 blah
+   (icalendar--convert-ordinary-to-ical nonmarker entry-main)
+   ;; everything else
+   ;; Oops! what's that?
+   (error "Could not parse entry")))
+
+(defun icalendar--parse-summary-and-rest (summary-and-rest)
+  "Parse SUMMARY-AND-REST from a diary to fill iCalendar properties."
+  (save-match-data
+    (let* ((s icalendar-import-format)
+           (p-cla (or (string-match "%c" icalendar-import-format) -1))
+           (p-des (or (string-match "%d" icalendar-import-format) -1))
+           (p-loc (or (string-match "%l" icalendar-import-format) -1))
+           (p-org (or (string-match "%o" icalendar-import-format) -1))
+           (p-sum (or (string-match "%s" icalendar-import-format) -1))
+           (p-sta (or (string-match "%t" icalendar-import-format) -1))
+           (p-url (or (string-match "%u" icalendar-import-format) -1))
+           (p-list (sort (list p-cla p-des p-loc p-org p-sta p-sum p-url) '<))
+           pos-cla pos-des pos-loc pos-org pos-sta pos-sum pos-url)
+      (dotimes (i (length p-list))
+        (cond ((and (>= p-cla 0) (= (nth i p-list) p-cla))
+               (setq pos-cla (+ 2 (* 2 i))))
+              ((and (>= p-des 0) (= (nth i p-list) p-des))
+               (setq pos-des (+ 2 (* 2 i))))
+              ((and (>= p-loc 0) (= (nth i p-list) p-loc))
+               (setq pos-loc (+ 2 (* 2 i))))
+              ((and (>= p-org 0) (= (nth i p-list) p-org))
+               (setq pos-org (+ 2 (* 2 i))))
+              ((and (>= p-sta 0) (= (nth i p-list) p-sta))
+               (setq pos-sta (+ 2 (* 2 i))))
+              ((and (>= p-sum 0) (= (nth i p-list) p-sum))
+               (setq pos-sum (+ 2 (* 2 i))))
+              ((and (>= p-url 0) (= (nth i p-list) p-url))
+               (setq pos-url (+ 2 (* 2 i))))))
+      (mapc (lambda (ij)
+              (setq s (icalendar--rris (car ij) (cadr ij) s t t)))
+            (list
+             ;; summary must be first! because of %s
+             (list "%s"
+                   (concat "\\(" icalendar-import-format-summary "\\)?"))
+             (list "%c"
+                   (concat "\\(" icalendar-import-format-class "\\)?"))
+             (list "%d"
+                   (concat "\\(" icalendar-import-format-description "\\)?"))
+             (list "%l"
+                   (concat "\\(" icalendar-import-format-location "\\)?"))
+             (list "%o"
+                   (concat "\\(" icalendar-import-format-organizer "\\)?"))
+             (list "%t"
+                   (concat "\\(" icalendar-import-format-status "\\)?"))
+             (list "%u"
+                   (concat "\\(" icalendar-import-format-url "\\)?"))))
+      (setq s (concat (icalendar--rris "%s" "\\(.*\\)" s nil t) " "))
+      (if (string-match s summary-and-rest)
+          (let (cla des loc org sta sum url)
+            (if (and pos-sum (match-beginning pos-sum))
+                (setq sum (substring summary-and-rest
+                                     (match-beginning pos-sum)
+                                     (match-end pos-sum))))
+            (if (and pos-cla (match-beginning pos-cla))
+                (setq cla (substring summary-and-rest
+                                     (match-beginning pos-cla)
+                                     (match-end pos-cla))))
+            (if (and pos-des (match-beginning pos-des))
+                (setq des (substring summary-and-rest
+                                     (match-beginning pos-des)
+                                     (match-end pos-des))))
+            (if (and pos-loc (match-beginning pos-loc))
+                (setq loc (substring summary-and-rest
+                                     (match-beginning pos-loc)
+                                     (match-end pos-loc))))
+            (if (and pos-org (match-beginning pos-org))
+                (setq org (substring summary-and-rest
+                                     (match-beginning pos-org)
+                                     (match-end pos-org))))
+            (if (and pos-sta (match-beginning pos-sta))
+                (setq sta (substring summary-and-rest
+                                     (match-beginning pos-sta)
+                                     (match-end pos-sta))))
+            (if (and pos-url (match-beginning pos-url))
+                (setq url (substring summary-and-rest
+                                     (match-beginning pos-url)
+                                     (match-end pos-url))))
+            (list (if cla (cons 'cla cla) nil)
+                  (if des (cons 'des des) nil)
+                  (if loc (cons 'loc loc) nil)
+                  (if org (cons 'org org) nil)
+                  (if sta (cons 'sta sta) nil)
+                  ;;(if sum (cons 'sum sum) nil)
+                  (if url (cons 'url url) nil)))))))
+
+;; subroutines for icalendar-export-region
+(defun icalendar--convert-ordinary-to-ical (nonmarker entry-main)
+  "Convert \"ordinary\" diary entry to icalendar format.
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (string-match (concat nonmarker
+                            "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)\\s-*"
+                            "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
+                            "\\("
+                            "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
+                            "\\)?"
+                            "\\s-*\\(.*?\\) ?$")
+                    entry-main)
+      (let* ((datetime (substring entry-main (match-beginning 1)
+                                  (match-end 1)))
+             (startisostring (icalendar--datestring-to-isodate
+                              datetime))
+             (endisostring (icalendar--datestring-to-isodate
+                            datetime 1))
+             (starttimestring (icalendar--diarytime-to-isotime
+                               (if (match-beginning 3)
+                                   (substring entry-main
+                                              (match-beginning 3)
+                                              (match-end 3))
+                                 nil)
+                               (if (match-beginning 4)
+                                   (substring entry-main
+                                              (match-beginning 4)
+                                              (match-end 4))
+                                 nil)))
+             (endtimestring (icalendar--diarytime-to-isotime
+                             (if (match-beginning 6)
+                                 (substring entry-main
+                                            (match-beginning 6)
+                                            (match-end 6))
+                               nil)
+                             (if (match-beginning 7)
+                                 (substring entry-main
+                                            (match-beginning 7)
+                                            (match-end 7))
+                               nil)))
+             (summary (icalendar--convert-string-for-export
+                       (substring entry-main (match-beginning 8)
+                                  (match-end 8)))))
+        (icalendar--dmsg "ordinary %s" entry-main)
+
+        (unless startisostring
+          (error "Could not parse date"))
+        (when starttimestring
+          (unless endtimestring
+            (let ((time
+                   (read (icalendar--rris "^T0?" ""
+                                          starttimestring))))
+              (setq endtimestring (format "T%06d"
+                                          (+ 10000 time))))))
+        (list (concat "\nDTSTART;"
+                      (if starttimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      startisostring
+                      (or starttimestring "")
+                      "\nDTEND;"
+                      (if endtimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      (if starttimestring
+                          startisostring
+                        endisostring)
+                      (or endtimestring ""))
+              summary))
+    ;; no match
+    nil))
+
+(defun icalendar--convert-weekly-to-ical (nonmarker entry-main)
+  "Convert weekly diary entry to icalendar format.
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (and (string-match (concat nonmarker
+                                 "\\([a-z]+\\)\\s-+"
+                                 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)"
+                                 "\\([ap]m\\)?"
+                                 "\\(-0?"
+                                 "\\([1-9][0-9]?:[0-9][0-9]\\)"
+                                 "\\([ap]m\\)?\\)?"
+                                 "\\)?"
+                                 "\\s-*\\(.*?\\) ?$")
+                         entry-main)
+           (icalendar--get-weekday-abbrev
+            (substring entry-main (match-beginning 1)
+                       (match-end 1))))
+      (let* ((day (icalendar--get-weekday-abbrev
+                   (substring entry-main (match-beginning 1)
+                              (match-end 1))))
+             (starttimestring (icalendar--diarytime-to-isotime
+                               (if (match-beginning 3)
+                                   (substring entry-main
+                                              (match-beginning 3)
+                                              (match-end 3))
+                                 nil)
+                               (if (match-beginning 4)
+                                   (substring entry-main
+                                              (match-beginning 4)
+                                              (match-end 4))
+                                 nil)))
+             (endtimestring (icalendar--diarytime-to-isotime
+                             (if (match-beginning 6)
+                                 (substring entry-main
+                                            (match-beginning 6)
+                                            (match-end 6))
+                               nil)
+                             (if (match-beginning 7)
+                                 (substring entry-main
+                                            (match-beginning 7)
+                                            (match-end 7))
+                               nil)))
+             (summary (icalendar--convert-string-for-export
+                       (substring entry-main (match-beginning 8)
+                                  (match-end 8)))))
+        (icalendar--dmsg "weekly %s" entry-main)
+
+        (when starttimestring
+          (unless endtimestring
+            (let ((time (read
+                         (icalendar--rris "^T0?" ""
+                                          starttimestring))))
+              (setq endtimestring (format "T%06d"
+                                          (+ 10000 time))))))
+        (list (concat "\nDTSTART;"
+                      (if starttimestring
+                          "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      ;; find the correct week day,
+                      ;; 1st january 2000 was a saturday
+                      (format
+                       "200001%02d"
+                       (+ (icalendar--get-weekday-number day) 2))
+                      (or starttimestring "")
+                      "\nDTEND;"
+                      (if endtimestring
+                          "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      (format
+                       "200001%02d"
+                       ;; end is non-inclusive!
+                       (+ (icalendar--get-weekday-number day)
+                          (if endtimestring 2 3)))
+                      (or endtimestring "")
+                      "\nRRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY="
+                      day)
+              summary))
+    ;; no match
+    nil))
+
+(defun icalendar--convert-yearly-to-ical (nonmarker entry-main)
+  "Convert yearly diary entry to icalendar format.
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (string-match (concat nonmarker
+                            (if european-calendar-style
+                                "0?\\([1-9]+[0-9]?\\)\\s-+\\([a-z]+\\)\\s-+"
+                              "\\([a-z]+\\)\\s-+0?\\([1-9]+[0-9]?\\)\\s-+")
+                            "\\*?\\s-*"
+                            "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
+                            "\\("
+                            "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
+                            "\\)?"
+                            "\\s-*\\([^0-9]+.*?\\) ?$" ; must not match years
+                            )
+                    entry-main)
+      (let* ((daypos (if european-calendar-style 1 2))
+             (monpos (if european-calendar-style 2 1))
+             (day (read (substring entry-main
+                                   (match-beginning daypos)
+                                   (match-end daypos))))
+             (month (icalendar--get-month-number
+                     (substring entry-main
+                                (match-beginning monpos)
+                                (match-end monpos))))
+             (starttimestring (icalendar--diarytime-to-isotime
+                               (if (match-beginning 4)
+                                   (substring entry-main
+                                              (match-beginning 4)
+                                              (match-end 4))
+                                 nil)
+                               (if (match-beginning 5)
+                                   (substring entry-main
+                                              (match-beginning 5)
+                                              (match-end 5))
+                                 nil)))
+             (endtimestring (icalendar--diarytime-to-isotime
+                             (if (match-beginning 7)
+                                 (substring entry-main
+                                            (match-beginning 7)
+                                            (match-end 7))
+                               nil)
+                             (if (match-beginning 8)
+                                 (substring entry-main
+                                            (match-beginning 8)
+                                            (match-end 8))
+                               nil)))
+             (summary (icalendar--convert-string-for-export
+                       (substring entry-main (match-beginning 9)
+                                  (match-end 9)))))
+        (icalendar--dmsg "yearly %s" entry-main)
+
+        (when starttimestring
+          (unless endtimestring
+            (let ((time (read
+                         (icalendar--rris "^T0?" ""
+                                          starttimestring))))
+              (setq endtimestring (format "T%06d"
+                                          (+ 10000 time))))))
+        (list (concat "\nDTSTART;"
+                      (if starttimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      (format "1900%02d%02d" month day)
+                      (or starttimestring "")
+                      "\nDTEND;"
+                      (if endtimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      ;; end is not included! shift by one day
+                      (icalendar--date-to-isodate
+                       (list month day 1900)
+                       (if endtimestring 0 1))
+                      (or endtimestring "")
+                      "\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH="
+                      (format "%2d" month)
+                      ";BYMONTHDAY="
+                      (format "%2d" day))
+              summary))
+    ;; no match
+    nil))
+
+(defun icalendar--convert-sexp-to-ical (nonmarker entry-main)
+  "Convert complex sexp diary entry to icalendar format -- unsupported!
+
+FIXME!
+
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (cond ((string-match (concat nonmarker
+                               "%%(and \\(([^)]+)\\))\\(\\s-*.*?\\) ?$")
+                       entry-main)
+         ;; simple sexp entry as generated by icalendar.el: strip off the
+         ;; unnecessary (and)
+         (icalendar--dmsg "diary-sexp from icalendar.el %s" entry-main)
+         (icalendar--convert-to-ical
+          nonmarker
+          (concat "%%"
+                  (substring entry-main (match-beginning 1) (match-end 1))
+                  (substring entry-main (match-beginning 2) (match-end 2)))))
+        ((string-match (concat nonmarker
+                               "%%([^)]+)\\s-*.*")
+                       entry-main)
+         (icalendar--dmsg "diary-sexp %s" entry-main)
+         (error "Sexp-entries are not supported yet"))
+        (t
+         ;; no match
+         nil)))
+
+(defun icalendar--convert-block-to-ical (nonmarker entry-main)
+  "Convert block diary entry to icalendar format.
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (string-match (concat nonmarker
+                            "%%(diary-block \\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)"
+                            " +\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*"
+                            "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
+                            "\\("
+                            "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
+                            "\\)?"
+                            "\\s-*\\(.*?\\) ?$")
+                    entry-main)
+      (let* ((startstring (substring entry-main
+                                     (match-beginning 1)
+                                     (match-end 1)))
+             (endstring (substring entry-main
+                                   (match-beginning 2)
+                                   (match-end 2)))
+             (startisostring (icalendar--datestring-to-isodate
+                              startstring))
+             (endisostring (icalendar--datestring-to-isodate
+                            endstring))
+             (endisostring+1 (icalendar--datestring-to-isodate
+                              endstring 1))
+             (starttimestring (icalendar--diarytime-to-isotime
+                               (if (match-beginning 4)
+                                   (substring entry-main
+                                              (match-beginning 4)
+                                              (match-end 4))
+                                 nil)
+                               (if (match-beginning 5)
+                                   (substring entry-main
+                                              (match-beginning 5)
+                                              (match-end 5))
+                                 nil)))
+             (endtimestring (icalendar--diarytime-to-isotime
+                             (if (match-beginning 7)
+                                 (substring entry-main
+                                            (match-beginning 7)
+                                            (match-end 7))
+                               nil)
+                             (if (match-beginning 8)
+                                 (substring entry-main
+                                            (match-beginning 8)
+                                            (match-end 8))
+                               nil)))
+             (summary (icalendar--convert-string-for-export
+                       (substring entry-main (match-beginning 9)
+                                  (match-end 9)))))
+        (icalendar--dmsg "diary-block %s" entry-main)
+        (when starttimestring
+          (unless endtimestring
+            (let ((time
+                   (read (icalendar--rris "^T0?" ""
+                                          starttimestring))))
+              (setq endtimestring (format "T%06d"
+                                          (+ 10000 time))))))
+        (if starttimestring
+            ;; with time -> write rrule
+            (list (concat "\nDTSTART;VALUE=DATE-TIME:"
+                          startisostring
+                          starttimestring
+                          "\nDTEND;VALUE=DATE-TIME:"
+                          startisostring
+                          endtimestring
+                          "\nRRULE:FREQ=DAILY;INTERVAL=1;UNTIL="
+                          endisostring)
+                  summary)
+          ;; no time -> write long event
+          (list (concat "\nDTSTART;VALUE=DATE:" startisostring
+                        "\nDTEND;VALUE=DATE:" endisostring+1)
+                summary)))
+    ;; no match
+    nil))
+
+(defun icalendar--convert-float-to-ical (nonmarker entry-main)
+  "Convert float diary entry to icalendar format -- unsupported!
+
+FIXME!
+
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (string-match (concat nonmarker
+                            "%%(diary-float \\([^)]+\\))\\s-*\\(.*?\\) ?$")
+                    entry-main)
+      (progn
+        (icalendar--dmsg "diary-float %s" entry-main)
+        (error "`diary-float' is not supported yet"))
+    ;; no match
+    nil))
+
+(defun icalendar--convert-date-to-ical (nonmarker entry-main)
+  "Convert `diary-date' diary entry to icalendar format -- unsupported!
+
+FIXME!
+
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (string-match (concat nonmarker
+                            "%%(diary-date \\([^)]+\\))\\s-*\\(.*?\\) ?$")
+                    entry-main)
+      (progn
+        (icalendar--dmsg "diary-date %s" entry-main)
+        (error "`diary-date' is not supported yet"))
+    ;; no match
+    nil))
+
+(defun icalendar--convert-cyclic-to-ical (nonmarker entry-main)
+  "Convert `diary-cyclic' diary entry to icalendar format.
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (string-match (concat nonmarker
+                            "%%(diary-cyclic \\([^ ]+\\) +"
+                            "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*"
+                            "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
+                            "\\("
+                            "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
+                            "\\)?"
+                            "\\s-*\\(.*?\\) ?$")
+                    entry-main)
+      (let* ((frequency (substring entry-main (match-beginning 1)
+                                   (match-end 1)))
+             (datetime (substring entry-main (match-beginning 2)
+                                  (match-end 2)))
+             (startisostring (icalendar--datestring-to-isodate
+                              datetime))
+             (endisostring (icalendar--datestring-to-isodate
+                            datetime))
+             (endisostring+1 (icalendar--datestring-to-isodate
+                              datetime 1))
+             (starttimestring (icalendar--diarytime-to-isotime
+                               (if (match-beginning 4)
+                                   (substring entry-main
+                                              (match-beginning 4)
+                                              (match-end 4))
+                                 nil)
+                               (if (match-beginning 5)
+                                   (substring entry-main
+                                              (match-beginning 5)
+                                              (match-end 5))
+                                 nil)))
+             (endtimestring (icalendar--diarytime-to-isotime
+                             (if (match-beginning 7)
+                                 (substring entry-main
+                                            (match-beginning 7)
+                                            (match-end 7))
+                               nil)
+                             (if (match-beginning 8)
+                                 (substring entry-main
+                                            (match-beginning 8)
+                                            (match-end 8))
+                               nil)))
+             (summary (icalendar--convert-string-for-export
+                       (substring entry-main (match-beginning 9)
+                                  (match-end 9)))))
+        (icalendar--dmsg "diary-cyclic %s" entry-main)
+        (when starttimestring
+          (unless endtimestring
+            (let ((time
+                   (read (icalendar--rris "^T0?" ""
+                                          starttimestring))))
+              (setq endtimestring (format "T%06d"
+                                          (+ 10000 time))))))
+        (list (concat "\nDTSTART;"
+                      (if starttimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      startisostring
+                      (or starttimestring "")
+                      "\nDTEND;"
+                      (if endtimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      (if endtimestring endisostring endisostring+1)
+                      (or endtimestring "")
+                      "\nRRULE:FREQ=DAILY;INTERVAL=" frequency
+                      ;; strange: korganizer does not expect
+                      ;; BYSOMETHING here...
+                      )
+              summary))
+    ;; no match
+    nil))
+
+(defun icalendar--convert-anniversary-to-ical (nonmarker entry-main)
+  "Convert `diary-anniversary' diary entry to icalendar format.
+NONMARKER is a regular expression matching the start of non-marking
+entries.  ENTRY-MAIN is the first line of the diary entry."
+  (if (string-match (concat nonmarker
+                            "%%(diary-anniversary \\([^)]+\\))\\s-*"
+                            "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
+                            "\\("
+                            "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
+                            "\\)?"
+                            "\\s-*\\(.*?\\) ?$")
+                    entry-main)
+      (let* ((datetime (substring entry-main (match-beginning 1)
+                                  (match-end 1)))
+             (startisostring (icalendar--datestring-to-isodate
+                              datetime))
+             (endisostring (icalendar--datestring-to-isodate
+                            datetime 1))
+             (starttimestring (icalendar--diarytime-to-isotime
+                               (if (match-beginning 3)
+                                   (substring entry-main
+                                              (match-beginning 3)
+                                              (match-end 3))
+                                 nil)
+                               (if (match-beginning 4)
+                                   (substring entry-main
+                                              (match-beginning 4)
+                                              (match-end 4))
+                                 nil)))
+             (endtimestring (icalendar--diarytime-to-isotime
+                             (if (match-beginning 6)
+                                 (substring entry-main
+                                            (match-beginning 6)
+                                            (match-end 6))
+                               nil)
+                             (if (match-beginning 7)
+                                 (substring entry-main
+                                            (match-beginning 7)
+                                            (match-end 7))
+                               nil)))
+             (summary (icalendar--convert-string-for-export
+                       (substring entry-main (match-beginning 8)
+                                  (match-end 8)))))
+        (icalendar--dmsg "diary-anniversary %s" entry-main)
+        (when starttimestring
+          (unless endtimestring
+            (let ((time
+                   (read (icalendar--rris "^T0?" ""
+                                          starttimestring))))
+              (setq endtimestring (format "T%06d"
+                                          (+ 10000 time))))))
+        (list (concat "\nDTSTART;"
+                      (if starttimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      startisostring
+                      (or starttimestring "")
+                      "\nDTEND;"
+                      (if endtimestring "VALUE=DATE-TIME:"
+                        "VALUE=DATE:")
+                      endisostring
+                      (or endtimestring "")
+                      "\nRRULE:FREQ=YEARLY;INTERVAL=1"
+                      ;; the following is redundant,
+                      ;; but korganizer seems to expect this... ;(
+                      ;; and evolution doesn't understand it... :(
+                      ;; so... who is wrong?!
+                      ";BYMONTH="
+                      (substring startisostring 4 6)
+                      ";BYMONTHDAY="
+                      (substring startisostring 6 8))
+              summary))
+    ;; no match
+    nil))
+
 ;; ======================================================================
 ;; Import -- convert icalendar to emacs-diary
 ;; ======================================================================
 
 ;; ======================================================================
 ;; Import -- convert icalendar to emacs-diary
 ;; ======================================================================
 
-;; User function
+;;;###autoload
 (defun icalendar-import-file (ical-filename diary-filename
                                             &optional non-marking)
 (defun icalendar-import-file (ical-filename diary-filename
                                             &optional non-marking)
-  "Import a iCalendar file and append to a diary file.
+  "Import an iCalendar file and append to a diary file.
 Argument ICAL-FILENAME output iCalendar file.
 Argument DIARY-FILENAME input `diary-file'.
 Optional argument NON-MARKING determines whether events are created as
 Argument ICAL-FILENAME output iCalendar file.
 Argument DIARY-FILENAME input `diary-file'.
 Optional argument NON-MARKING determines whether events are created as
@@ -1129,7 +1458,7 @@ p")
     (set-buffer (find-file ical-filename))
     (icalendar-import-buffer diary-filename t non-marking)))
 
     (set-buffer (find-file ical-filename))
     (icalendar-import-buffer diary-filename t non-marking)))
 
-;; User function
+;;;###autoload
 (defun icalendar-import-buffer (&optional diary-file do-not-ask
                                           non-marking)
   "Extract iCalendar events from current buffer.
 (defun icalendar-import-buffer (&optional diary-file do-not-ask
                                           non-marking)
   "Extract iCalendar events from current buffer.
@@ -1168,10 +1497,12 @@ buffer `*icalendar-errors*'."
                              ical-contents
                              diary-file do-not-ask non-marking))
           (when diary-file
                              ical-contents
                              diary-file do-not-ask non-marking))
           (when diary-file
-            ;; save the diary file
-            (save-current-buffer
-              (set-buffer (find-buffer-visiting diary-file))
-              (save-buffer)))
+            ;; save the diary file if it is visited already
+            (let ((b (find-buffer-visiting diary-file)))
+              (when b
+                (save-current-buffer
+                  (set-buffer b)
+                  (save-buffer)))))
           (message "Converting icalendar...done")
           ;; return t if no error occured
           (not ical-errors))
           (message "Converting icalendar...done")
           ;; return t if no error occured
           (not ical-errors))
@@ -1183,18 +1514,17 @@ buffer `*icalendar-errors*'."
 (defalias 'icalendar-extract-ical-from-buffer 'icalendar-import-buffer)
 (make-obsolete 'icalendar-extract-ical-from-buffer 'icalendar-import-buffer)
 
 (defalias 'icalendar-extract-ical-from-buffer 'icalendar-import-buffer)
 (make-obsolete 'icalendar-extract-ical-from-buffer 'icalendar-import-buffer)
 
-;; ======================================================================
-;; private area
-;; ======================================================================
-
 (defun icalendar--format-ical-event (event)
   "Create a string representation of an iCalendar EVENT."
   (let ((string icalendar-import-format)
         (conversion-list
 (defun icalendar--format-ical-event (event)
   "Create a string representation of an iCalendar EVENT."
   (let ((string icalendar-import-format)
         (conversion-list
-         '(("%d" DESCRIPTION icalendar-import-format-description)
-           ("%s" SUMMARY     icalendar-import-format-subject)
+         '(("%c" CLASS       icalendar-import-format-class)
+           ("%d" DESCRIPTION icalendar-import-format-description)
            ("%l" LOCATION    icalendar-import-format-location)
            ("%l" LOCATION    icalendar-import-format-location)
-           ("%o" ORGANIZER   icalendar-import-format-organizer))))
+           ("%o" ORGANIZER   icalendar-import-format-organizer)
+           ("%s" SUMMARY     icalendar-import-format-summary)
+           ("%t" STATUS      icalendar-import-format-status)
+           ("%u" URL         icalendar-import-format-url))))
     ;; convert the specifiers in the format string
     (mapcar (lambda (i)
               (let* ((spec (car i))
     ;; convert the specifiers in the format string
     (mapcar (lambda (i)
               (let* ((spec (car i))
@@ -1207,24 +1537,26 @@ buffer `*icalendar-errors*'."
                         (icalendar--rris "%s"
                                          (icalendar--convert-string-for-import
                                           contents)
                         (icalendar--rris "%s"
                                          (icalendar--convert-string-for-import
                                           contents)
-                                         (symbol-value format))))
+                                         (symbol-value format)
+                                         t t)))
                 (setq string (icalendar--rris spec
                                               formatted-contents
                 (setq string (icalendar--rris spec
                                               formatted-contents
-                                              string))))
+                                              string
+                                              t t))))
             conversion-list)
     string))
 
 (defun icalendar--convert-ical-to-diary (ical-list diary-file
                                                    &optional do-not-ask
                                                    non-marking)
             conversion-list)
     string))
 
 (defun icalendar--convert-ical-to-diary (ical-list diary-file
                                                    &optional do-not-ask
                                                    non-marking)
-  "Convert an iCalendar file to an Emacs diary file.
+  "Convert Calendar data to an Emacs diary file.
 Import VEVENTS from the iCalendar object ICAL-LIST and saves them to a
 DIARY-FILE.  If DO-NOT-ASK is nil the user is asked for each event
 whether to actually import it.  NON-MARKING determines whether diary
 events are created as non-marking.
 This function attempts to return t if something goes wrong.  In this
 case an error string which describes all the errors and problems is
 Import VEVENTS from the iCalendar object ICAL-LIST and saves them to a
 DIARY-FILE.  If DO-NOT-ASK is nil the user is asked for each event
 whether to actually import it.  NON-MARKING determines whether diary
 events are created as non-marking.
 This function attempts to return t if something goes wrong.  In this
 case an error string which describes all the errors and problems is
-written into the buffer ` *icalendar-errors*'."
+written into the buffer `*icalendar-errors*'."
   (let* ((ev (icalendar--all-events ical-list))
          (error-string "")
          (event-ok t)
   (let* ((ev (icalendar--all-events ical-list))
          (error-string "")
          (event-ok t)
@@ -1236,180 +1568,67 @@ written into the buffer ` *icalendar-errors*'."
       (setq ev (cdr ev))
       (setq event-ok nil)
       (condition-case error-val
       (setq ev (cdr ev))
       (setq event-ok nil)
       (condition-case error-val
-          (let* ((dtstart (icalendar--decode-isodatetime
-                           (icalendar--get-event-property e 'DTSTART)))
+          (let* ((dtstart (icalendar--get-event-property e 'DTSTART))
+                 (dtstart-dec (icalendar--decode-isodatetime dtstart))
                  (start-d (icalendar--datetime-to-diary-date
                  (start-d (icalendar--datetime-to-diary-date
-                           dtstart))
-                 (start-t (icalendar--datetime-to-colontime dtstart))
-                 (dtend (icalendar--decode-isodatetime
-                         (icalendar--get-event-property e 'DTEND)))
+                           dtstart-dec))
+                 (start-t (icalendar--datetime-to-colontime dtstart-dec))
+                 (dtend (icalendar--get-event-property e 'DTEND))
+                 (dtend-dec (icalendar--decode-isodatetime dtend))
+                 (dtend-1-dec (icalendar--decode-isodatetime dtend -1))
                  end-d
                  end-d
+                 end-1-d
                  end-t
                  end-t
-                 (subject (icalendar--convert-string-for-import
+                 (summary (icalendar--convert-string-for-import
                            (or (icalendar--get-event-property e 'SUMMARY)
                            (or (icalendar--get-event-property e 'SUMMARY)
-                               "No Subject")))
+                               "No summary")))
                  (rrule (icalendar--get-event-property e 'RRULE))
                  (rdate (icalendar--get-event-property e 'RDATE))
                  (duration (icalendar--get-event-property e 'DURATION)))
                  (rrule (icalendar--get-event-property e 'RRULE))
                  (rdate (icalendar--get-event-property e 'RDATE))
                  (duration (icalendar--get-event-property e 'DURATION)))
-            (icalendar--dmsg "%s: %s" start-d subject)
+            (icalendar--dmsg "%s: `%s'" start-d summary)
             ;; check whether start-time is missing
             ;; check whether start-time is missing
-            (if  (and (icalendar--get-event-property-attributes
-                       e 'DTSTART)
-                      (string= (cadr (icalendar--get-event-property-attributes
-                                      e 'DTSTART))
-                               "DATE"))
+            (if  (and dtstart
+                      (string=
+                       (cadr (icalendar--get-event-property-attributes
+                              e 'DTSTART))
+                       "DATE"))
                 (setq start-t nil))
             (when duration
                 (setq start-t nil))
             (when duration
-              (let ((dtend2 (icalendar--add-decoded-times
-                             dtstart
-                             (icalendar--decode-isoduration duration))))
-                (if (and dtend (not (eq dtend dtend2)))
+              (let ((dtend-dec-d (icalendar--add-decoded-times
+                                  dtstart-dec
+                                  (icalendar--decode-isoduration duration)))
+                    (dtend-1-dec-d (icalendar--add-decoded-times
+                                    dtstart-dec
+                                    (icalendar--decode-isoduration duration
+                                                                   t))))
+                (if (and dtend-dec (not (eq dtend-dec dtend-dec-d)))
                     (message "Inconsistent endtime and duration for %s"
                     (message "Inconsistent endtime and duration for %s"
-                             subject))
-                (setq dtend dtend2)))
-            (setq end-d (if dtend
-                            (icalendar--datetime-to-diary-date dtend)
+                             summary))
+                (setq dtend-dec dtend-dec-d)
+                (setq dtend-1-dec dtend-1-dec-d)))
+            (setq end-d (if dtend-dec
+                            (icalendar--datetime-to-diary-date dtend-dec)
                           start-d))
                           start-d))
-            (setq end-t (if dtend
-                            (icalendar--datetime-to-colontime dtend)
+            (setq end-1-d (if dtend-1-dec
+                              (icalendar--datetime-to-diary-date dtend-1-dec)
+                            start-d))
+            (setq end-t (if (and
+                             dtend-dec
+                             (not (string=
+                                   (cadr
+                                    (icalendar--get-event-property-attributes
+                                     e 'DTEND))
+                                   "DATE")))
+                            (icalendar--datetime-to-colontime dtend-dec)
                           start-t))
             (icalendar--dmsg "start-d: %s, end-d: %s" start-d end-d)
             (cond
              ;; recurring event
              (rrule
                           start-t))
             (icalendar--dmsg "start-d: %s, end-d: %s" start-d end-d)
             (cond
              ;; recurring event
              (rrule
-              (icalendar--dmsg "recurring event")
-              (let* ((rrule-props (icalendar--split-value rrule))
-                     (frequency (cadr (assoc 'FREQ rrule-props)))
-                     (until (cadr (assoc 'UNTIL rrule-props)))
-                     (interval (read (cadr (assoc 'INTERVAL rrule-props)))))
-                (cond ((string-equal frequency "WEEKLY")
-                       (if (not start-t)
-                           (progn
-                             ;; weekly and all-day
-                             (icalendar--dmsg "weekly all-day")
-                             (if until
-                                 (let ((fro
-                                        (icalendar--datetime-to-diary-date
-                                         (icalendar--decode-isodatetime
-                                          (icalendar--get-event-property
-                                           e
-                                           'DTSTART))))
-                                       (unt
-                                        (icalendar--datetime-to-diary-date
-                                         (icalendar--decode-isodatetime
-                                          until -1))))
-                             (setq diary-string
-                                   (format
-                                    (concat "%%%%(and "
-                                            "(diary-cyclic %d %s) "
-                                            "(diary-block %s %s))")
-                                    (* interval 7)
-                                    (icalendar--datetime-to-diary-date
-                                     dtstart)
-                                    (icalendar--datetime-to-diary-date
-                                     dtstart)
-                                    (icalendar--datetime-to-diary-date
-                                     (icalendar--decode-isodatetime
-                                      until -1)))))
-                               (setq diary-string
-                                     (format "%%%%(and (diary-cyclic %d %s))"
-                                             (* interval 7)
-                                             (icalendar--datetime-to-diary-date
-                                              dtstart))))
-                             (setq event-ok t))
-                         ;; weekly and not all-day
-                         (let* ((byday (cadr (assoc 'BYDAY rrule-props)))
-                                (weekday
-                                 (icalendar--get-weekday-number byday)))
-                           (icalendar--dmsg "weekly not-all-day")
-                           (if until
-                               (let ((fro
-                                      (icalendar--datetime-to-diary-date
-                                       (icalendar--decode-isodatetime
-                                        (icalendar--get-event-property
-                                         e
-                                         'DTSTART))))
-                                     (unt
-                                      (icalendar--datetime-to-diary-date
-                                       (icalendar--decode-isodatetime
-                                        until))))
-                                 (setq diary-string
-                                       (format
-                                        (concat "%%%%(and "
-                                                "(diary-cyclic %d %s) "
-                                                "(diary-block %s %s)) "
-                                                "%s%s%s")
-                                        (* interval 7)
-                                        (icalendar--datetime-to-diary-date
-                                         dtstart)
-                                        (icalendar--datetime-to-diary-date
-                                         dtstart)
-                                        (icalendar--datetime-to-diary-date
-                                         (icalendar--decode-isodatetime
-                                          until))
-                                        start-t
-                                        (if end-t "-" "") (or end-t ""))))
-                             ;; no limit
-                             ;; FIXME!!!!
-                             ;; DTSTART;VALUE=DATE-TIME:20030919T090000
-                             ;; DTEND;VALUE=DATE-TIME:20030919T113000
-                             (setq diary-string
-                                   (format
-                                    "%%%%(and (diary-cyclic %s %s)) %s%s%s"
-                                    (* interval 7)
-                                    (icalendar--datetime-to-diary-date
-                                     dtstart)
-                                    start-t
-                                    (if end-t "-" "") (or end-t ""))))
-                           (setq event-ok t))))
-                      ;; yearly
-                      ((string-equal frequency "YEARLY")
-                       (icalendar--dmsg "yearly")
-                       (setq diary-string
-                             (format
-                              "%%%%(and (diary-anniversary %s))"
-                              (icalendar--datetime-to-diary-date dtstart)))
-                       (setq event-ok t))
-                      ;; FIXME: war auskommentiert:
-                      ((and (string-equal frequency "DAILY")
-                            ;;(not (string= start-d end-d))
-                            ;;(not start-t)
-                            ;;(not end-t)
-                            )
-                       (let ((ds (icalendar--datetime-to-diary-date
-                                  (icalendar--decode-isodatetime
-                                   (icalendar--get-event-property
-                                    e 'DTSTART))))
-                             (de (icalendar--datetime-to-diary-date
-                                  (icalendar--decode-isodatetime
-                                   until -1))))
-                         (setq diary-string
-                               (format
-                                "%%%%(and (diary-block %s %s))"
-                                ds de)))
-                       (setq event-ok t))))
-              ;; Handle exceptions from recurrence rules
-              (let ((ex-dates (icalendar--get-event-properties e
-                                                               'EXDATE)))
-                (while ex-dates
-                  (let* ((ex-start (icalendar--decode-isodatetime
-                  (car ex-dates)))
-                         (ex-d (icalendar--datetime-to-diary-date
-                         ex-start)))
-                    (setq diary-string
-                          (icalendar--rris "^%%(\\(and \\)?"
-                                           (format
-                                           "%%%%(and (not (diary-date %s)) "
-                                           ex-d)
-                                           diary-string)))
-                  (setq ex-dates (cdr ex-dates))))
-              ;; FIXME: exception rules are not recognized
-              (if (icalendar--get-event-property e 'EXRULE)
-                  (setq diary-string
-                        (concat diary-string
-                                "\n Exception rules: "
-                                (icalendar--get-event-properties
-                                 e 'EXRULE)))))
+              (setq diary-string
+                    (icalendar--convert-recurring-to-diary e dtstart-dec start-t
+                                                           end-t))
+              (setq event-ok t))
              (rdate
               (icalendar--dmsg "rdate event")
               (setq diary-string "")
              (rdate
               (icalendar--dmsg "rdate event")
               (setq diary-string "")
@@ -1421,38 +1640,22 @@ written into the buffer ` *icalendar-errors*'."
              ;; non-recurring event
              ;; all-day event
              ((not (string= start-d end-d))
              ;; non-recurring event
              ;; all-day event
              ((not (string= start-d end-d))
-              (icalendar--dmsg "non-recurring event")
-              (let ((ds (icalendar--datetime-to-diary-date dtstart))
-                    (de (icalendar--datetime-to-diary-date
-                         (icalendar--decode-isodatetime
-                          (icalendar--get-event-property e 'DTEND)
-                          -1))))
-                (setq diary-string
-                      (format "%%%%(and (diary-block %s %s))"
-                              ds de)))
+              (setq diary-string
+                    (icalendar--convert-non-recurring-all-day-to-diary
+                     e start-d end-1-d))
               (setq event-ok t))
              ;; not all-day
              ((and start-t (or (not end-t)
                                (not (string= start-t end-t))))
               (setq event-ok t))
              ;; not all-day
              ((and start-t (or (not end-t)
                                (not (string= start-t end-t))))
-              (icalendar--dmsg "not all day event")
-              (cond (end-t
-                     (setq diary-string
-                           (format "%s %s-%s"
-                                   (icalendar--datetime-to-diary-date
-                                    dtstart "/")
-                                   start-t end-t)))
-                    (t
-                     (setq diary-string
-                           (format "%s %s"
-                                   (icalendar--datetime-to-diary-date
-                                    dtstart "/")
-                                   start-t))))
+              (setq diary-string
+                    (icalendar--convert-non-recurring-not-all-day-to-diary
+                     e dtstart-dec dtend-dec start-t end-t))
               (setq event-ok t))
              ;; all-day event
              (t
               (icalendar--dmsg "all day event")
               (setq diary-string (icalendar--datetime-to-diary-date
               (setq event-ok t))
              ;; all-day event
              (t
               (icalendar--dmsg "all day event")
               (setq diary-string (icalendar--datetime-to-diary-date
-                                  dtstart "/"))
+                                  dtstart-dec "/"))
               (setq event-ok t)))
             ;; add all other elements unless the user doesn't want to have
             ;; them
               (setq event-ok t)))
             ;; add all other elements unless the user doesn't want to have
             ;; them
@@ -1461,9 +1664,9 @@ written into the buffer ` *icalendar-errors*'."
                   (setq diary-string
                         (concat diary-string " "
                                 (icalendar--format-ical-event e)))
                   (setq diary-string
                         (concat diary-string " "
                                 (icalendar--format-ical-event e)))
-                  (if do-not-ask (setq subject nil))
+                  (if do-not-ask (setq summary nil))
                   (icalendar--add-diary-entry diary-string diary-file
                   (icalendar--add-diary-entry diary-string diary-file
-                                              non-marking subject))
+                                              non-marking summary))
               ;; event was not ok
               (setq found-error t)
               (setq error-string
               ;; event was not ok
               (setq found-error t)
               (setq error-string
@@ -1476,33 +1679,259 @@ written into the buffer ` *icalendar-errors*'."
          (setq found-error t)
          (setq error-string (format "%s\n%s\nCannot handle this event: %s"
                                     error-val error-string e))
          (setq found-error t)
          (setq error-string (format "%s\n%s\nCannot handle this event: %s"
                                     error-val error-string e))
-         (message error-string))))
+         (message "%s" error-string))))
     (if found-error
         (save-current-buffer
     (if found-error
         (save-current-buffer
-          (set-buffer (get-buffer-create " *icalendar-errors*"))
+          (set-buffer (get-buffer-create "*icalendar-errors*"))
           (erase-buffer)
           (insert error-string)))
     (message "Converting icalendar...done")
     found-error))
 
           (erase-buffer)
           (insert error-string)))
     (message "Converting icalendar...done")
     found-error))
 
+;; subroutines for importing
+(defun icalendar--convert-recurring-to-diary (e dtstart-dec start-t end-t)
+  "Convert recurring icalendar event E to diary format.
+
+DTSTART-DEC is the DTSTART property of E.
+START-T is the event's start time in diary format.
+END-T is the event's end time in diary format."
+  (icalendar--dmsg "recurring event")
+  (let* ((rrule        (icalendar--get-event-property e 'RRULE))
+         (rrule-props  (icalendar--split-value rrule))
+         (frequency    (cadr (assoc 'FREQ rrule-props)))
+         (until        (cadr (assoc 'UNTIL rrule-props)))
+         (count        (cadr (assoc 'COUNT rrule-props)))
+         (interval     (read (or (cadr (assoc 'INTERVAL rrule-props)) "1")))
+         (dtstart-conv (icalendar--datetime-to-diary-date dtstart-dec))
+         (until-conv   (icalendar--datetime-to-diary-date
+                        (icalendar--decode-isodatetime until)))
+         (until-1-conv (icalendar--datetime-to-diary-date
+                        (icalendar--decode-isodatetime until -1)))
+         (result  ""))
+
+    ;; FIXME FIXME interval!!!!!!!!!!!!!
+
+    (when count
+      (if until
+          (message "Must not have UNTIL and COUNT -- ignoring COUNT element!")
+        (let ((until-1 0))
+          (cond ((string-equal frequency "DAILY")
+                 (setq until (icalendar--add-decoded-times
+                              dtstart-dec
+                              (list 0 0 0 (* (read count) interval) 0 0)))
+                 (setq until-1 (icalendar--add-decoded-times
+                                dtstart-dec
+                                (list 0 0 0 (* (- (read count) 1) interval)
+                                      0 0)))
+                 )
+                ((string-equal frequency "WEEKLY")
+                 (setq until (icalendar--add-decoded-times
+                              dtstart-dec
+                              (list 0 0 0 (* (read count) 7 interval) 0 0)))
+                 (setq until-1 (icalendar--add-decoded-times
+                                dtstart-dec
+                                (list 0 0 0 (* (- (read count) 1) 7
+                                               interval) 0 0)))
+                 )
+                ((string-equal frequency "MONTHLY")
+                 (setq until (icalendar--add-decoded-times
+                              dtstart-dec (list 0 0 0 0 (* (- (read count) 1)
+                                                           interval) 0)))
+                 (setq until-1 (icalendar--add-decoded-times
+                                dtstart-dec (list 0 0 0 0 (* (- (read count) 1)
+                                                             interval) 0)))
+                 )
+                ((string-equal frequency "YEARLY")
+                 (setq until (icalendar--add-decoded-times
+                              dtstart-dec (list 0 0 0 0 0 (* (- (read count) 1)
+                                                             interval))))
+                 (setq until-1 (icalendar--add-decoded-times
+                                dtstart-dec
+                                (list 0 0 0 0 0 (* (- (read count) 1)
+                                                   interval))))
+                 )
+                (t
+                 (message "Cannot handle COUNT attribute for `%s' events."
+                          frequency)))
+          (setq until-conv (icalendar--datetime-to-diary-date until))
+          (setq until-1-conv (icalendar--datetime-to-diary-date until-1))
+          ))
+      )
+    (cond ((string-equal frequency "WEEKLY")
+           (if (not start-t)
+               (progn
+                 ;; weekly and all-day
+                 (icalendar--dmsg "weekly all-day")
+                 (if until
+                     (setq result
+                           (format
+                            (concat "%%%%(and "
+                                    "(diary-cyclic %d %s) "
+                                    "(diary-block %s %s))")
+                            (* interval 7)
+                            dtstart-conv
+                            dtstart-conv
+                            (if count until-1-conv until-conv)
+                            ))
+                   (setq result
+                         (format "%%%%(and (diary-cyclic %d %s))"
+                                 (* interval 7)
+                                 dtstart-conv))))
+             ;; weekly and not all-day
+             (let* ((byday (cadr (assoc 'BYDAY rrule-props)))
+                    (weekday
+                     (icalendar--get-weekday-number byday)))
+               (icalendar--dmsg "weekly not-all-day")
+               (if until
+                   (setq result
+                         (format
+                          (concat "%%%%(and "
+                                  "(diary-cyclic %d %s) "
+                                  "(diary-block %s %s)) "
+                                  "%s%s%s")
+                          (* interval 7)
+                          dtstart-conv
+                          dtstart-conv
+                          until-conv
+                          (or start-t "")
+                          (if end-t "-" "") (or end-t "")))
+                 ;; no limit
+                 ;; FIXME!!!!
+                 ;; DTSTART;VALUE=DATE-TIME:20030919T090000
+                 ;; DTEND;VALUE=DATE-TIME:20030919T113000
+                 (setq result
+                       (format
+                        "%%%%(and (diary-cyclic %s %s)) %s%s%s"
+                        (* interval 7)
+                        dtstart-conv
+                        (or start-t "")
+                        (if end-t "-" "") (or end-t "")))))))
+          ;; yearly
+          ((string-equal frequency "YEARLY")
+           (icalendar--dmsg "yearly")
+           (if until
+               (setq result (format
+                             (concat "%%%%(and (diary-date %s %s t) "
+                                     "(diary-block %s %s)) %s%s%s")
+                             (if european-calendar-style (nth 3 dtstart-dec)
+                               (nth 4 dtstart-dec))
+                             (if european-calendar-style (nth 4 dtstart-dec)
+                               (nth 3 dtstart-dec))
+                             dtstart-conv
+                             until-conv
+                             (or start-t "")
+                             (if end-t "-" "") (or end-t "")))
+             (setq result (format
+                           "%%%%(and (diary-anniversary %s)) %s%s%s"
+                           dtstart-conv
+                           (or start-t "")
+                           (if end-t "-" "") (or end-t "")))))
+          ;; monthly
+          ((string-equal frequency "MONTHLY")
+           (icalendar--dmsg "monthly")
+           (setq result
+                 (format
+                  "%%%%(and (diary-date %s %s %s) (diary-block %s %s)) %s%s%s"
+                  (if european-calendar-style (nth 3 dtstart-dec) "t")
+                  (if european-calendar-style "t" (nth 3 dtstart-dec))
+                  "t"
+                  dtstart-conv
+                  (if until
+                      until-conv
+                    "1 1 9999") ;; FIXME: should be unlimited
+                  (or start-t "")
+                  (if end-t "-" "") (or end-t ""))))
+          ;; daily
+          ((and (string-equal frequency "DAILY"))
+           (if until
+               (setq result
+                     (format
+                      (concat "%%%%(and (diary-cyclic %s %s) "
+                              "(diary-block %s %s)) %s%s%s")
+                      interval dtstart-conv dtstart-conv
+                      (if count until-1-conv until-conv)
+                      (or start-t "")
+                      (if end-t "-" "") (or end-t "")))
+             (setq result
+                   (format
+                    "%%%%(and (diary-cyclic %s %s)) %s%s%s"
+                    interval
+                    dtstart-conv
+                    (or start-t "")
+                    (if end-t "-" "") (or end-t ""))))))
+    ;; Handle exceptions from recurrence rules
+    (let ((ex-dates (icalendar--get-event-properties e 'EXDATE)))
+      (while ex-dates
+        (let* ((ex-start (icalendar--decode-isodatetime
+                          (car ex-dates)))
+               (ex-d (icalendar--datetime-to-diary-date
+                      ex-start)))
+          (setq result
+                (icalendar--rris "^%%(\\(and \\)?"
+                                 (format
+                                  "%%%%(and (not (diary-date %s)) "
+                                  ex-d)
+                                 result)))
+        (setq ex-dates (cdr ex-dates))))
+    ;; FIXME: exception rules are not recognized
+    (if (icalendar--get-event-property e 'EXRULE)
+        (setq result
+              (concat result
+                      "\n Exception rules: "
+                      (icalendar--get-event-properties
+                       e 'EXRULE))))
+    result))
+
+(defun icalendar--convert-non-recurring-all-day-to-diary (event start-d end-d)
+  "Convert non-recurring icalendar EVENT to diary format.
+
+DTSTART is the decoded DTSTART property of E.
+Argument START-D gives the first day.
+Argument END-D gives the last day."
+  (icalendar--dmsg "non-recurring all-day event")
+  (format "%%%%(and (diary-block %s %s))" start-d end-d))
+
+(defun icalendar--convert-non-recurring-not-all-day-to-diary (event dtstart-dec
+                                                                    dtend-dec
+                                                                    start-t
+                                                                    end-t)
+  "Convert recurring icalendar EVENT to diary format.
+
+DTSTART-DEC is the decoded DTSTART property of E.
+DTEND-DEC is the decoded DTEND property of E.
+START-T is the event's start time in diary format.
+END-T is the event's end time in diary format."
+  (icalendar--dmsg "not all day event")
+  (cond (end-t
+         (format "%s %s-%s"
+                 (icalendar--datetime-to-diary-date
+                  dtstart-dec "/")
+                 start-t end-t))
+        (t
+         (format "%s %s"
+                 (icalendar--datetime-to-diary-date
+                  dtstart-dec "/")
+                 start-t))))
+
 (defun icalendar--add-diary-entry (string diary-file non-marking
 (defun icalendar--add-diary-entry (string diary-file non-marking
-                                          &optional subject)
+                                          &optional summary)
   "Add STRING to the diary file DIARY-FILE.
 STRING must be a properly formatted valid diary entry.  NON-MARKING
 determines whether diary events are created as non-marking.  If
   "Add STRING to the diary file DIARY-FILE.
 STRING must be a properly formatted valid diary entry.  NON-MARKING
 determines whether diary events are created as non-marking.  If
-SUBJECT is not nil it must be a string that gives the subject of the
+SUMMARY is not nil it must be a string that gives the summary of the
 entry.  In this case the user will be asked whether he wants to insert
 the entry."
 entry.  In this case the user will be asked whether he wants to insert
 the entry."
-  (when (or (not subject)
+  (when (or (not summary)
             (y-or-n-p (format "Add appointment for `%s' to diary? "
             (y-or-n-p (format "Add appointment for `%s' to diary? "
-                              subject)))
-    (when subject
+                              summary)))
+    (when summary
       (setq non-marking
             (y-or-n-p (format "Make appointment non-marking? "))))
     (save-window-excursion
       (unless diary-file
         (setq diary-file
               (read-file-name "Add appointment to this diary file: ")))
       (setq non-marking
             (y-or-n-p (format "Make appointment non-marking? "))))
     (save-window-excursion
       (unless diary-file
         (setq diary-file
               (read-file-name "Add appointment to this diary file: ")))
+      ;; Note: make-diary-entry will add a trailing blank char.... :(
       (make-diary-entry string non-marking diary-file))))
 
 (provide 'icalendar)
       (make-diary-entry string non-marking diary-file))))
 
 (provide 'icalendar)