comparison lisp/calendar/icalendar.el @ 66374:88fc54347c68

From Ulf Jasper <ulf.jasper@web.de>: (icalendar-version): Increase to 0.13. Now a string. (icalendar-import-format): Handle CLASS, STATUS, URL. Rename `subject' to `summary'. (icalendar-import-format-summary): Rename from `icalendar-import-format-subject'. (icalendar-import-format-url, icalendar-import-format-status) (icalendar-import-format-class): New variables. (icalendar--rris): Take variable argument list. (icalendar--datestring-to-isodate): Remove unnecessary calendar-style check when converting dates with explicit month names. (icalendar-export-region): Change return type of conversion subroutines. Bury current buffer unless error occurred. (icalendar--convert-to-ical) (icalendar--parse-summary-and-rest): New functions. (icalendar--convert-ordinary-to-ical) (icalendar--convert-weekly-to-ical) (icalendar--convert-yearly-to-ical) (icalendar--convert-block-to-ical) (icalendar--convert-cyclic-to-ical) (icalendar--convert-anniversary-to-ical): Change return type. Strip trailing blanks from subject. (icalendar--convert-sexp-to-ical): Change return type. Strip trailing blanks from subject. Handle simple sexp entries as generated by icalendar.el. (icalendar--convert-float-to-ical) (icalendar--convert-date-to-ical): Strip trailing blanks from subject. (icalendar-import-file): Doc fix. (icalendar--format-ical-event): Handle CLASS, STATUS, URL. Correct call to icalendar--rris. (icalendar--convert-ical-to-diary): Doc fix. Rename `subject' to `summary'. (icalendar--add-diary-entry): Rename `subject' to `summary'.
author Glenn Morris <rgm@gnu.org>
date Mon, 24 Oct 2005 07:27:39 +0000
parents a65f8ec418fc
children 8daf7d9a0771
comparison
equal deleted inserted replaced
66373:a0e9e787944e 66374:88fc54347c68
95 ;; + Which chars to (un)escape? 95 ;; + Which chars to (un)escape?
96 96
97 97
98 ;;; Code: 98 ;;; Code:
99 99
100 (defconst icalendar-version 0.12 100 (defconst icalendar-version "0.13"
101 "Version number of icalendar.el.") 101 "Version number of icalendar.el.")
102 102
103 ;; ====================================================================== 103 ;; ======================================================================
104 ;; Customizables 104 ;; Customizables
105 ;; ====================================================================== 105 ;; ======================================================================
111 (defcustom icalendar-import-format 111 (defcustom icalendar-import-format
112 "%s%d%l%o" 112 "%s%d%l%o"
113 "Format string for importing events from iCalendar into Emacs diary. 113 "Format string for importing events from iCalendar into Emacs diary.
114 This string defines how iCalendar events are inserted into diary 114 This string defines how iCalendar events are inserted into diary
115 file. Meaning of the specifiers: 115 file. Meaning of the specifiers:
116 %c Class, see `icalendar-import-format-class'
116 %d Description, see `icalendar-import-format-description' 117 %d Description, see `icalendar-import-format-description'
117 %l Location, see `icalendar-import-format-location' 118 %l Location, see `icalendar-import-format-location'
118 %o Organizer, see `icalendar-import-format-organizer' 119 %o Organizer, see `icalendar-import-format-organizer'
119 %s Subject, see `icalendar-import-format-subject'" 120 %s Summary, see `icalendar-import-format-summary'
121 %t Status, see `icalendar-import-format-status'
122 %u URL, see `icalendar-import-format-url'"
120 :type 'string 123 :type 'string
121 :group 'icalendar) 124 :group 'icalendar)
122 125
123 (defcustom icalendar-import-format-subject 126 (defcustom icalendar-import-format-summary
124 "%s" 127 "%s"
125 "Format string defining how the subject element is formatted. 128 "Format string defining how the summary element is formatted.
126 This applies only if the subject is not empty! `%s' is replaced 129 This applies only if the summary is not empty! `%s' is replaced
127 by the subject." 130 by the summary."
128 :type 'string 131 :type 'string
129 :group 'icalendar) 132 :group 'icalendar)
130 133
131 (defcustom icalendar-import-format-description 134 (defcustom icalendar-import-format-description
132 "\n Desc: %s" 135 "\n Desc: %s"
147 (defcustom icalendar-import-format-organizer 150 (defcustom icalendar-import-format-organizer
148 "\n Organizer: %s" 151 "\n Organizer: %s"
149 "Format string defining how the organizer element is formatted. 152 "Format string defining how the organizer element is formatted.
150 This applies only if the organizer is not empty! `%s' is 153 This applies only if the organizer is not empty! `%s' is
151 replaced by the organizer." 154 replaced by the organizer."
155 :type 'string
156 :group 'icalendar)
157
158 (defcustom icalendar-import-format-url
159 "\n URL: %s"
160 "Format string defining how the URL element is formatted.
161 This applies only if the URL is not empty! `%s' is replaced by
162 the URL."
163 :type 'string
164 :group 'icalendar)
165
166 (defcustom icalendar-import-format-status
167 "\n Status: %s"
168 "Format string defining how the status element is formatted.
169 This applies only if the status is not empty! `%s' is replaced by
170 the status."
171 :type 'string
172 :group 'icalendar)
173
174 (defcustom icalendar-import-format-class
175 "\n Class: %s"
176 "Format string defining how the class element is formatted.
177 This applies only if the class is not empty! `%s' is replaced by
178 the class."
152 :type 'string 179 :type 'string
153 :group 'icalendar) 180 :group 'icalendar)
154 181
155 (defvar icalendar-debug nil 182 (defvar icalendar-debug nil
156 "Enable icalendar debug messages.") 183 "Enable icalendar debug messages.")
193 (goto-char (point-min)) 220 (goto-char (point-min))
194 (while (re-search-forward "\r?\n[ \t]" nil t) 221 (while (re-search-forward "\r?\n[ \t]" nil t)
195 (replace-match "" nil nil))) 222 (replace-match "" nil nil)))
196 unfolded-buffer)) 223 unfolded-buffer))
197 224
198 (defsubst icalendar--rris (re rp st) 225 (defsubst icalendar--rris (&rest args)
199 "Replace regexp RE with RP in string ST and return the new string. 226 "Replace regular expression in string.
200 This is here for compatibility with XEmacs." 227 Pass ARGS to `replace-regexp-in-string' (Emacs) or to
228 `replace-in-string' (XEmacs)."
201 ;; XEmacs: 229 ;; XEmacs:
202 (if (fboundp 'replace-in-string) 230 (if (fboundp 'replace-in-string)
203 (save-match-data ;; apparently XEmacs needs save-match-data 231 (save-match-data ;; apparently XEmacs needs save-match-data
204 (replace-in-string st re rp)) 232 (apply 'replace-in-string args))
205 ;; Emacs: 233 ;; Emacs:
206 (replace-regexp-in-string re rp st))) 234 (apply 'replace-regexp-in-string args)))
207 235
208 (defun icalendar--read-element (invalue inparams) 236 (defun icalendar--read-element (invalue inparams)
209 "Recursively read the next iCalendar element in the current buffer. 237 "Recursively read the next iCalendar element in the current buffer.
210 INVALUE gives the current iCalendar element we are reading. 238 INVALUE gives the current iCalendar element we are reading.
211 INPARAMS gives the current parameters..... 239 INPARAMS gives the current parameters.....
607 (unless european-calendar-style 635 (unless european-calendar-style
608 (let ((x month)) 636 (let ((x month))
609 (setq month day) 637 (setq month day)
610 (setq day x)))) 638 (setq day x))))
611 ( ;; date contains month names -- european-style 639 ( ;; date contains month names -- european-style
612 (and european-calendar-style 640 (string-match (concat "\\s-*"
613 (string-match (concat "\\s-*" 641 "0?\\([123]?[0-9]\\)[ \t/]\\s-*"
614 "0?\\([123]?[0-9]\\)[ \t/]\\s-*" 642 "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
615 "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*" 643 "\\([0-9]\\{4\\}\\)")
616 "\\([0-9]\\{4\\}\\)") 644 datestring)
617 datestring))
618 (setq day (read (substring datestring (match-beginning 1) 645 (setq day (read (substring datestring (match-beginning 1)
619 (match-end 1)))) 646 (match-end 1))))
620 (setq month (icalendar--get-month-number 647 (setq month (icalendar--get-month-number
621 (substring datestring (match-beginning 2) 648 (substring datestring (match-beginning 2)
622 (match-end 2)))) 649 (match-end 2))))
623 (setq year (read (substring datestring (match-beginning 3) 650 (setq year (read (substring datestring (match-beginning 3)
624 (match-end 3))))) 651 (match-end 3)))))
625 ( ;; date contains month names -- non-european-style 652 ( ;; date contains month names -- non-european-style
626 (and (not european-calendar-style) 653 (string-match (concat "\\s-*"
627 (string-match (concat "\\s-*" 654 "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
628 "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*" 655 "0?\\([123]?[0-9]\\),?[ \t/]\\s-*"
629 "0?\\([123]?[0-9]\\),?[ \t/]\\s-*" 656 "\\([0-9]\\{4\\}\\)")
630 "\\([0-9]\\{4\\}\\)") 657 datestring)
631 datestring))
632 (setq day (read (substring datestring (match-beginning 2) 658 (setq day (read (substring datestring (match-beginning 2)
633 (match-end 2)))) 659 (match-end 2))))
634 (setq month (icalendar--get-month-number 660 (setq month (icalendar--get-month-number
635 (substring datestring (match-beginning 1) 661 (substring datestring (match-beginning 1)
636 (match-end 1)))) 662 (match-end 1))))
702 (let ((result "") 728 (let ((result "")
703 (start 0) 729 (start 0)
704 (entry-main "") 730 (entry-main "")
705 (entry-rest "") 731 (entry-rest "")
706 (header "") 732 (header "")
733 (contents-n-summary)
707 (contents) 734 (contents)
708 (found-error nil) 735 (found-error nil)
709 (nonmarker (concat "^" (regexp-quote diary-nonmarking-symbol) 736 (nonmarker (concat "^" (regexp-quote diary-nonmarking-symbol)
710 "?"))) 737 "?"))
738 (other-elements nil))
711 ;; prepare buffer with error messages 739 ;; prepare buffer with error messages
712 (save-current-buffer 740 (save-current-buffer
713 (set-buffer (get-buffer-create "*icalendar-errors*")) 741 (set-buffer (get-buffer-create "*icalendar-errors*"))
714 (erase-buffer)) 742 (erase-buffer))
715 743
726 (car (current-time)) 754 (car (current-time))
727 (cadr (current-time)) 755 (cadr (current-time))
728 (car (cddr (current-time))))) 756 (car (cddr (current-time)))))
729 (condition-case error-val 757 (condition-case error-val
730 (progn 758 (progn
731 (setq contents 759 (setq contents-n-summary
732 (or 760 (icalendar--convert-to-ical nonmarker entry-main))
733 ;; anniversaries -- %%(diary-anniversary ...) 761 (setq other-elements (icalendar--parse-summary-and-rest
734 (icalendar--convert-anniversary-to-ical nonmarker 762 (concat entry-main entry-rest)))
735 entry-main) 763 (setq contents (concat (car contents-n-summary)
736 ;; cyclic events -- %%(diary-cyclic ...) 764 "\nSUMMARY:" (cadr contents-n-summary)))
737 (icalendar--convert-cyclic-to-ical nonmarker entry-main) 765 (let ((cla (cdr (assoc 'cla other-elements)))
738 ;; diary-date -- %%(diary-date ...) 766 (des (cdr (assoc 'des other-elements)))
739 (icalendar--convert-date-to-ical nonmarker entry-main) 767 (loc (cdr (assoc 'loc other-elements)))
740 ;; float events -- %%(diary-float ...) 768 (org (cdr (assoc 'org other-elements)))
741 (icalendar--convert-float-to-ical nonmarker entry-main) 769 (sta (cdr (assoc 'sta other-elements)))
742 ;; block events -- %%(diary-block ...) 770 (sum (cdr (assoc 'sum other-elements)))
743 (icalendar--convert-block-to-ical nonmarker entry-main) 771 (url (cdr (assoc 'url other-elements))))
744 ;; other sexp diary entries 772 (if cla
745 (icalendar--convert-sexp-to-ical nonmarker entry-main) 773 (setq contents (concat contents "\nCLASS:" cla)))
746 ;; weekly by day -- Monday 8:30 Team meeting 774 (if des
747 (icalendar--convert-weekly-to-ical nonmarker entry-main) 775 (setq contents (concat contents "\nDESCRIPTION:" des)))
748 ;; yearly by day -- 1 May Tag der Arbeit 776 (if loc
749 (icalendar--convert-yearly-to-ical nonmarker entry-main) 777 (setq contents (concat contents "\nLOCATION:" loc)))
750 ;; "ordinary" events, start and end time given 778 (if org
751 ;; 1 Feb 2003 blah 779 (setq contents (concat contents "\nORGANIZER:" org)))
752 (icalendar--convert-ordinary-to-ical nonmarker entry-main) 780 (if sta
753 ;; everything else 781 (setq contents (concat contents "\nSTATUS:" sta)))
754 ;; Oops! what's that? 782 ;;(if sum
755 (error "Could not parse entry"))) 783 ;; (setq contents (concat contents "\nSUMMARY:" sum)))
756 (unless (string= entry-rest "") 784 (if url
757 (setq contents 785 (setq contents (concat contents "\nURL:" url))))
758 (concat contents "\nDESCRIPTION:"
759 (icalendar--convert-string-for-export
760 entry-rest))))
761 (setq result (concat result header contents "\nEND:VEVENT"))) 786 (setq result (concat result header contents "\nEND:VEVENT")))
762 ;; handle errors 787 ;; handle errors
763 (error 788 (error
764 (setq found-error t) 789 (setq found-error t)
765 (save-current-buffer 790 (save-current-buffer
778 (insert "\nPRODID:-//Emacs//NONSGML icalendar.el//EN") 803 (insert "\nPRODID:-//Emacs//NONSGML icalendar.el//EN")
779 (insert "\nVERSION:2.0") 804 (insert "\nVERSION:2.0")
780 (insert result) 805 (insert result)
781 (insert "\nEND:VCALENDAR\n") 806 (insert "\nEND:VCALENDAR\n")
782 ;; save the diary file 807 ;; save the diary file
783 (save-buffer)))) 808 (save-buffer)
809 (unless found-error
810 (bury-buffer)))))
784 found-error)) 811 found-error))
785 812
786 ;; subroutines 813 (defun icalendar--convert-to-ical (nonmarker entry-main)
814 "Convert a diary entry to icalendar format.
815 NONMARKER is a regular expression matching the start of non-marking
816 entries. ENTRY-MAIN is the first line of the diary entry."
817 (or
818 ;; anniversaries -- %%(diary-anniversary ...)
819 (icalendar--convert-anniversary-to-ical nonmarker entry-main)
820 ;; cyclic events -- %%(diary-cyclic ...)
821 (icalendar--convert-cyclic-to-ical nonmarker entry-main)
822 ;; diary-date -- %%(diary-date ...)
823 (icalendar--convert-date-to-ical nonmarker entry-main)
824 ;; float events -- %%(diary-float ...)
825 (icalendar--convert-float-to-ical nonmarker entry-main)
826 ;; block events -- %%(diary-block ...)
827 (icalendar--convert-block-to-ical nonmarker entry-main)
828 ;; other sexp diary entries
829 (icalendar--convert-sexp-to-ical nonmarker entry-main)
830 ;; weekly by day -- Monday 8:30 Team meeting
831 (icalendar--convert-weekly-to-ical nonmarker entry-main)
832 ;; yearly by day -- 1 May Tag der Arbeit
833 (icalendar--convert-yearly-to-ical nonmarker entry-main)
834 ;; "ordinary" events, start and end time given
835 ;; 1 Feb 2003 blah
836 (icalendar--convert-ordinary-to-ical nonmarker entry-main)
837 ;; everything else
838 ;; Oops! what's that?
839 (error "Could not parse entry")))
840
841 (defun icalendar--parse-summary-and-rest (summary-and-rest)
842 "Parse SUMMARY-AND-REST from a diary to fill iCalendar properties."
843 (save-match-data
844 (let* ((s icalendar-import-format)
845 (p-cla (or (string-match "%c" icalendar-import-format) -1))
846 (p-des (or (string-match "%d" icalendar-import-format) -1))
847 (p-loc (or (string-match "%l" icalendar-import-format) -1))
848 (p-org (or (string-match "%o" icalendar-import-format) -1))
849 (p-sum (or (string-match "%s" icalendar-import-format) -1))
850 (p-sta (or (string-match "%t" icalendar-import-format) -1))
851 (p-url (or (string-match "%u" icalendar-import-format) -1))
852 (p-list (sort (list p-cla p-des p-loc p-org p-sta p-sum p-url) '<))
853 pos-cla pos-des pos-loc pos-org pos-sta pos-sum pos-url)
854 (dotimes (i (length p-list))
855 (cond ((and (>= p-cla 0) (= (nth i p-list) p-cla))
856 (setq pos-cla (+ 2 (* 2 i))))
857 ((and (>= p-des 0) (= (nth i p-list) p-des))
858 (setq pos-des (+ 2 (* 2 i))))
859 ((and (>= p-loc 0) (= (nth i p-list) p-loc))
860 (setq pos-loc (+ 2 (* 2 i))))
861 ((and (>= p-org 0) (= (nth i p-list) p-org))
862 (setq pos-org (+ 2 (* 2 i))))
863 ((and (>= p-sta 0) (= (nth i p-list) p-sta))
864 (setq pos-sta (+ 2 (* 2 i))))
865 ((and (>= p-sum 0) (= (nth i p-list) p-sum))
866 (setq pos-sum (+ 2 (* 2 i))))
867 ((and (>= p-url 0) (= (nth i p-list) p-url))
868 (setq pos-url (+ 2 (* 2 i))))))
869 (mapc (lambda (ij)
870 (setq s (icalendar--rris (car ij) (cadr ij) s t t)))
871 (list
872 ;; summary must be first! because of %s
873 (list "%s"
874 (concat "\\(" icalendar-import-format-summary "\\)?"))
875 (list "%c"
876 (concat "\\(" icalendar-import-format-class "\\)?"))
877 (list "%d"
878 (concat "\\(" icalendar-import-format-description "\\)?"))
879 (list "%l"
880 (concat "\\(" icalendar-import-format-location "\\)?"))
881 (list "%o"
882 (concat "\\(" icalendar-import-format-organizer "\\)?"))
883 (list "%t"
884 (concat "\\(" icalendar-import-format-status "\\)?"))
885 (list "%u"
886 (concat "\\(" icalendar-import-format-url "\\)?"))))
887 (setq s (concat (icalendar--rris "%s" "\\(.*\\)" s nil t) " "))
888 (if (string-match s summary-and-rest)
889 (let (cla des loc org sta sum url)
890 (if (and pos-sum (match-beginning pos-sum))
891 (setq sum (substring summary-and-rest
892 (match-beginning pos-sum)
893 (match-end pos-sum))))
894 (if (and pos-cla (match-beginning pos-cla))
895 (setq cla (substring summary-and-rest
896 (match-beginning pos-cla)
897 (match-end pos-cla))))
898 (if (and pos-des (match-beginning pos-des))
899 (setq des (substring summary-and-rest
900 (match-beginning pos-des)
901 (match-end pos-des))))
902 (if (and pos-loc (match-beginning pos-loc))
903 (setq loc (substring summary-and-rest
904 (match-beginning pos-loc)
905 (match-end pos-loc))))
906 (if (and pos-org (match-beginning pos-org))
907 (setq org (substring summary-and-rest
908 (match-beginning pos-org)
909 (match-end pos-org))))
910 (if (and pos-sta (match-beginning pos-sta))
911 (setq sta (substring summary-and-rest
912 (match-beginning pos-sta)
913 (match-end pos-sta))))
914 (if (and pos-url (match-beginning pos-url))
915 (setq url (substring summary-and-rest
916 (match-beginning pos-url)
917 (match-end pos-url))))
918 (list (if cla (cons 'cla cla) nil)
919 (if des (cons 'des des) nil)
920 (if loc (cons 'loc loc) nil)
921 (if org (cons 'org org) nil)
922 (if sta (cons 'sta sta) nil)
923 ;;(if sum (cons 'sum sum) nil)
924 (if url (cons 'url url) nil)))))))
925
926 ;; subroutines for icalendar-export-region
787 (defun icalendar--convert-ordinary-to-ical (nonmarker entry-main) 927 (defun icalendar--convert-ordinary-to-ical (nonmarker entry-main)
788 "Convert \"ordinary\" diary entry to icalendar format. 928 "Convert \"ordinary\" diary entry to icalendar format.
789
790 NONMARKER is a regular expression matching the start of non-marking 929 NONMARKER is a regular expression matching the start of non-marking
791 entries. ENTRY-MAIN is the first line of the diary entry." 930 entries. ENTRY-MAIN is the first line of the diary entry."
792 (if (string-match (concat nonmarker 931 (if (string-match (concat nonmarker
793 "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)\\s-*" 932 "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)\\s-*"
794 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?" 933 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
795 "\\(" 934 "\\("
796 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?" 935 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
797 "\\)?" 936 "\\)?"
798 "\\s-*\\(.*\\)") 937 "\\s-*\\(.*?\\) ?$")
799 entry-main) 938 entry-main)
800 (let* ((datetime (substring entry-main (match-beginning 1) 939 (let* ((datetime (substring entry-main (match-beginning 1)
801 (match-end 1))) 940 (match-end 1)))
802 (startisostring (icalendar--datestring-to-isodate 941 (startisostring (icalendar--datestring-to-isodate
803 datetime)) 942 datetime))
837 (let ((time 976 (let ((time
838 (read (icalendar--rris "^T0?" "" 977 (read (icalendar--rris "^T0?" ""
839 starttimestring)))) 978 starttimestring))))
840 (setq endtimestring (format "T%06d" 979 (setq endtimestring (format "T%06d"
841 (+ 10000 time)))))) 980 (+ 10000 time))))))
842 (concat "\nDTSTART;" 981 (list (concat "\nDTSTART;"
843 (if starttimestring "VALUE=DATE-TIME:" 982 (if starttimestring "VALUE=DATE-TIME:"
844 "VALUE=DATE:") 983 "VALUE=DATE:")
845 startisostring 984 startisostring
846 (or starttimestring "") 985 (or starttimestring "")
847 "\nDTEND;" 986 "\nDTEND;"
848 (if endtimestring "VALUE=DATE-TIME:" 987 (if endtimestring "VALUE=DATE-TIME:"
849 "VALUE=DATE:") 988 "VALUE=DATE:")
850 (if starttimestring 989 (if starttimestring
851 startisostring 990 startisostring
852 endisostring) 991 endisostring)
853 (or endtimestring "") 992 (or endtimestring ""))
854 "\nSUMMARY:" 993 summary))
855 summary))
856 ;; no match 994 ;; no match
857 nil)) 995 nil))
858 996
859 (defun icalendar--convert-weekly-to-ical (nonmarker entry-main) 997 (defun icalendar--convert-weekly-to-ical (nonmarker entry-main)
860 "Convert weekly diary entry to icalendar format. 998 "Convert weekly diary entry to icalendar format.
861
862 NONMARKER is a regular expression matching the start of non-marking 999 NONMARKER is a regular expression matching the start of non-marking
863 entries. ENTRY-MAIN is the first line of the diary entry." 1000 entries. ENTRY-MAIN is the first line of the diary entry."
864 (if (and (string-match (concat nonmarker 1001 (if (and (string-match (concat nonmarker
865 "\\([a-z]+\\)\\s-+" 1002 "\\([a-z]+\\)\\s-+"
866 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)" 1003 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)"
867 "\\([ap]m\\)?" 1004 "\\([ap]m\\)?"
868 "\\(-0?" 1005 "\\(-0?"
869 "\\([1-9][0-9]?:[0-9][0-9]\\)" 1006 "\\([1-9][0-9]?:[0-9][0-9]\\)"
870 "\\([ap]m\\)?\\)?" 1007 "\\([ap]m\\)?\\)?"
871 "\\)?" 1008 "\\)?"
872 "\\s-*\\(.*\\)$") 1009 "\\s-*\\(.*?\\) ?$")
873 entry-main) 1010 entry-main)
874 (icalendar--get-weekday-abbrev 1011 (icalendar--get-weekday-abbrev
875 (substring entry-main (match-beginning 1) 1012 (substring entry-main (match-beginning 1)
876 (match-end 1)))) 1013 (match-end 1))))
877 (let* ((day (icalendar--get-weekday-abbrev 1014 (let* ((day (icalendar--get-weekday-abbrev
909 (let ((time (read 1046 (let ((time (read
910 (icalendar--rris "^T0?" "" 1047 (icalendar--rris "^T0?" ""
911 starttimestring)))) 1048 starttimestring))))
912 (setq endtimestring (format "T%06d" 1049 (setq endtimestring (format "T%06d"
913 (+ 10000 time)))))) 1050 (+ 10000 time))))))
914 (concat "\nDTSTART;" 1051 (list (concat "\nDTSTART;"
915 (if starttimestring 1052 (if starttimestring
916 "VALUE=DATE-TIME:" 1053 "VALUE=DATE-TIME:"
917 "VALUE=DATE:") 1054 "VALUE=DATE:")
918 ;; find the correct week day, 1055 ;; find the correct week day,
919 ;; 1st january 2000 was a saturday 1056 ;; 1st january 2000 was a saturday
920 (format 1057 (format
921 "200001%02d" 1058 "200001%02d"
922 (+ (icalendar--get-weekday-number day) 2)) 1059 (+ (icalendar--get-weekday-number day) 2))
923 (or starttimestring "") 1060 (or starttimestring "")
924 "\nDTEND;" 1061 "\nDTEND;"
925 (if endtimestring 1062 (if endtimestring
926 "VALUE=DATE-TIME:" 1063 "VALUE=DATE-TIME:"
927 "VALUE=DATE:") 1064 "VALUE=DATE:")
928 (format 1065 (format
929 "200001%02d" 1066 "200001%02d"
930 ;; end is non-inclusive! 1067 ;; end is non-inclusive!
931 (+ (icalendar--get-weekday-number day) 1068 (+ (icalendar--get-weekday-number day)
932 (if endtimestring 2 3))) 1069 (if endtimestring 2 3)))
933 (or endtimestring "") 1070 (or endtimestring "")
934 "\nSUMMARY:" summary 1071 "\nRRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY="
935 "\nRRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=" 1072 day)
936 day)) 1073 summary))
937 ;; no match 1074 ;; no match
938 nil)) 1075 nil))
939 1076
940 (defun icalendar--convert-yearly-to-ical (nonmarker entry-main) 1077 (defun icalendar--convert-yearly-to-ical (nonmarker entry-main)
941 "Convert yearly diary entry to icalendar format. 1078 "Convert yearly diary entry to icalendar format.
942
943 NONMARKER is a regular expression matching the start of non-marking 1079 NONMARKER is a regular expression matching the start of non-marking
944 entries. ENTRY-MAIN is the first line of the diary entry." 1080 entries. ENTRY-MAIN is the first line of the diary entry."
945 (if (string-match (concat nonmarker 1081 (if (string-match (concat nonmarker
946 (if european-calendar-style 1082 (if european-calendar-style
947 "0?\\([1-9]+[0-9]?\\)\\s-+\\([a-z]+\\)\\s-+" 1083 "0?\\([1-9]+[0-9]?\\)\\s-+\\([a-z]+\\)\\s-+"
949 "\\*?\\s-*" 1085 "\\*?\\s-*"
950 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?" 1086 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
951 "\\(" 1087 "\\("
952 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?" 1088 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
953 "\\)?" 1089 "\\)?"
954 "\\s-*\\([^0-9]+.*\\)$" ; must not match years 1090 "\\s-*\\([^0-9]+.*?\\) ?$" ; must not match years
955 ) 1091 )
956 entry-main) 1092 entry-main)
957 (let* ((daypos (if european-calendar-style 1 2)) 1093 (let* ((daypos (if european-calendar-style 1 2))
958 (monpos (if european-calendar-style 2 1)) 1094 (monpos (if european-calendar-style 2 1))
959 (day (read (substring entry-main 1095 (day (read (substring entry-main
995 (let ((time (read 1131 (let ((time (read
996 (icalendar--rris "^T0?" "" 1132 (icalendar--rris "^T0?" ""
997 starttimestring)))) 1133 starttimestring))))
998 (setq endtimestring (format "T%06d" 1134 (setq endtimestring (format "T%06d"
999 (+ 10000 time)))))) 1135 (+ 10000 time))))))
1000 (concat "\nDTSTART;" 1136 (list (concat "\nDTSTART;"
1001 (if starttimestring "VALUE=DATE-TIME:" 1137 (if starttimestring "VALUE=DATE-TIME:"
1002 "VALUE=DATE:") 1138 "VALUE=DATE:")
1003 (format "1900%02d%02d" month day) 1139 (format "1900%02d%02d" month day)
1004 (or starttimestring "") 1140 (or starttimestring "")
1005 "\nDTEND;" 1141 "\nDTEND;"
1006 (if endtimestring "VALUE=DATE-TIME:" 1142 (if endtimestring "VALUE=DATE-TIME:"
1007 "VALUE=DATE:") 1143 "VALUE=DATE:")
1008 ;; end is not included! shift by one day 1144 ;; end is not included! shift by one day
1009 (icalendar--date-to-isodate 1145 (icalendar--date-to-isodate
1010 (list month day 1900) 1146 (list month day 1900)
1011 (if endtimestring 0 1)) 1147 (if endtimestring 0 1))
1012 (or endtimestring "") 1148 (or endtimestring "")
1013 "\nSUMMARY:" 1149 "\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH="
1014 summary 1150 (format "%2d" month)
1015 "\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=" 1151 ";BYMONTHDAY="
1016 (format "%2d" month) 1152 (format "%2d" day))
1017 ";BYMONTHDAY=" 1153 summary))
1018 (format "%2d" day)))
1019 ;; no match 1154 ;; no match
1020 nil)) 1155 nil))
1021 1156
1022 (defun icalendar--convert-sexp-to-ical (nonmarker entry-main) 1157 (defun icalendar--convert-sexp-to-ical (nonmarker entry-main)
1023 "Convert complex sexp diary entry to icalendar format -- unsupported! 1158 "Convert complex sexp diary entry to icalendar format -- unsupported!
1024 1159
1025 FIXME! 1160 FIXME!
1026 1161
1027 NONMARKER is a regular expression matching the start of non-marking 1162 NONMARKER is a regular expression matching the start of non-marking
1028 entries. ENTRY-MAIN is the first line of the diary entry." 1163 entries. ENTRY-MAIN is the first line of the diary entry."
1029 (if (string-match (concat nonmarker 1164 (cond ((string-match (concat nonmarker
1030 "%%(\\([^)]+\\))\\s-*\\(.*\\)") 1165 "%%(and \\(([^)]+)\\))\\(\\s-*.*?\\) ?$")
1031 entry-main) 1166 entry-main)
1032 (progn 1167 ;; simple sexp entry as generated by icalendar.el: strip off the
1033 (icalendar--dmsg "diary-sexp %s" entry-main) 1168 ;; unnecessary (and)
1034 (error "Sexp-entries are not supported yet")) 1169 (icalendar--dmsg "diary-sexp from icalendar.el %s" entry-main)
1035 ;; no match 1170 (icalendar--convert-to-ical
1036 nil)) 1171 nonmarker
1172 (concat "%%"
1173 (substring entry-main (match-beginning 1) (match-end 1))
1174 (substring entry-main (match-beginning 2) (match-end 2)))))
1175 ((string-match (concat nonmarker
1176 "%%([^)]+)\\s-*.*")
1177 entry-main)
1178 (icalendar--dmsg "diary-sexp %s" entry-main)
1179 (error "Sexp-entries are not supported yet"))
1180 (t
1181 ;; no match
1182 nil)))
1037 1183
1038 (defun icalendar--convert-block-to-ical (nonmarker entry-main) 1184 (defun icalendar--convert-block-to-ical (nonmarker entry-main)
1039 "Convert block diary entry to icalendar format. 1185 "Convert block diary entry to icalendar format.
1040
1041 NONMARKER is a regular expression matching the start of non-marking 1186 NONMARKER is a regular expression matching the start of non-marking
1042 entries. ENTRY-MAIN is the first line of the diary entry." 1187 entries. ENTRY-MAIN is the first line of the diary entry."
1043 (if (string-match (concat nonmarker 1188 (if (string-match (concat nonmarker
1044 "%%(diary-block \\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)" 1189 "%%(diary-block \\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)"
1045 " +\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*" 1190 " +\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*"
1046 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?" 1191 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
1047 "\\(" 1192 "\\("
1048 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?" 1193 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
1049 "\\)?" 1194 "\\)?"
1050 "\\s-*\\(.*\\)") 1195 "\\s-*\\(.*?\\) ?$")
1051 entry-main) 1196 entry-main)
1052 (let* ((startstring (substring entry-main 1197 (let* ((startstring (substring entry-main
1053 (match-beginning 1) 1198 (match-beginning 1)
1054 (match-end 1))) 1199 (match-end 1)))
1055 (endstring (substring entry-main 1200 (endstring (substring entry-main
1094 starttimestring)))) 1239 starttimestring))))
1095 (setq endtimestring (format "T%06d" 1240 (setq endtimestring (format "T%06d"
1096 (+ 10000 time)))))) 1241 (+ 10000 time))))))
1097 (if starttimestring 1242 (if starttimestring
1098 ;; with time -> write rrule 1243 ;; with time -> write rrule
1099 (concat "\nDTSTART;VALUE=DATE-TIME:" 1244 (list (concat "\nDTSTART;VALUE=DATE-TIME:"
1100 startisostring 1245 startisostring
1101 starttimestring 1246 starttimestring
1102 "\nDTEND;VALUE=DATE-TIME:" 1247 "\nDTEND;VALUE=DATE-TIME:"
1103 startisostring 1248 startisostring
1104 endtimestring 1249 endtimestring
1105 "\nSUMMARY:" 1250 "\nRRULE:FREQ=DAILY;INTERVAL=1;UNTIL="
1106 summary 1251 endisostring)
1107 "\nRRULE:FREQ=DAILY;INTERVAL=1;UNTIL=" 1252 summary)
1108 endisostring)
1109 ;; no time -> write long event 1253 ;; no time -> write long event
1110 (concat "\nDTSTART;VALUE=DATE:" startisostring 1254 (list (concat "\nDTSTART;VALUE=DATE:" startisostring
1111 "\nDTEND;VALUE=DATE:" endisostring+1 1255 "\nDTEND;VALUE=DATE:" endisostring+1)
1112 "\nSUMMARY:" summary))) 1256 summary)))
1113 ;; no match 1257 ;; no match
1114 nil)) 1258 nil))
1115 1259
1116 (defun icalendar--convert-float-to-ical (nonmarker entry-main) 1260 (defun icalendar--convert-float-to-ical (nonmarker entry-main)
1117 "Convert float diary entry to icalendar format -- unsupported! 1261 "Convert float diary entry to icalendar format -- unsupported!
1119 FIXME! 1263 FIXME!
1120 1264
1121 NONMARKER is a regular expression matching the start of non-marking 1265 NONMARKER is a regular expression matching the start of non-marking
1122 entries. ENTRY-MAIN is the first line of the diary entry." 1266 entries. ENTRY-MAIN is the first line of the diary entry."
1123 (if (string-match (concat nonmarker 1267 (if (string-match (concat nonmarker
1124 "%%(diary-float \\([^)]+\\))\\s-*\\(.*\\)") 1268 "%%(diary-float \\([^)]+\\))\\s-*\\(.*?\\) ?$")
1125 entry-main) 1269 entry-main)
1126 (progn 1270 (progn
1127 (icalendar--dmsg "diary-float %s" entry-main) 1271 (icalendar--dmsg "diary-float %s" entry-main)
1128 (error "`diary-float' is not supported yet")) 1272 (error "`diary-float' is not supported yet"))
1129 ;; no match 1273 ;; no match
1135 FIXME! 1279 FIXME!
1136 1280
1137 NONMARKER is a regular expression matching the start of non-marking 1281 NONMARKER is a regular expression matching the start of non-marking
1138 entries. ENTRY-MAIN is the first line of the diary entry." 1282 entries. ENTRY-MAIN is the first line of the diary entry."
1139 (if (string-match (concat nonmarker 1283 (if (string-match (concat nonmarker
1140 "%%(diary-date \\([^)]+\\))\\s-*\\(.*\\)") 1284 "%%(diary-date \\([^)]+\\))\\s-*\\(.*?\\) ?$")
1141 entry-main) 1285 entry-main)
1142 (progn 1286 (progn
1143 (icalendar--dmsg "diary-date %s" entry-main) 1287 (icalendar--dmsg "diary-date %s" entry-main)
1144 (error "`diary-date' is not supported yet")) 1288 (error "`diary-date' is not supported yet"))
1145 ;; no match 1289 ;; no match
1146 nil)) 1290 nil))
1147 1291
1148 (defun icalendar--convert-cyclic-to-ical (nonmarker entry-main) 1292 (defun icalendar--convert-cyclic-to-ical (nonmarker entry-main)
1149 "Convert `diary-cyclic' diary entry to icalendar format. 1293 "Convert `diary-cyclic' diary entry to icalendar format.
1150
1151 NONMARKER is a regular expression matching the start of non-marking 1294 NONMARKER is a regular expression matching the start of non-marking
1152 entries. ENTRY-MAIN is the first line of the diary entry." 1295 entries. ENTRY-MAIN is the first line of the diary entry."
1153 (if (string-match (concat nonmarker 1296 (if (string-match (concat nonmarker
1154 "%%(diary-cyclic \\([^ ]+\\) +" 1297 "%%(diary-cyclic \\([^ ]+\\) +"
1155 "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*" 1298 "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\))\\s-*"
1156 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?" 1299 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
1157 "\\(" 1300 "\\("
1158 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?" 1301 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
1159 "\\)?" 1302 "\\)?"
1160 "\\s-*\\(.*\\)") 1303 "\\s-*\\(.*?\\) ?$")
1161 entry-main) 1304 entry-main)
1162 (let* ((frequency (substring entry-main (match-beginning 1) 1305 (let* ((frequency (substring entry-main (match-beginning 1)
1163 (match-end 1))) 1306 (match-end 1)))
1164 (datetime (substring entry-main (match-beginning 2) 1307 (datetime (substring entry-main (match-beginning 2)
1165 (match-end 2))) 1308 (match-end 2)))
1200 (let ((time 1343 (let ((time
1201 (read (icalendar--rris "^T0?" "" 1344 (read (icalendar--rris "^T0?" ""
1202 starttimestring)))) 1345 starttimestring))))
1203 (setq endtimestring (format "T%06d" 1346 (setq endtimestring (format "T%06d"
1204 (+ 10000 time)))))) 1347 (+ 10000 time))))))
1205 (concat "\nDTSTART;" 1348 (list (concat "\nDTSTART;"
1206 (if starttimestring "VALUE=DATE-TIME:" 1349 (if starttimestring "VALUE=DATE-TIME:"
1207 "VALUE=DATE:") 1350 "VALUE=DATE:")
1208 startisostring 1351 startisostring
1209 (or starttimestring "") 1352 (or starttimestring "")
1210 "\nDTEND;" 1353 "\nDTEND;"
1211 (if endtimestring "VALUE=DATE-TIME:" 1354 (if endtimestring "VALUE=DATE-TIME:"
1212 "VALUE=DATE:") 1355 "VALUE=DATE:")
1213 (if endtimestring endisostring endisostring+1) 1356 (if endtimestring endisostring endisostring+1)
1214 (or endtimestring "") 1357 (or endtimestring "")
1215 "\nSUMMARY:" summary 1358 "\nRRULE:FREQ=DAILY;INTERVAL=" frequency
1216 "\nRRULE:FREQ=DAILY;INTERVAL=" frequency 1359 ;; strange: korganizer does not expect
1217 ;; strange: korganizer does not expect 1360 ;; BYSOMETHING here...
1218 ;; BYSOMETHING here... 1361 )
1219 )) 1362 summary))
1220 ;; no match 1363 ;; no match
1221 nil)) 1364 nil))
1222 1365
1223 (defun icalendar--convert-anniversary-to-ical (nonmarker entry-main) 1366 (defun icalendar--convert-anniversary-to-ical (nonmarker entry-main)
1224 "Convert `diary-anniversary' diary entry to icalendar format. 1367 "Convert `diary-anniversary' diary entry to icalendar format.
1225
1226 NONMARKER is a regular expression matching the start of non-marking 1368 NONMARKER is a regular expression matching the start of non-marking
1227 entries. ENTRY-MAIN is the first line of the diary entry." 1369 entries. ENTRY-MAIN is the first line of the diary entry."
1228 (if (string-match (concat nonmarker 1370 (if (string-match (concat nonmarker
1229 "%%(diary-anniversary \\([^)]+\\))\\s-*" 1371 "%%(diary-anniversary \\([^)]+\\))\\s-*"
1230 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?" 1372 "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
1231 "\\(" 1373 "\\("
1232 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?" 1374 "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
1233 "\\)?" 1375 "\\)?"
1234 "\\s-*\\(.*\\)") 1376 "\\s-*\\(.*?\\) ?$")
1235 entry-main) 1377 entry-main)
1236 (let* ((datetime (substring entry-main (match-beginning 1) 1378 (let* ((datetime (substring entry-main (match-beginning 1)
1237 (match-end 1))) 1379 (match-end 1)))
1238 (startisostring (icalendar--datestring-to-isodate 1380 (startisostring (icalendar--datestring-to-isodate
1239 datetime)) 1381 datetime))
1270 (let ((time 1412 (let ((time
1271 (read (icalendar--rris "^T0?" "" 1413 (read (icalendar--rris "^T0?" ""
1272 starttimestring)))) 1414 starttimestring))))
1273 (setq endtimestring (format "T%06d" 1415 (setq endtimestring (format "T%06d"
1274 (+ 10000 time)))))) 1416 (+ 10000 time))))))
1275 (concat "\nDTSTART;" 1417 (list (concat "\nDTSTART;"
1276 (if starttimestring "VALUE=DATE-TIME:" 1418 (if starttimestring "VALUE=DATE-TIME:"
1277 "VALUE=DATE:") 1419 "VALUE=DATE:")
1278 startisostring 1420 startisostring
1279 (or starttimestring "") 1421 (or starttimestring "")
1280 "\nDTEND;" 1422 "\nDTEND;"
1281 (if endtimestring "VALUE=DATE-TIME:" 1423 (if endtimestring "VALUE=DATE-TIME:"
1282 "VALUE=DATE:") 1424 "VALUE=DATE:")
1283 endisostring 1425 endisostring
1284 (or endtimestring "") 1426 (or endtimestring "")
1285 "\nSUMMARY:" summary 1427 "\nRRULE:FREQ=YEARLY;INTERVAL=1"
1286 "\nRRULE:FREQ=YEARLY;INTERVAL=1" 1428 ;; the following is redundant,
1287 ;; the following is redundant, 1429 ;; but korganizer seems to expect this... ;(
1288 ;; but korganizer seems to expect this... ;( 1430 ;; and evolution doesn't understand it... :(
1289 ;; and evolution doesn't understand it... :( 1431 ;; so... who is wrong?!
1290 ;; so... who is wrong?! 1432 ";BYMONTH="
1291 ";BYMONTH=" 1433 (substring startisostring 4 6)
1292 (substring startisostring 4 6) 1434 ";BYMONTHDAY="
1293 ";BYMONTHDAY=" 1435 (substring startisostring 6 8))
1294 (substring startisostring 6 8))) 1436 summary))
1295 ;; no match 1437 ;; no match
1296 nil)) 1438 nil))
1297 1439
1298 ;; ====================================================================== 1440 ;; ======================================================================
1299 ;; Import -- convert icalendar to emacs-diary 1441 ;; Import -- convert icalendar to emacs-diary
1300 ;; ====================================================================== 1442 ;; ======================================================================
1301 1443
1302 ;;;###autoload 1444 ;;;###autoload
1303 (defun icalendar-import-file (ical-filename diary-filename 1445 (defun icalendar-import-file (ical-filename diary-filename
1304 &optional non-marking) 1446 &optional non-marking)
1305 "Import a iCalendar file and append to a diary file. 1447 "Import an iCalendar file and append to a diary file.
1306 Argument ICAL-FILENAME output iCalendar file. 1448 Argument ICAL-FILENAME output iCalendar file.
1307 Argument DIARY-FILENAME input `diary-file'. 1449 Argument DIARY-FILENAME input `diary-file'.
1308 Optional argument NON-MARKING determines whether events are created as 1450 Optional argument NON-MARKING determines whether events are created as
1309 non-marking or not." 1451 non-marking or not."
1310 (interactive "fImport iCalendar data from file: 1452 (interactive "fImport iCalendar data from file:
1374 1516
1375 (defun icalendar--format-ical-event (event) 1517 (defun icalendar--format-ical-event (event)
1376 "Create a string representation of an iCalendar EVENT." 1518 "Create a string representation of an iCalendar EVENT."
1377 (let ((string icalendar-import-format) 1519 (let ((string icalendar-import-format)
1378 (conversion-list 1520 (conversion-list
1379 '(("%d" DESCRIPTION icalendar-import-format-description) 1521 '(("%c" CLASS icalendar-import-format-class)
1380 ("%s" SUMMARY icalendar-import-format-subject) 1522 ("%d" DESCRIPTION icalendar-import-format-description)
1381 ("%l" LOCATION icalendar-import-format-location) 1523 ("%l" LOCATION icalendar-import-format-location)
1382 ("%o" ORGANIZER icalendar-import-format-organizer)))) 1524 ("%o" ORGANIZER icalendar-import-format-organizer)
1525 ("%s" SUMMARY icalendar-import-format-summary)
1526 ("%t" STATUS icalendar-import-format-status)
1527 ("%u" URL icalendar-import-format-url))))
1383 ;; convert the specifiers in the format string 1528 ;; convert the specifiers in the format string
1384 (mapcar (lambda (i) 1529 (mapcar (lambda (i)
1385 (let* ((spec (car i)) 1530 (let* ((spec (car i))
1386 (prop (cadr i)) 1531 (prop (cadr i))
1387 (format (car (cddr i))) 1532 (format (car (cddr i)))
1390 (when (and contents (> (length contents) 0)) 1535 (when (and contents (> (length contents) 0))
1391 (setq formatted-contents 1536 (setq formatted-contents
1392 (icalendar--rris "%s" 1537 (icalendar--rris "%s"
1393 (icalendar--convert-string-for-import 1538 (icalendar--convert-string-for-import
1394 contents) 1539 contents)
1395 (symbol-value format)))) 1540 (symbol-value format)
1541 t t)))
1396 (setq string (icalendar--rris spec 1542 (setq string (icalendar--rris spec
1397 formatted-contents 1543 formatted-contents
1398 string)))) 1544 string
1545 t t))))
1399 conversion-list) 1546 conversion-list)
1400 string)) 1547 string))
1401 1548
1402 (defun icalendar--convert-ical-to-diary (ical-list diary-file 1549 (defun icalendar--convert-ical-to-diary (ical-list diary-file
1403 &optional do-not-ask 1550 &optional do-not-ask
1404 non-marking) 1551 non-marking)
1405 "Convert an iCalendar file to an Emacs diary file. 1552 "Convert Calendar data to an Emacs diary file.
1406 Import VEVENTS from the iCalendar object ICAL-LIST and saves them to a 1553 Import VEVENTS from the iCalendar object ICAL-LIST and saves them to a
1407 DIARY-FILE. If DO-NOT-ASK is nil the user is asked for each event 1554 DIARY-FILE. If DO-NOT-ASK is nil the user is asked for each event
1408 whether to actually import it. NON-MARKING determines whether diary 1555 whether to actually import it. NON-MARKING determines whether diary
1409 events are created as non-marking. 1556 events are created as non-marking.
1410 This function attempts to return t if something goes wrong. In this 1557 This function attempts to return t if something goes wrong. In this
1430 (dtend-dec (icalendar--decode-isodatetime dtend)) 1577 (dtend-dec (icalendar--decode-isodatetime dtend))
1431 (dtend-1-dec (icalendar--decode-isodatetime dtend -1)) 1578 (dtend-1-dec (icalendar--decode-isodatetime dtend -1))
1432 end-d 1579 end-d
1433 end-1-d 1580 end-1-d
1434 end-t 1581 end-t
1435 (subject (icalendar--convert-string-for-import 1582 (summary (icalendar--convert-string-for-import
1436 (or (icalendar--get-event-property e 'SUMMARY) 1583 (or (icalendar--get-event-property e 'SUMMARY)
1437 "No Subject"))) 1584 "No summary")))
1438 (rrule (icalendar--get-event-property e 'RRULE)) 1585 (rrule (icalendar--get-event-property e 'RRULE))
1439 (rdate (icalendar--get-event-property e 'RDATE)) 1586 (rdate (icalendar--get-event-property e 'RDATE))
1440 (duration (icalendar--get-event-property e 'DURATION))) 1587 (duration (icalendar--get-event-property e 'DURATION)))
1441 (icalendar--dmsg "%s: `%s'" start-d subject) 1588 (icalendar--dmsg "%s: `%s'" start-d summary)
1442 ;; check whether start-time is missing 1589 ;; check whether start-time is missing
1443 (if (and dtstart 1590 (if (and dtstart
1444 (string= 1591 (string=
1445 (cadr (icalendar--get-event-property-attributes 1592 (cadr (icalendar--get-event-property-attributes
1446 e 'DTSTART)) 1593 e 'DTSTART))
1454 dtstart-dec 1601 dtstart-dec
1455 (icalendar--decode-isoduration duration 1602 (icalendar--decode-isoduration duration
1456 t)))) 1603 t))))
1457 (if (and dtend-dec (not (eq dtend-dec dtend-dec-d))) 1604 (if (and dtend-dec (not (eq dtend-dec dtend-dec-d)))
1458 (message "Inconsistent endtime and duration for %s" 1605 (message "Inconsistent endtime and duration for %s"
1459 subject)) 1606 summary))
1460 (setq dtend-dec dtend-dec-d) 1607 (setq dtend-dec dtend-dec-d)
1461 (setq dtend-1-dec dtend-1-dec-d))) 1608 (setq dtend-1-dec dtend-1-dec-d)))
1462 (setq end-d (if dtend-dec 1609 (setq end-d (if dtend-dec
1463 (icalendar--datetime-to-diary-date dtend-dec) 1610 (icalendar--datetime-to-diary-date dtend-dec)
1464 start-d)) 1611 start-d))
1515 (if event-ok 1662 (if event-ok
1516 (progn 1663 (progn
1517 (setq diary-string 1664 (setq diary-string
1518 (concat diary-string " " 1665 (concat diary-string " "
1519 (icalendar--format-ical-event e))) 1666 (icalendar--format-ical-event e)))
1520 (if do-not-ask (setq subject nil)) 1667 (if do-not-ask (setq summary nil))
1521 (icalendar--add-diary-entry diary-string diary-file 1668 (icalendar--add-diary-entry diary-string diary-file
1522 non-marking subject)) 1669 non-marking summary))
1523 ;; event was not ok 1670 ;; event was not ok
1524 (setq found-error t) 1671 (setq found-error t)
1525 (setq error-string 1672 (setq error-string
1526 (format "%s\nCannot handle this event:%s" 1673 (format "%s\nCannot handle this event:%s"
1527 error-string e)))) 1674 error-string e))))
1568 (if until 1715 (if until
1569 (message "Must not have UNTIL and COUNT -- ignoring COUNT element!") 1716 (message "Must not have UNTIL and COUNT -- ignoring COUNT element!")
1570 (let ((until-1 0)) 1717 (let ((until-1 0))
1571 (cond ((string-equal frequency "DAILY") 1718 (cond ((string-equal frequency "DAILY")
1572 (setq until (icalendar--add-decoded-times 1719 (setq until (icalendar--add-decoded-times
1573 dtstart-dec 1720 dtstart-dec
1574 (list 0 0 0 (* (read count) interval) 0 0))) 1721 (list 0 0 0 (* (read count) interval) 0 0)))
1575 (setq until-1 (icalendar--add-decoded-times 1722 (setq until-1 (icalendar--add-decoded-times
1576 dtstart-dec 1723 dtstart-dec
1577 (list 0 0 0 (* (- (read count) 1) interval) 1724 (list 0 0 0 (* (- (read count) 1) interval)
1578 0 0))) 1725 0 0)))
1765 (icalendar--datetime-to-diary-date 1912 (icalendar--datetime-to-diary-date
1766 dtstart-dec "/") 1913 dtstart-dec "/")
1767 start-t)))) 1914 start-t))))
1768 1915
1769 (defun icalendar--add-diary-entry (string diary-file non-marking 1916 (defun icalendar--add-diary-entry (string diary-file non-marking
1770 &optional subject) 1917 &optional summary)
1771 "Add STRING to the diary file DIARY-FILE. 1918 "Add STRING to the diary file DIARY-FILE.
1772 STRING must be a properly formatted valid diary entry. NON-MARKING 1919 STRING must be a properly formatted valid diary entry. NON-MARKING
1773 determines whether diary events are created as non-marking. If 1920 determines whether diary events are created as non-marking. If
1774 SUBJECT is not nil it must be a string that gives the subject of the 1921 SUMMARY is not nil it must be a string that gives the summary of the
1775 entry. In this case the user will be asked whether he wants to insert 1922 entry. In this case the user will be asked whether he wants to insert
1776 the entry." 1923 the entry."
1777 (when (or (not subject) 1924 (when (or (not summary)
1778 (y-or-n-p (format "Add appointment for `%s' to diary? " 1925 (y-or-n-p (format "Add appointment for `%s' to diary? "
1779 subject))) 1926 summary)))
1780 (when subject 1927 (when summary
1781 (setq non-marking 1928 (setq non-marking
1782 (y-or-n-p (format "Make appointment non-marking? ")))) 1929 (y-or-n-p (format "Make appointment non-marking? "))))
1783 (save-window-excursion 1930 (save-window-excursion
1784 (unless diary-file 1931 (unless diary-file
1785 (setq diary-file 1932 (setq diary-file
1786 (read-file-name "Add appointment to this diary file: "))) 1933 (read-file-name "Add appointment to this diary file: ")))
1934 ;; Note: make-diary-entry will add a trailing blank char.... :(
1787 (make-diary-entry string non-marking diary-file)))) 1935 (make-diary-entry string non-marking diary-file))))
1788 1936
1789 (provide 'icalendar) 1937 (provide 'icalendar)
1790 1938
1791 ;; arch-tag: 74fdbe8e-0451-4e38-bb61-4416e822f4fc 1939 ;; arch-tag: 74fdbe8e-0451-4e38-bb61-4416e822f4fc