(diary-mail-entries): Use call-interactively.
[emacs.git] / lisp / calendar / diary-lib.el
blob75a1fc16ac71ec53b80b99eab242164791d5cafd
1 ;;; diary-lib.el --- diary functions
3 ;; Copyright (C) 1989, 1990, 1992, 1993, 1994, 1995 Free Software
4 ;; Foundation, Inc.
6 ;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
7 ;; Keywords: calendar
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
26 ;;; Commentary:
28 ;; This collection of functions implements the diary features as described
29 ;; in calendar.el.
31 ;; Comments, corrections, and improvements should be sent to
32 ;; Edward M. Reingold Department of Computer Science
33 ;; (217) 333-6733 University of Illinois at Urbana-Champaign
34 ;; reingold@cs.uiuc.edu 1304 West Springfield Avenue
35 ;; Urbana, Illinois 61801
37 ;;; Code:
39 (require 'calendar)
41 ;;;###autoload
42 (defun diary (&optional arg)
43 "Generate the diary window for ARG days starting with the current date.
44 If no argument is provided, the number of days of diary entries is governed
45 by the variable `number-of-diary-entries'. This function is suitable for
46 execution in a `.emacs' file."
47 (interactive "P")
48 (let ((d-file (substitute-in-file-name diary-file))
49 (date (calendar-current-date)))
50 (if (and d-file (file-exists-p d-file))
51 (if (file-readable-p d-file)
52 (list-diary-entries
53 date
54 (cond
55 (arg (prefix-numeric-value arg))
56 ((vectorp number-of-diary-entries)
57 (aref number-of-diary-entries (calendar-day-of-week date)))
58 (t number-of-diary-entries)))
59 (error "Your diary file is not readable!"))
60 (error "You don't have a diary file!"))))
62 (defun view-diary-entries (arg)
63 "Prepare and display a buffer with diary entries.
64 Searches the file named in `diary-file' for entries that
65 match ARG days starting with the date indicated by the cursor position
66 in the displayed three-month calendar."
67 (interactive "p")
68 (let ((d-file (substitute-in-file-name diary-file)))
69 (if (and d-file (file-exists-p d-file))
70 (if (file-readable-p d-file)
71 (list-diary-entries (calendar-cursor-to-date t) arg)
72 (error "Diary file is not readable!"))
73 (error "You don't have a diary file!"))))
75 (defun view-other-diary-entries (arg d-file)
76 "Prepare and display buffer of diary entries from an alternative diary file.
77 Prompts for a file name and searches that file for entries that match ARG
78 days starting with the date indicated by the cursor position in the displayed
79 three-month calendar."
80 (interactive
81 (list (cond ((null current-prefix-arg) 1)
82 ((listp current-prefix-arg) (car current-prefix-arg))
83 (t current-prefix-arg))
84 (read-file-name "Enter diary file name: " default-directory nil t)))
85 (let ((diary-file d-file))
86 (view-diary-entries arg)))
88 (autoload 'check-calendar-holidays "holidays"
89 "Check the list of holidays for any that occur on DATE.
90 The value returned is a list of strings of relevant holiday descriptions.
91 The holidays are those in the list `calendar-holidays'.")
93 (autoload 'calendar-holiday-list "holidays"
94 "Form the list of holidays that occur on dates in the calendar window.
95 The holidays are those in the list `calendar-holidays'.")
97 (autoload 'diary-french-date "cal-french"
98 "French calendar equivalent of date diary entry.")
100 (autoload 'diary-mayan-date "cal-mayan"
101 "Mayan calendar equivalent of date diary entry.")
103 (autoload 'diary-iso-date "cal-iso"
104 "ISO calendar equivalent of date diary entry.")
106 (autoload 'diary-julian-date "cal-julian"
107 "Julian calendar equivalent of date diary entry.")
109 (autoload 'diary-astro-day-number "cal-julian"
110 "Astronomical (Julian) day number diary entry.")
112 (autoload 'diary-chinese-date "cal-china"
113 "Chinese calendar equivalent of date diary entry.")
115 (autoload 'diary-islamic-date "cal-islam"
116 "Islamic calendar equivalent of date diary entry.")
118 (autoload 'list-islamic-diary-entries "cal-islam"
119 "Add any Islamic date entries from the diary file to `diary-entries-list'.")
121 (autoload 'mark-islamic-diary-entries "cal-islam"
122 "Mark days in the calendar window that have Islamic date diary entries.")
124 (autoload 'mark-islamic-calendar-date-pattern "cal-islam"
125 "Mark dates in calendar window that conform to Islamic date MONTH/DAY/YEAR.")
127 (autoload 'diary-hebrew-date "cal-hebrew"
128 "Hebrew calendar equivalent of date diary entry.")
130 (autoload 'diary-omer "cal-hebrew"
131 "Omer count diary entry.")
133 (autoload 'diary-yahrzeit "cal-hebrew"
134 "Yahrzeit diary entry--entry applies if date is yahrzeit or the day before.")
136 (autoload 'diary-parasha "cal-hebrew"
137 "Parasha diary entry--entry applies if date is a Saturday.")
139 (autoload 'diary-rosh-hodesh "cal-hebrew"
140 "Rosh Hodesh diary entry.")
142 (autoload 'list-hebrew-diary-entries "cal-hebrew"
143 "Add any Hebrew date entries from the diary file to `diary-entries-list'.")
145 (autoload 'mark-hebrew-diary-entries "cal-hebrew"
146 "Mark days in the calendar window that have Hebrew date diary entries.")
148 (autoload 'mark-hebrew-calendar-date-pattern "cal-hebrew"
149 "Mark dates in calendar window that conform to Hebrew date MONTH/DAY/YEAR.")
151 (autoload 'diary-coptic-date "cal-coptic"
152 "Coptic calendar equivalent of date diary entry.")
154 (autoload 'diary-ethiopic-date "cal-coptic"
155 "Ethiopic calendar equivalent of date diary entry.")
157 (autoload 'diary-persian-date "cal-persia"
158 "Persian calendar equivalent of date diary entry.")
160 (autoload 'diary-phases-of-moon "lunar" "Moon phases diary entry.")
162 (autoload 'diary-sunrise-sunset "solar"
163 "Local time of sunrise and sunset as a diary entry.")
165 (autoload 'diary-sabbath-candles "solar"
166 "Local time of candle lighting diary entry--applies if date is a Friday.
167 No diary entry if there is no sunset on that date.")
169 (defvar diary-syntax-table (copy-syntax-table (standard-syntax-table))
170 "The syntax table used when parsing dates in the diary file.
171 It is the standard syntax table used in Fundamental mode, but with the
172 syntax of `*' changed to be a word constituent.")
174 (modify-syntax-entry ?* "w" diary-syntax-table)
175 (modify-syntax-entry ?: "w" diary-syntax-table)
177 (defvar diary-modified)
178 (defvar diary-entries-list)
179 (defvar displayed-year)
180 (defvar displayed-month)
181 (defvar entry)
182 (defvar date)
183 (defvar number)
184 (defvar date-string)
185 (defvar d-file)
186 (defvar original-date)
188 (defun diary-attrtype-convert (attrvalue type)
189 "Convert the attrvalue from a string to the appropriate type for using
190 in a face description"
191 (let (ret)
192 (setq ret (cond ((eq type 'string) attrvalue)
193 ((eq type 'symbol) (read attrvalue))
194 ((eq type 'int) (string-to-int attrvalue))
195 ((eq type 'stringtnil)
196 (cond ((string= "t" attrvalue) t)
197 ((string= "nil" attrvalue) nil)
198 (t attrvalue)))
199 ((eq type 'tnil)
200 (cond ((string= "t" attrvalue) t)
201 ((string= "nil" attrvalue) nil)))))
202 ; (message "(%s)[%s]=[%s]" (print type) attrvalue ret)
203 ret))
206 (defun diary-pull-attrs (entry fileglobattrs)
207 "Pull the face-related attributes off the entry, merge with the
208 fileglobattrs, and return the (possibly modified) entry and face
209 data in a list of attrname attrvalue values.
210 The entry will be modified to drop all tags that are used for face matching.
211 If entry is nil, then the fileglobattrs are being searched for,
212 the fileglobattrs variable is ignored, and
213 diary-glob-file-regexp-prefix is prepended to the regexps before each
214 search."
215 (save-excursion
216 (let (regexp regnum attrname attr-list attrname attrvalue type
217 ret-attr attr)
218 (if (null entry)
219 (progn
220 (setq ret-attr '()
221 attr-list diary-face-attrs)
222 (while attr-list
223 (goto-char (point-min))
224 (setq attr (car attr-list)
225 regexp (nth 0 attr)
226 regnum (nth 1 attr)
227 attrname (nth 2 attr)
228 type (nth 3 attr)
229 regexp (concat diary-glob-file-regexp-prefix regexp))
230 (setq attrvalue nil)
231 (if (re-search-forward regexp (point-max) t)
232 (setq attrvalue (buffer-substring-no-properties
233 (match-beginning regnum)
234 (match-end regnum))))
235 (if (and attrvalue
236 (setq attrvalue (diary-attrtype-convert attrvalue type)))
237 (setq ret-attr (append ret-attr (list attrname attrvalue))))
238 (setq attr-list (cdr attr-list)))
239 (setq fileglobattrs ret-attr))
240 (progn
241 (setq ret-attr fileglobattrs
242 attr-list diary-face-attrs)
243 (while attr-list
244 (goto-char (point-min))
245 (setq attr (car attr-list)
246 regexp (nth 0 attr)
247 regnum (nth 1 attr)
248 attrname (nth 2 attr)
249 type (nth 3 attr))
250 (setq attrvalue nil)
251 (if (string-match regexp entry)
252 (progn
253 (setq attrvalue (substring-no-properties entry
254 (match-beginning regnum)
255 (match-end regnum)))
256 (setq entry (replace-match "" t t entry))))
257 (if (and attrvalue
258 (setq attrvalue (diary-attrtype-convert attrvalue type)))
259 (setq ret-attr (append ret-attr (list attrname attrvalue))))
260 (setq attr-list (cdr attr-list)))))
261 (list entry ret-attr))))
265 (defun list-diary-entries (date number)
266 "Create and display a buffer containing the relevant lines in diary-file.
267 The arguments are DATE and NUMBER; the entries selected are those
268 for NUMBER days starting with date DATE. The other entries are hidden
269 using selective display.
271 Returns a list of all relevant diary entries found, if any, in order by date.
272 The list entries have the form ((month day year) string specifier) where
273 \(month day year) is the date of the entry, string is the entry text, and
274 specifier is the applicability. If the variable `diary-list-include-blanks'
275 is t, this list includes a dummy diary entry consisting of the empty string)
276 for a date with no diary entries.
278 After the list is prepared, the hooks `nongregorian-diary-listing-hook',
279 `list-diary-entries-hook', `diary-display-hook', and `diary-hook' are run.
280 These hooks have the following distinct roles:
282 `nongregorian-diary-listing-hook' can cull dates from the diary
283 and each included file. Usually used for Hebrew or Islamic
284 diary entries in files. Applied to *each* file.
286 `list-diary-entries-hook' adds or manipulates diary entries from
287 external sources. Used, for example, to include diary entries
288 from other files or to sort the diary entries. Invoked *once* only,
289 before the display hook is run.
291 `diary-display-hook' does the actual display of information. If this is
292 nil, simple-diary-display will be used. Use add-hook to set this to
293 fancy-diary-display, if desired. If you want no diary display, use
294 add-hook to set this to ignore.
296 `diary-hook' is run last. This can be used for an appointment
297 notification function."
299 (if (< 0 number)
300 (let* ((original-date date);; save for possible use in the hooks
301 old-diary-syntax-table
302 diary-entries-list
303 file-glob-attrs
304 (date-string (calendar-date-string date))
305 (d-file (substitute-in-file-name diary-file)))
306 (message "Preparing diary...")
307 (save-excursion
308 (let ((diary-buffer (find-buffer-visiting d-file)))
309 (if (not diary-buffer)
310 (set-buffer (find-file-noselect d-file t))
311 (set-buffer diary-buffer)
312 (or (verify-visited-file-modtime diary-buffer)
313 (revert-buffer t t))))
314 (setq file-glob-attrs (nth 1 (diary-pull-attrs nil "")))
315 (setq selective-display t)
316 (setq selective-display-ellipses nil)
317 (setq old-diary-syntax-table (syntax-table))
318 (set-syntax-table diary-syntax-table)
319 (unwind-protect
320 (let ((buffer-read-only nil)
321 (diary-modified (buffer-modified-p))
322 (mark (regexp-quote diary-nonmarking-symbol)))
323 ;; First and last characters must be ^M or \n for
324 ;; selective display to work properly
325 (goto-char (1- (point-max)))
326 (if (not (looking-at "\^M\\|\n"))
327 (progn
328 (goto-char (point-max))
329 (insert "\^M")))
330 (goto-char (point-min))
331 (if (not (looking-at "\^M\\|\n"))
332 (insert "\^M"))
333 (subst-char-in-region (point-min) (point-max) ?\n ?\^M t)
334 (calendar-for-loop i from 1 to number do
335 (let ((d diary-date-forms)
336 (month (extract-calendar-month date))
337 (day (extract-calendar-day date))
338 (year (extract-calendar-year date))
339 (entry-found (list-sexp-diary-entries date)))
340 (while d
341 (let*
342 ((date-form (if (equal (car (car d)) 'backup)
343 (cdr (car d))
344 (car d)))
345 (backup (equal (car (car d)) 'backup))
346 (dayname
347 (concat
348 (calendar-day-name date) "\\|"
349 (substring (calendar-day-name date) 0 3) ".?"))
350 (monthname
351 (concat
352 "\\*\\|"
353 (calendar-month-name month) "\\|"
354 (substring (calendar-month-name month) 0 3) ".?"))
355 (month (concat "\\*\\|0*" (int-to-string month)))
356 (day (concat "\\*\\|0*" (int-to-string day)))
357 (year
358 (concat
359 "\\*\\|0*" (int-to-string year)
360 (if abbreviated-calendar-year
361 (concat "\\|" (format "%02d" (% year 100)))
362 "")))
363 (regexp
364 (concat
365 "\\(\\`\\|\^M\\|\n\\)" mark "?\\("
366 (mapconcat 'eval date-form "\\)\\(")
367 "\\)"))
368 (case-fold-search t))
369 (goto-char (point-min))
370 (while (re-search-forward regexp nil t)
371 (if backup (re-search-backward "\\<" nil t))
372 (if (and (or (char-equal (preceding-char) ?\^M)
373 (char-equal (preceding-char) ?\n))
374 (not (looking-at " \\|\^I")))
375 ;; Diary entry that consists only of date.
376 (backward-char 1)
377 ;; Found a nonempty diary entry--make it visible and
378 ;; add it to the list.
379 (setq entry-found t)
380 (let ((entry-start (point))
381 date-start temp)
382 (re-search-backward "\^M\\|\n\\|\\`")
383 (setq date-start (point))
384 (re-search-forward "\^M\\|\n" nil t 2)
385 (while (looking-at " \\|\^I")
386 (re-search-forward "\^M\\|\n" nil t))
387 (backward-char 1)
388 (subst-char-in-region date-start
389 (point) ?\^M ?\n t)
390 (setq entry (buffer-substring entry-start (point))
391 temp (diary-pull-attrs entry file-glob-attrs)
392 entry (nth 0 temp))
393 (add-to-diary-list
394 date
395 entry
396 (buffer-substring
397 (1+ date-start) (1- entry-start))
398 (copy-marker entry-start) (nth 1 temp))))))
399 (setq d (cdr d)))
400 (or entry-found
401 (not diary-list-include-blanks)
402 (setq diary-entries-list
403 (append diary-entries-list
404 (list (list date "" "" "" "")))))
405 (setq date
406 (calendar-gregorian-from-absolute
407 (1+ (calendar-absolute-from-gregorian date))))
408 (setq entry-found nil)))
409 (set-buffer-modified-p diary-modified))
410 (set-syntax-table old-diary-syntax-table))
411 (goto-char (point-min))
412 (run-hooks 'nongregorian-diary-listing-hook
413 'list-diary-entries-hook)
414 (if diary-display-hook
415 (run-hooks 'diary-display-hook)
416 (simple-diary-display))
417 (run-hooks 'diary-hook)
418 diary-entries-list))))
420 (defun include-other-diary-files ()
421 "Include the diary entries from other diary files with those of diary-file.
422 This function is suitable for use in `list-diary-entries-hook';
423 it enables you to use shared diary files together with your own.
424 The files included are specified in the diaryfile by lines of this form:
425 #include \"filename\"
426 This is recursive; that is, #include directives in diary files thus included
427 are obeyed. You can change the `#include' to some other string by
428 changing the variable `diary-include-string'."
429 (goto-char (point-min))
430 (while (re-search-forward
431 (concat
432 "\\(\\`\\|\^M\\|\n\\)"
433 (regexp-quote diary-include-string)
434 " \"\\([^\"]*\\)\"")
435 nil t)
436 (let* ((diary-file (substitute-in-file-name
437 (buffer-substring-no-properties
438 (match-beginning 2) (match-end 2))))
439 (diary-list-include-blanks nil)
440 (list-diary-entries-hook 'include-other-diary-files)
441 (diary-display-hook 'ignore)
442 (diary-hook nil)
443 (d-buffer (find-buffer-visiting diary-file))
444 (diary-modified (if d-buffer
445 (save-excursion
446 (set-buffer d-buffer)
447 (buffer-modified-p)))))
448 (if (file-exists-p diary-file)
449 (if (file-readable-p diary-file)
450 (unwind-protect
451 (setq diary-entries-list
452 (append diary-entries-list
453 (list-diary-entries original-date number)))
454 (save-excursion
455 (set-buffer (find-buffer-visiting diary-file))
456 (let ((inhibit-read-only t))
457 (subst-char-in-region (point-min) (point-max) ?\^M ?\n t))
458 (setq selective-display nil)
459 (set-buffer-modified-p diary-modified)))
460 (beep)
461 (message "Can't read included diary file %s" diary-file)
462 (sleep-for 2))
463 (beep)
464 (message "Can't find included diary file %s" diary-file)
465 (sleep-for 2))))
466 (goto-char (point-min)))
468 (defun simple-diary-display ()
469 "Display the diary buffer if there are any relevant entries or holidays."
470 (let* ((holiday-list (if holidays-in-diary-buffer
471 (check-calendar-holidays original-date)))
472 (msg (format "No diary entries for %s %s"
473 (concat date-string (if holiday-list ":" ""))
474 (mapconcat 'identity holiday-list "; "))))
475 (calendar-set-mode-line
476 (concat "Diary for " date-string
477 (if holiday-list ": " "")
478 (mapconcat 'identity holiday-list "; ")))
479 (if (or (not diary-entries-list)
480 (and (not (cdr diary-entries-list))
481 (string-equal (car (cdr (car diary-entries-list))) "")))
482 (if (<= (length msg) (frame-width))
483 (message "%s" msg)
484 (set-buffer (get-buffer-create holiday-buffer))
485 (setq buffer-read-only nil)
486 (calendar-set-mode-line date-string)
487 (erase-buffer)
488 (insert (mapconcat 'identity holiday-list "\n"))
489 (goto-char (point-min))
490 (set-buffer-modified-p nil)
491 (setq buffer-read-only t)
492 (display-buffer holiday-buffer)
493 (message "No diary entries for %s" date-string))
494 (display-buffer (find-buffer-visiting d-file))
495 (message "Preparing diary...done"))))
497 (defface diary-button-face '((((type pc) (class color))
498 (:foreground "lightblue")))
499 "Default face used for buttons."
500 :version "21.4"
501 :group 'diary)
503 (define-button-type 'diary-entry
504 'action #'diary-goto-entry
505 'face #'diary-button-face)
507 (defun diary-goto-entry (button)
508 (let ((marker (button-get button 'marker)))
509 (when marker
510 (pop-to-buffer (marker-buffer marker))
511 (goto-char (marker-position marker)))))
513 (defun fancy-diary-display ()
514 "Prepare a diary buffer with relevant entries in a fancy, noneditable form.
515 This function is provided for optional use as the `diary-display-hook'."
516 (save-excursion;; Turn off selective-display in the diary file's buffer.
517 (set-buffer (find-buffer-visiting (substitute-in-file-name diary-file)))
518 (let ((diary-modified (buffer-modified-p)))
519 (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)
520 (setq selective-display nil)
521 (kill-local-variable 'mode-line-format)
522 (set-buffer-modified-p diary-modified)))
523 (if (or (not diary-entries-list)
524 (and (not (cdr diary-entries-list))
525 (string-equal (car (cdr (car diary-entries-list))) "")))
526 (let* ((holiday-list (if holidays-in-diary-buffer
527 (check-calendar-holidays original-date)))
528 (msg (format "No diary entries for %s %s"
529 (concat date-string (if holiday-list ":" ""))
530 (mapconcat 'identity holiday-list "; "))))
531 (if (<= (length msg) (frame-width))
532 (message "%s" msg)
533 (set-buffer (get-buffer-create holiday-buffer))
534 (setq buffer-read-only nil)
535 (calendar-set-mode-line date-string)
536 (erase-buffer)
537 (insert (mapconcat 'identity holiday-list "\n"))
538 (goto-char (point-min))
539 (set-buffer-modified-p nil)
540 (setq buffer-read-only t)
541 (display-buffer holiday-buffer)
542 (message "No diary entries for %s" date-string)))
543 (save-excursion;; Prepare the fancy diary buffer.
544 (set-buffer (make-fancy-diary-buffer))
545 (setq buffer-read-only nil)
546 (let ((entry-list diary-entries-list)
547 (holiday-list)
548 (holiday-list-last-month 1)
549 (holiday-list-last-year 1)
550 (date (list 0 0 0)))
551 (while entry-list
552 (if (not (calendar-date-equal date (car (car entry-list))))
553 (progn
554 (setq date (car (car entry-list)))
555 (and holidays-in-diary-buffer
556 (calendar-date-compare
557 (list (list holiday-list-last-month
558 (calendar-last-day-of-month
559 holiday-list-last-month
560 holiday-list-last-year)
561 holiday-list-last-year))
562 (list date))
563 ;; We need to get the holidays for the next 3 months.
564 (setq holiday-list-last-month
565 (extract-calendar-month date))
566 (setq holiday-list-last-year
567 (extract-calendar-year date))
568 (increment-calendar-month
569 holiday-list-last-month holiday-list-last-year 1)
570 (setq holiday-list
571 (let ((displayed-month holiday-list-last-month)
572 (displayed-year holiday-list-last-year))
573 (calendar-holiday-list)))
574 (increment-calendar-month
575 holiday-list-last-month holiday-list-last-year 1))
576 (let* ((date-string (calendar-date-string date))
577 (date-holiday-list
578 (let ((h holiday-list)
579 (d))
580 ;; Make a list of all holidays for date.
581 (while h
582 (if (calendar-date-equal date (car (car h)))
583 (setq d (append d (cdr (car h)))))
584 (setq h (cdr h)))
585 d)))
586 (insert (if (= (point) (point-min)) "" ?\n) date-string)
587 (if date-holiday-list (insert ": "))
588 (let* ((l (current-column))
589 (longest 0))
590 (insert (mapconcat (lambda (x)
591 (if (< longest (length x))
592 (setq longest (length x)))
594 date-holiday-list
595 (concat "\n" (make-string l ? ))))
596 (insert ?\n (make-string (+ l longest) ?=) ?\n)))))
598 (setq entry (car (cdr (car entry-list))))
599 (if (< 0 (length entry))
600 (progn
601 (if (nth 3 (car entry-list))
602 (insert-button (concat entry "\n")
603 'marker (nth 3 (car entry-list))
604 :type 'diary-entry)
605 (insert entry ?\n))
606 (save-excursion
607 (let* ((marks (nth 4 (car entry-list)))
608 (temp-face (make-symbol
609 (apply
610 'concat "temp-face-"
611 (mapcar '(lambda (sym)
612 (if (stringp sym)
614 (symbol-name sym)))
615 marks))))
616 faceinfo)
617 ;; Remove :face info from the marks,
618 ;; copy the face info into temp-face
619 (setq faceinfo marks)
620 (while (setq faceinfo (memq :face faceinfo))
621 (copy-face (read (nth 1 faceinfo)) temp-face)
622 (setcar faceinfo nil)
623 (setcar (cdr faceinfo) nil))
624 (setq marks (delq nil marks))
625 ;; Apply the font aspects
626 (apply 'set-face-attribute temp-face nil marks)
627 (search-backward entry)
628 (overlay-put
629 (make-overlay (match-beginning 0) (match-end 0))
630 'face temp-face)))))
631 (setq entry-list (cdr entry-list))))
632 (set-buffer-modified-p nil)
633 (goto-char (point-min))
634 (setq buffer-read-only t)
635 (display-buffer fancy-diary-buffer)
636 (fancy-diary-display-mode)
637 (message "Preparing diary...done"))))
639 (defun make-fancy-diary-buffer ()
640 "Create and return the initial fancy diary buffer."
641 (save-excursion
642 (set-buffer (get-buffer-create fancy-diary-buffer))
643 (setq buffer-read-only nil)
644 (make-local-variable 'mode-line-format)
645 (calendar-set-mode-line "Diary Entries")
646 (erase-buffer)
647 (set-buffer-modified-p nil)
648 (setq buffer-read-only t)
649 (get-buffer fancy-diary-buffer)))
651 (defun print-diary-entries ()
652 "Print a hard copy of the diary display.
654 If the simple diary display is being used, prepare a temp buffer with the
655 visible lines of the diary buffer, add a heading line composed from the mode
656 line, print the temp buffer, and destroy it.
658 If the fancy diary display is being used, just print the buffer.
660 The hooks given by the variable `print-diary-entries-hook' are called to do
661 the actual printing."
662 (interactive)
663 (if (bufferp (get-buffer fancy-diary-buffer))
664 (save-excursion
665 (set-buffer (get-buffer fancy-diary-buffer))
666 (run-hooks 'print-diary-entries-hook))
667 (let ((diary-buffer
668 (find-buffer-visiting (substitute-in-file-name diary-file))))
669 (if diary-buffer
670 (let ((temp-buffer (get-buffer-create "*Printable Diary Entries*"))
671 (heading))
672 (save-excursion
673 (set-buffer diary-buffer)
674 (setq heading
675 (if (not (stringp mode-line-format))
676 "All Diary Entries"
677 (string-match "^-*\\([^-].*[^-]\\)-*$" mode-line-format)
678 (substring mode-line-format
679 (match-beginning 1) (match-end 1))))
680 (copy-to-buffer temp-buffer (point-min) (point-max))
681 (set-buffer temp-buffer)
682 (while (re-search-forward "\^M.*$" nil t)
683 (replace-match ""))
684 (goto-char (point-min))
685 (insert heading "\n"
686 (make-string (length heading) ?=) "\n")
687 (run-hooks 'print-diary-entries-hook)
688 (kill-buffer temp-buffer)))
689 (error "You don't have a diary buffer!")))))
691 (defun show-all-diary-entries ()
692 "Show all of the diary entries in the diary file.
693 This function gets rid of the selective display of the diary file so that
694 all entries, not just some, are visible. If there is no diary buffer, one
695 is created."
696 (interactive)
697 (let ((d-file (substitute-in-file-name diary-file)))
698 (if (and d-file (file-exists-p d-file))
699 (if (file-readable-p d-file)
700 (save-excursion
701 (let ((diary-buffer (find-buffer-visiting d-file)))
702 (set-buffer (if diary-buffer
703 diary-buffer
704 (find-file-noselect d-file t)))
705 (let ((buffer-read-only nil)
706 (diary-modified (buffer-modified-p)))
707 (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)
708 (setq selective-display nil)
709 (make-local-variable 'mode-line-format)
710 (setq mode-line-format default-mode-line-format)
711 (display-buffer (current-buffer))
712 (set-buffer-modified-p diary-modified))))
713 (error "Your diary file is not readable!"))
714 (error "You don't have a diary file!"))))
718 (defcustom diary-mail-addr
719 (if (boundp 'user-mail-address) user-mail-address nil)
720 "*Email address that `diary-mail-entries' will send email to."
721 :group 'diary
722 :type '(choice string (const nil))
723 :version "20.3")
725 (defcustom diary-mail-days 7
726 "*Number of days for `diary-mail-entries' to check."
727 :group 'diary
728 :type 'integer
729 :version "20.3")
731 ;;;###autoload
732 (defun diary-mail-entries (&optional ndays)
733 "Send a mail message showing diary entries for next NDAYS days.
734 If no prefix argument is given, NDAYS is set to `diary-mail-days'.
736 You can call `diary-mail-entries' every night using an at/cron job.
737 For example, this script will run the program at 2am daily. Since
738 `emacs -batch' does not load your `.emacs' file, you must ensure that
739 all relevant variables are set, as done here.
741 #!/bin/sh
742 # diary-rem.sh -- repeatedly run the Emacs diary-reminder
743 emacs -batch \\
744 -eval \"(setq diary-mail-days 3 \\
745 european-calendar-style t \\
746 diary-mail-addr \\\"user@host.name\\\" )\" \\
747 -l diary-lib -f diary-mail-entries
748 at -f diary-rem.sh 0200 tomorrow
750 You may have to tweak the syntax of the `at' command to suit your
751 system. Alternatively, you can specify a cron entry:
752 0 1 * * * diary-rem.sh
753 to run it every morning at 1am."
754 (interactive "P")
755 (let ((diary-display-hook 'fancy-diary-display))
756 (list-diary-entries (calendar-current-date) (or ndays diary-mail-days)))
757 (compose-mail diary-mail-addr
758 (concat "Diary entries generated "
759 (calendar-date-string (calendar-current-date))))
760 (insert
761 (if (get-buffer fancy-diary-buffer)
762 (save-excursion
763 (set-buffer fancy-diary-buffer)
764 (buffer-substring (point-min) (point-max)))
765 "No entries found"))
766 (call-interactively (get mail-user-agent 'sendfunc)))
769 (defun diary-name-pattern (string-array &optional fullname)
770 "Convert a STRING-ARRAY, an array of strings to a pattern.
771 The pattern will match any of the strings, either entirely or abbreviated
772 to three characters. An abbreviated form will match with or without a period;
773 If the optional FULLNAME is t, abbreviations will not match, just the full
774 name."
775 (let ((pattern ""))
776 (calendar-for-loop i from 0 to (1- (length string-array)) do
777 (setq pattern
778 (concat
779 pattern
780 (if (string-equal pattern "") "" "\\|")
781 (aref string-array i)
782 (if fullname
784 (concat
785 "\\|"
786 (substring (aref string-array i) 0 3) ".?")))))
787 pattern))
789 (defvar marking-diary-entries nil
790 "True during the marking of diary entries, nil otherwise.")
792 (defvar marking-diary-entry nil
793 "True during the marking of diary entries, if current entry is marking.")
795 (defun mark-diary-entries ()
796 "Mark days in the calendar window that have diary entries.
797 Each entry in the diary file visible in the calendar window is marked.
798 After the entries are marked, the hooks `nongregorian-diary-marking-hook' and
799 `mark-diary-entries-hook' are run."
800 (interactive)
801 (setq mark-diary-entries-in-calendar t)
802 (let (file-glob-attrs
803 marks
804 (d-file (substitute-in-file-name diary-file))
805 (marking-diary-entries t))
806 (if (and d-file (file-exists-p d-file))
807 (if (file-readable-p d-file)
808 (save-excursion
809 (message "Marking diary entries...")
810 (set-buffer (find-file-noselect d-file t))
811 (setq file-glob-attrs (nth 1 (diary-pull-attrs nil '())))
812 (let ((d diary-date-forms)
813 (old-diary-syntax-table (syntax-table))
814 temp)
815 (set-syntax-table diary-syntax-table)
816 (while d
817 (let*
818 ((date-form (if (equal (car (car d)) 'backup)
819 (cdr (car d))
820 (car d)));; ignore 'backup directive
821 (dayname (diary-name-pattern calendar-day-name-array))
822 (monthname
823 (concat
824 (diary-name-pattern calendar-month-name-array)
825 "\\|\\*"))
826 (month "[0-9]+\\|\\*")
827 (day "[0-9]+\\|\\*")
828 (year "[0-9]+\\|\\*")
829 (l (length date-form))
830 (d-name-pos (- l (length (memq 'dayname date-form))))
831 (d-name-pos (if (/= l d-name-pos) (+ 2 d-name-pos)))
832 (m-name-pos (- l (length (memq 'monthname date-form))))
833 (m-name-pos (if (/= l m-name-pos) (+ 2 m-name-pos)))
834 (d-pos (- l (length (memq 'day date-form))))
835 (d-pos (if (/= l d-pos) (+ 2 d-pos)))
836 (m-pos (- l (length (memq 'month date-form))))
837 (m-pos (if (/= l m-pos) (+ 2 m-pos)))
838 (y-pos (- l (length (memq 'year date-form))))
839 (y-pos (if (/= l y-pos) (+ 2 y-pos)))
840 (regexp
841 (concat
842 "\\(\\`\\|\^M\\|\n\\)\\("
843 (mapconcat 'eval date-form "\\)\\(")
844 "\\)"))
845 (case-fold-search t))
846 (goto-char (point-min))
847 (while (re-search-forward regexp nil t)
848 (let* ((dd-name
849 (if d-name-pos
850 (buffer-substring-no-properties
851 (match-beginning d-name-pos)
852 (match-end d-name-pos))))
853 (mm-name
854 (if m-name-pos
855 (buffer-substring-no-properties
856 (match-beginning m-name-pos)
857 (match-end m-name-pos))))
858 (mm (string-to-int
859 (if m-pos
860 (buffer-substring-no-properties
861 (match-beginning m-pos)
862 (match-end m-pos))
863 "")))
864 (dd (string-to-int
865 (if d-pos
866 (buffer-substring-no-properties
867 (match-beginning d-pos)
868 (match-end d-pos))
869 "")))
870 (y-str (if y-pos
871 (buffer-substring-no-properties
872 (match-beginning y-pos)
873 (match-end y-pos))))
874 (yy (if (not y-str)
876 (if (and (= (length y-str) 2)
877 abbreviated-calendar-year)
878 (let* ((current-y
879 (extract-calendar-year
880 (calendar-current-date)))
881 (y (+ (string-to-int y-str)
882 (* 100
883 (/ current-y 100)))))
884 (if (> (- y current-y) 50)
885 (- y 100)
886 (if (> (- current-y y) 50)
887 (+ y 100)
888 y)))
889 (string-to-int y-str))))
890 (save-excursion
891 (setq entry (buffer-substring-no-properties (point) (line-end-position))
892 temp (diary-pull-attrs entry file-glob-attrs)
893 entry (nth 0 temp)
894 marks (nth 1 temp))))
895 (if dd-name
896 (mark-calendar-days-named
897 (cdr (assoc-ignore-case
898 (substring dd-name 0 3)
899 (calendar-make-alist
900 calendar-day-name-array
902 (lambda (x) (substring x 0 3))))) marks)
903 (if mm-name
904 (if (string-equal mm-name "*")
905 (setq mm 0)
906 (setq mm
907 (cdr (assoc-ignore-case
908 (substring mm-name 0 3)
909 (calendar-make-alist
910 calendar-month-name-array
912 (lambda (x) (substring x 0 3)))
913 )))))
914 (mark-calendar-date-pattern mm dd yy marks))))
915 (setq d (cdr d))))
916 (mark-sexp-diary-entries)
917 (run-hooks 'nongregorian-diary-marking-hook
918 'mark-diary-entries-hook)
919 (set-syntax-table old-diary-syntax-table)
920 (message "Marking diary entries...done")))
921 (error "Your diary file is not readable!"))
922 (error "You don't have a diary file!"))))
924 (defun mark-sexp-diary-entries ()
925 "Mark days in the calendar window that have sexp diary entries.
926 Each entry in the diary file (or included files) visible in the calendar window
927 is marked. See the documentation for the function `list-sexp-diary-entries'."
928 (let* ((sexp-mark (regexp-quote sexp-diary-entry-symbol))
929 (s-entry (concat "\\(\\`\\|\^M\\|\n\\)\\("
930 (regexp-quote sexp-mark) "(\\)\\|\\("
931 (regexp-quote diary-nonmarking-symbol)
932 (regexp-quote sexp-mark) "(diary-remind\\)"))
935 (first-date)
936 (last-date)
937 (mark)
938 file-glob-attrs)
939 (setq file-glob-attrs (nth 1 (diary-pull-attrs nil '())))
940 (save-excursion
941 (set-buffer calendar-buffer)
942 (setq m displayed-month)
943 (setq y displayed-year))
944 (increment-calendar-month m y -1)
945 (setq first-date
946 (calendar-absolute-from-gregorian (list m 1 y)))
947 (increment-calendar-month m y 2)
948 (setq last-date
949 (calendar-absolute-from-gregorian
950 (list m (calendar-last-day-of-month m y) y)))
951 (goto-char (point-min))
952 (while (re-search-forward s-entry nil t)
953 (if (char-equal (preceding-char) ?\()
954 (setq marking-diary-entry t)
955 (setq marking-diary-entry nil))
956 (re-search-backward "(")
957 (let ((sexp-start (point))
958 sexp entry entry-start line-start marks)
959 (forward-sexp)
960 (setq sexp (buffer-substring-no-properties sexp-start (point)))
961 (save-excursion
962 (re-search-backward "\^M\\|\n\\|\\`")
963 (setq line-start (point)))
964 (forward-char 1)
965 (if (and (or (char-equal (preceding-char) ?\^M)
966 (char-equal (preceding-char) ?\n))
967 (not (looking-at " \\|\^I")))
968 (progn;; Diary entry consists only of the sexp
969 (backward-char 1)
970 (setq entry ""))
971 (setq entry-start (point))
972 ;; Find end of entry
973 (re-search-forward "\^M\\|\n" nil t)
974 (while (looking-at " \\|\^I")
975 (or (re-search-forward "\^M\\|\n" nil t)
976 (re-search-forward "$" nil t)))
977 (if (or (char-equal (preceding-char) ?\^M)
978 (char-equal (preceding-char) ?\n))
979 (backward-char 1))
980 (setq entry (buffer-substring-no-properties entry-start (point)))
981 (while (string-match "[\^M]" entry)
982 (aset entry (match-beginning 0) ?\n )))
983 (calendar-for-loop date from first-date to last-date do
984 (if (setq mark (diary-sexp-entry sexp entry
985 (calendar-gregorian-from-absolute date)))
986 (progn
987 (setq marks (diary-pull-attrs entry file-glob-attrs)
988 marks (nth 1 (diary-pull-attrs entry file-glob-attrs)))
989 (mark-visible-calendar-date
990 (calendar-gregorian-from-absolute date)
991 (if (< 0 (length marks))
992 marks
993 (if (consp mark)
994 (car mark)))))))))))
996 (defun mark-included-diary-files ()
997 "Mark the diary entries from other diary files with those of the diary file.
998 This function is suitable for use as the `mark-diary-entries-hook'; it enables
999 you to use shared diary files together with your own. The files included are
1000 specified in the diary-file by lines of this form:
1001 #include \"filename\"
1002 This is recursive; that is, #include directives in diary files thus included
1003 are obeyed. You can change the `#include' to some other string by
1004 changing the variable `diary-include-string'."
1005 (goto-char (point-min))
1006 (while (re-search-forward
1007 (concat
1008 "\\(\\`\\|\^M\\|\n\\)"
1009 (regexp-quote diary-include-string)
1010 " \"\\([^\"]*\\)\"")
1011 nil t)
1012 (let ((diary-file (substitute-in-file-name
1013 (buffer-substring-no-properties
1014 (match-beginning 2) (match-end 2))))
1015 (mark-diary-entries-hook 'mark-included-diary-files))
1016 (if (file-exists-p diary-file)
1017 (if (file-readable-p diary-file)
1018 (progn
1019 (mark-diary-entries)
1020 (kill-buffer (find-buffer-visiting diary-file)))
1021 (beep)
1022 (message "Can't read included diary file %s" diary-file)
1023 (sleep-for 2))
1024 (beep)
1025 (message "Can't find included diary file %s" diary-file)
1026 (sleep-for 2))))
1027 (goto-char (point-min)))
1029 (defun mark-calendar-days-named (dayname &optional color)
1030 "Mark all dates in the calendar window that are day DAYNAME of the week.
1031 0 means all Sundays, 1 means all Mondays, and so on."
1032 (save-excursion
1033 (set-buffer calendar-buffer)
1034 (let ((prev-month displayed-month)
1035 (prev-year displayed-year)
1036 (succ-month displayed-month)
1037 (succ-year displayed-year)
1038 (last-day)
1039 (day))
1040 (increment-calendar-month succ-month succ-year 1)
1041 (increment-calendar-month prev-month prev-year -1)
1042 (setq day (calendar-absolute-from-gregorian
1043 (calendar-nth-named-day 1 dayname prev-month prev-year)))
1044 (setq last-day (calendar-absolute-from-gregorian
1045 (calendar-nth-named-day -1 dayname succ-month succ-year)))
1046 (while (<= day last-day)
1047 (mark-visible-calendar-date (calendar-gregorian-from-absolute day) color)
1048 (setq day (+ day 7))))))
1050 (defun mark-calendar-date-pattern (month day year &optional color)
1051 "Mark all dates in the calendar window that conform to MONTH/DAY/YEAR.
1052 A value of 0 in any position is a wildcard."
1053 (save-excursion
1054 (set-buffer calendar-buffer)
1055 (let ((m displayed-month)
1056 (y displayed-year))
1057 (increment-calendar-month m y -1)
1058 (calendar-for-loop i from 0 to 2 do
1059 (mark-calendar-month m y month day year color)
1060 (increment-calendar-month m y 1)))))
1062 (defun mark-calendar-month (month year p-month p-day p-year &optional color)
1063 "Mark dates in the MONTH/YEAR that conform to pattern P-MONTH/P_DAY/P-YEAR.
1064 A value of 0 in any position of the pattern is a wildcard."
1065 (if (or (and (= month p-month)
1066 (or (= p-year 0) (= year p-year)))
1067 (and (= p-month 0)
1068 (or (= p-year 0) (= year p-year))))
1069 (if (= p-day 0)
1070 (calendar-for-loop
1071 i from 1 to (calendar-last-day-of-month month year) do
1072 (mark-visible-calendar-date (list month i year) color))
1073 (mark-visible-calendar-date (list month p-day year) color))))
1075 (defun sort-diary-entries ()
1076 "Sort the list of diary entries by time of day."
1077 (setq diary-entries-list (sort diary-entries-list 'diary-entry-compare)))
1079 (defun diary-entry-compare (e1 e2)
1080 "Returns t if E1 is earlier than E2."
1081 (or (calendar-date-compare e1 e2)
1082 (and (calendar-date-equal (car e1) (car e2))
1083 (let* ((ts1 (cadr e1)) (t1 (diary-entry-time ts1))
1084 (ts2 (cadr e2)) (t2 (diary-entry-time ts2)))
1085 (or (< t1 t2)
1086 (and (= t1 t2)
1087 (string-lessp ts1 ts2)))))))
1089 (defcustom diary-unknown-time
1090 -9999
1091 "*Value returned by diary-entry-time when no time is found.
1092 The default value -9999 causes entries with no recognizable time to be placed
1093 before those with times; 9999 would place entries with no recognizable time
1094 after those with times."
1095 :type 'integer
1096 :group 'diary
1097 :version "20.3")
1099 (defun diary-entry-time (s)
1100 "Return time at the beginning of the string S as a military-style integer.
1101 For example, returns 1325 for 1:25pm.
1102 Returns `diary-unknown-time' (default value -9999) if no time is recognized. The recognized forms are XXXX, X:XX, or
1103 XX:XX (military time), and XXam, XXAM, XXpm, XXPM, XX:XXam, XX:XXAM XX:XXpm,
1104 or XX:XXPM."
1105 (let ((case-fold-search nil))
1106 (cond ((string-match ; Military time
1107 "\\`[ \t\n\\^M]*\\([0-9]?[0-9]\\):?\\([0-9][0-9]\\)\\(\\>\\|[^ap]\\)" s)
1108 (+ (* 100 (string-to-int
1109 (substring s (match-beginning 1) (match-end 1))))
1110 (string-to-int (substring s (match-beginning 2) (match-end 2)))))
1111 ((string-match ; Hour only XXam or XXpm
1112 "\\`[ \t\n\\^M]*\\([0-9]?[0-9]\\)\\([ap]\\)m\\>" s)
1113 (+ (* 100 (% (string-to-int
1114 (substring s (match-beginning 1) (match-end 1)))
1115 12))
1116 (if (equal ?a (downcase (aref s (match-beginning 2))))
1117 0 1200)))
1118 ((string-match ; Hour and minute XX:XXam or XX:XXpm
1119 "\\`[ \t\n\\^M]*\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\>" s)
1120 (+ (* 100 (% (string-to-int
1121 (substring s (match-beginning 1) (match-end 1)))
1122 12))
1123 (string-to-int (substring s (match-beginning 2) (match-end 2)))
1124 (if (equal ?a (downcase (aref s (match-beginning 3))))
1125 0 1200)))
1126 (t diary-unknown-time)))) ; Unrecognizable
1128 (defun list-sexp-diary-entries (date)
1129 "Add sexp entries for DATE from the diary file to `diary-entries-list'.
1130 Also, Make them visible in the diary file. Returns t if any entries were
1131 found.
1133 Sexp diary entries must be prefaced by a `sexp-diary-entry-symbol' (normally
1134 `%%'). The form of a sexp diary entry is
1136 %%(SEXP) ENTRY
1138 Both ENTRY and DATE are globally available when the SEXP is evaluated. If the
1139 SEXP yields the value nil, the diary entry does not apply. If it yields a
1140 non-nil value, ENTRY will be taken to apply to DATE; if the non-nil value is a
1141 string, that string will be the diary entry in the fancy diary display.
1143 For example, the following diary entry will apply to the 21st of the month
1144 if it is a weekday and the Friday before if the 21st is on a weekend:
1146 &%%(let ((dayname (calendar-day-of-week date))
1147 (day (extract-calendar-day date)))
1149 (and (= day 21) (memq dayname '(1 2 3 4 5)))
1150 (and (memq day '(19 20)) (= dayname 5)))
1151 ) UIUC pay checks deposited
1153 A number of built-in functions are available for this type of diary entry:
1155 %%(diary-date MONTH DAY YEAR &optional MARK) text
1156 Entry applies if date is MONTH, DAY, YEAR if
1157 `european-calendar-style' is nil, and DAY, MONTH, YEAR if
1158 `european-calendar-style' is t. DAY, MONTH, and YEAR
1159 can be lists of integers, the constant t, or an integer.
1160 The constant t means all values. An optional parameter
1161 MARK specifies a face or single-character string to use
1162 when highlighting the day in the calendar.
1164 %%(diary-float MONTH DAYNAME N &optional DAY MARK) text
1165 Entry will appear on the Nth DAYNAME of MONTH.
1166 (DAYNAME=0 means Sunday, 1 means Monday, and so on;
1167 if N is negative it counts backward from the end of
1168 the month. MONTH can be a list of months, a single
1169 month, or t to specify all months. Optional DAY means
1170 Nth DAYNAME of MONTH on or after/before DAY. DAY defaults
1171 to 1 if N>0 and the last day of the month if N<0. An
1172 optional parameter MARK specifies a face or single-character
1173 string to use when highlighting the day in the calendar.
1175 %%(diary-block M1 D1 Y1 M2 D2 Y2 &optional MARK) text
1176 Entry will appear on dates between M1/D1/Y1 and M2/D2/Y2,
1177 inclusive. (If `european-calendar-style' is t, the
1178 order of the parameters should be changed to D1, M1, Y1,
1179 D2, M2, Y2.) An optional parameter MARK specifies a face
1180 or single-character string to use when highlighting the
1181 day in the calendar.
1183 %%(diary-anniversary MONTH DAY YEAR &optional MARK) text
1184 Entry will appear on anniversary dates of MONTH DAY, YEAR.
1185 (If `european-calendar-style' is t, the order of the
1186 parameters should be changed to DAY, MONTH, YEAR.) Text
1187 can contain %d or %d%s; %d will be replaced by the number
1188 of years since the MONTH DAY, YEAR and %s will be replaced
1189 by the ordinal ending of that number (that is, `st', `nd',
1190 `rd' or `th', as appropriate. The anniversary of February
1191 29 is considered to be March 1 in a non-leap year. An
1192 optional parameter MARK specifies a face or single-character
1193 string to use when highlighting the day in the calendar.
1195 %%(diary-cyclic N MONTH DAY YEAR &optional MARK) text
1196 Entry will appear every N days, starting MONTH DAY, YEAR.
1197 (If `european-calendar-style' is t, the order of the
1198 parameters should be changed to N, DAY, MONTH, YEAR.) Text
1199 can contain %d or %d%s; %d will be replaced by the number
1200 of repetitions since the MONTH DAY, YEAR and %s will
1201 be replaced by the ordinal ending of that number (that is,
1202 `st', `nd', `rd' or `th', as appropriate. An optional
1203 parameter MARK specifies a face or single-character string
1204 to use when highlighting the day in the calendar.
1206 %%(diary-remind SEXP DAYS &optional MARKING) text
1207 Entry is a reminder for diary sexp SEXP. DAYS is either a
1208 single number or a list of numbers indicating the number(s)
1209 of days before the event that the warning(s) should occur.
1210 If the current date is (one of) DAYS before the event
1211 indicated by EXPR, then a suitable message (as specified
1212 by `diary-remind-message') appears. In addition to the
1213 reminders beforehand, the diary entry also appears on
1214 the date itself. If optional MARKING is non-nil then the
1215 *reminders* are marked on the calendar. Marking of
1216 reminders is independent of whether the entry *itself* is
1217 a marking or nonmarking one.
1219 %%(diary-day-of-year)
1220 Diary entries giving the day of the year and the number of
1221 days remaining in the year will be made every day. Note
1222 that since there is no text, it makes sense only if the
1223 fancy diary display is used.
1225 %%(diary-iso-date)
1226 Diary entries giving the corresponding ISO commercial date
1227 will be made every day. Note that since there is no text,
1228 it makes sense only if the fancy diary display is used.
1230 %%(diary-french-date)
1231 Diary entries giving the corresponding French Revolutionary
1232 date will be made every day. Note that since there is no
1233 text, it makes sense only if the fancy diary display is used.
1235 %%(diary-islamic-date)
1236 Diary entries giving the corresponding Islamic date will be
1237 made every day. Note that since there is no text, it
1238 makes sense only if the fancy diary display is used.
1240 %%(diary-hebrew-date)
1241 Diary entries giving the corresponding Hebrew date will be
1242 made every day. Note that since there is no text, it
1243 makes sense only if the fancy diary display is used.
1245 %%(diary-astro-day-number) Diary entries giving the corresponding
1246 astronomical (Julian) day number will be made every day.
1247 Note that since there is no text, it makes sense only if the
1248 fancy diary display is used.
1250 %%(diary-julian-date) Diary entries giving the corresponding
1251 Julian date will be made every day. Note that since
1252 there is no text, it makes sense only if the fancy diary
1253 display is used.
1255 %%(diary-sunrise-sunset)
1256 Diary entries giving the local times of sunrise and sunset
1257 will be made every day. Note that since there is no text,
1258 it makes sense only if the fancy diary display is used.
1259 Floating point required.
1261 %%(diary-phases-of-moon)
1262 Diary entries giving the times of the phases of the moon
1263 will be when appropriate. Note that since there is no text,
1264 it makes sense only if the fancy diary display is used.
1265 Floating point required.
1267 %%(diary-yahrzeit MONTH DAY YEAR) text
1268 Text is assumed to be the name of the person; the date is
1269 the date of death on the *civil* calendar. The diary entry
1270 will appear on the proper Hebrew-date anniversary and on the
1271 day before. (If `european-calendar-style' is t, the order
1272 of the parameters should be changed to DAY, MONTH, YEAR.)
1274 %%(diary-rosh-hodesh)
1275 Diary entries will be made on the dates of Rosh Hodesh on
1276 the Hebrew calendar. Note that since there is no text, it
1277 makes sense only if the fancy diary display is used.
1279 %%(diary-parasha)
1280 Diary entries giving the weekly parasha will be made on
1281 every Saturday. Note that since there is no text, it
1282 makes sense only if the fancy diary display is used.
1284 %%(diary-omer)
1285 Diary entries giving the omer count will be made every day
1286 from Passover to Shavuot. Note that since there is no text,
1287 it makes sense only if the fancy diary display is used.
1289 Marking these entries is *extremely* time consuming, so these entries are
1290 best if they are nonmarking."
1291 (let* ((mark (regexp-quote diary-nonmarking-symbol))
1292 (sexp-mark (regexp-quote sexp-diary-entry-symbol))
1293 (s-entry (concat "\\(\\`\\|\^M\\|\n\\)" mark "?" sexp-mark "("))
1294 entry-found file-glob-attrs marks)
1295 (goto-char (point-min))
1296 (save-excursion
1297 (setq file-glob-attrs (nth 1 (diary-pull-attrs nil '()))))
1298 (while (re-search-forward s-entry nil t)
1299 (backward-char 1)
1300 (let ((sexp-start (point))
1301 (sexp)
1302 (entry)
1303 (specifier)
1304 (entry-start)
1305 (line-start))
1306 (forward-sexp)
1307 (setq sexp (buffer-substring-no-properties sexp-start (point)))
1308 (save-excursion
1309 (re-search-backward "\^M\\|\n\\|\\`")
1310 (setq line-start (point)))
1311 (setq specifier
1312 (buffer-substring-no-properties (1+ line-start) (point))
1313 entry-start (1+ line-start))
1314 (forward-char 1)
1315 (if (and (or (char-equal (preceding-char) ?\^M)
1316 (char-equal (preceding-char) ?\n))
1317 (not (looking-at " \\|\^I")))
1318 (progn;; Diary entry consists only of the sexp
1319 (backward-char 1)
1320 (setq entry ""))
1321 (setq entry-start (point))
1322 (re-search-forward "\^M\\|\n" nil t)
1323 (while (looking-at " \\|\^I")
1324 (re-search-forward "\^M\\|\n" nil t))
1325 (backward-char 1)
1326 (setq entry (buffer-substring-no-properties entry-start (point)))
1327 (while (string-match "[\^M]" entry)
1328 (aset entry (match-beginning 0) ?\n )))
1329 (let ((diary-entry (diary-sexp-entry sexp entry date))
1330 temp)
1331 (setq entry (if (consp diary-entry)
1332 (cdr diary-entry)
1333 diary-entry))
1334 (if diary-entry
1335 (progn
1336 (subst-char-in-region line-start (point) ?\^M ?\n t)
1337 (if (< 0 (length entry))
1338 (setq temp (diary-pull-attrs entry file-glob-attrs)
1339 entry (nth 0 temp)
1340 marks (nth 1 temp)))))
1341 (add-to-diary-list date
1342 entry
1343 specifier
1344 (if entry-start (copy-marker entry-start)
1345 nil)
1346 marks)
1347 (setq entry-found (or entry-found diary-entry)))))
1348 entry-found))
1350 (defun diary-sexp-entry (sexp entry date)
1351 "Process a SEXP diary ENTRY for DATE."
1352 (let ((result (if calendar-debug-sexp
1353 (let ((stack-trace-on-error t))
1354 (eval (car (read-from-string sexp))))
1355 (condition-case nil
1356 (eval (car (read-from-string sexp)))
1357 (error
1358 (beep)
1359 (message "Bad sexp at line %d in %s: %s"
1360 (save-excursion
1361 (save-restriction
1362 (narrow-to-region 1 (point))
1363 (goto-char (point-min))
1364 (let ((lines 1))
1365 (while (re-search-forward "\n\\|\^M" nil t)
1366 (setq lines (1+ lines)))
1367 lines)))
1368 diary-file sexp)
1369 (sleep-for 2))))))
1370 (cond ((stringp result) result)
1371 ((and (consp result)
1372 (stringp (cdr result))) result)
1373 (result entry)
1374 (t nil))))
1376 (defun diary-date (month day year &optional mark)
1377 "Specific date(s) diary entry.
1378 Entry applies if date is MONTH, DAY, YEAR if `european-calendar-style' is nil,
1379 and DAY, MONTH, YEAR if `european-calendar-style' is t. DAY, MONTH, and YEAR
1380 can be lists of integers, the constant t, or an integer. The constant t means
1381 all values.
1383 An optional parameter MARK specifies a face or single-character string to
1384 use when highlighting the day in the calendar."
1385 (let* ((dd (if european-calendar-style
1386 month
1387 day))
1388 (mm (if european-calendar-style
1390 month))
1391 (m (extract-calendar-month date))
1392 (y (extract-calendar-year date))
1393 (d (extract-calendar-day date)))
1394 (if (and
1395 (or (and (listp dd) (memq d dd))
1396 (equal d dd)
1397 (eq dd t))
1398 (or (and (listp mm) (memq m mm))
1399 (equal m mm)
1400 (eq mm t))
1401 (or (and (listp year) (memq y year))
1402 (equal y year)
1403 (eq year t)))
1404 (cons mark entry))))
1406 (defun diary-block (m1 d1 y1 m2 d2 y2 &optional mark)
1407 "Block diary entry.
1408 Entry applies if date is between, or on one of, two dates.
1409 The order of the parameters is
1410 M1, D1, Y1, M2, D2, Y2 if `european-calendar-style' is nil, and
1411 D1, M1, Y1, D2, M2, Y2 if `european-calendar-style' is t.
1413 An optional parameter MARK specifies a face or single-character string to
1414 use when highlighting the day in the calendar."
1416 (let ((date1 (calendar-absolute-from-gregorian
1417 (if european-calendar-style
1418 (list d1 m1 y1)
1419 (list m1 d1 y1))))
1420 (date2 (calendar-absolute-from-gregorian
1421 (if european-calendar-style
1422 (list d2 m2 y2)
1423 (list m2 d2 y2))))
1424 (d (calendar-absolute-from-gregorian date)))
1425 (if (and (<= date1 d) (<= d date2))
1426 (cons mark entry))))
1428 (defun diary-float (month dayname n &optional day mark)
1429 "Floating diary entry--entry applies if date is the nth dayname of month.
1430 Parameters are MONTH, DAYNAME, N. MONTH can be a list of months, the constant
1431 t, or an integer. The constant t means all months. If N is negative, count
1432 backward from the end of the month.
1434 An optional parameter DAY means the Nth DAYNAME on or after/before MONTH DAY.
1435 Optional MARK specifies a face or single-character string to use when
1436 highlighting the day in the calendar."
1437 ;; This is messy because the diary entry may apply, but the date on which it
1438 ;; is based can be in a different month/year. For example, asking for the
1439 ;; first Monday after December 30. For large values of |n| the problem is
1440 ;; more grotesque.
1441 (and (= dayname (calendar-day-of-week date))
1442 (let* ((m (extract-calendar-month date))
1443 (d (extract-calendar-day date))
1444 (y (extract-calendar-year date))
1445 (limit; last (n>0) or first (n<0) possible base date for entry
1446 (calendar-nth-named-absday (- n) dayname m y d))
1447 (last-abs (if (> n 0) limit (+ limit 6)))
1448 (first-abs (if (> n 0) (- limit 6) limit))
1449 (last (calendar-gregorian-from-absolute last-abs))
1450 (first (calendar-gregorian-from-absolute first-abs))
1451 ; m1, d1 is first possible base date
1452 (m1 (extract-calendar-month first))
1453 (d1 (extract-calendar-day first))
1454 (y1 (extract-calendar-year first))
1455 ; m2, d2 is last possible base date
1456 (m2 (extract-calendar-month last))
1457 (d2 (extract-calendar-day last))
1458 (y2 (extract-calendar-year last)))
1459 (if (or (and (= m1 m2) ; only possible base dates in one month
1460 (or (eq month t)
1461 (if (listp month)
1462 (memq m1 month)
1463 (= m1 month)))
1464 (let ((d (or day (if (> n 0)
1466 (calendar-last-day-of-month m1 y1)))))
1467 (and (<= d1 d) (<= d d2))))
1468 ;; only possible base dates straddle two months
1469 (and (or (< y1 y2)
1470 (and (= y1 y2) (< m1 m2)))
1472 ;; m1, d1 works as a base date
1473 (and
1474 (or (eq month t)
1475 (if (listp month)
1476 (memq m1 month)
1477 (= m1 month)))
1478 (<= d1 (or day (if (> n 0)
1480 (calendar-last-day-of-month m1 y1)))))
1481 ;; m2, d2 works as a base date
1482 (and (or (eq month t)
1483 (if (listp month)
1484 (memq m2 month)
1485 (= m2 month)))
1486 (<= (or day (if (> n 0)
1488 (calendar-last-day-of-month m2 y2)))
1489 d2)))))
1490 (cons mark entry)))))
1493 (defun diary-anniversary (month day year &optional mark)
1494 "Anniversary diary entry.
1495 Entry applies if date is the anniversary of MONTH, DAY, YEAR if
1496 `european-calendar-style' is nil, and DAY, MONTH, YEAR if
1497 `european-calendar-style' is t. Diary entry can contain `%d' or `%d%s'; the
1498 %d will be replaced by the number of years since the MONTH DAY, YEAR and the
1499 %s will be replaced by the ordinal ending of that number (that is, `st', `nd',
1500 `rd' or `th', as appropriate. The anniversary of February 29 is considered
1501 to be March 1 in non-leap years.
1503 An optional parameter MARK specifies a face or single-character string to
1504 use when highlighting the day in the calendar."
1505 (let* ((d (if european-calendar-style
1506 month
1507 day))
1508 (m (if european-calendar-style
1510 month))
1511 (y (extract-calendar-year date))
1512 (diff (- y year)))
1513 (if (and (= m 2) (= d 29) (not (calendar-leap-year-p y)))
1514 (setq m 3
1515 d 1))
1516 (if (and (> diff 0) (calendar-date-equal (list m d y) date))
1517 (cons mark (format entry diff (diary-ordinal-suffix diff))))))
1519 (defun diary-cyclic (n month day year &optional mark)
1520 "Cycle diary entry--entry applies every N days starting at MONTH, DAY, YEAR.
1521 If `european-calendar-style' is t, parameters are N, DAY, MONTH, YEAR.
1522 ENTRY can contain `%d' or `%d%s'; the %d will be replaced by the number of
1523 repetitions since the MONTH DAY, YEAR and %s will be replaced by the
1524 ordinal ending of that number (that is, `st', `nd', `rd' or `th', as
1525 appropriate.
1527 An optional parameter MARK specifies a face or single-character string to
1528 use when highlighting the day in the calendar."
1529 (let* ((d (if european-calendar-style
1530 month
1531 day))
1532 (m (if european-calendar-style
1534 month))
1535 (diff (- (calendar-absolute-from-gregorian date)
1536 (calendar-absolute-from-gregorian
1537 (list m d year))))
1538 (cycle (/ diff n)))
1539 (if (and (>= diff 0) (zerop (% diff n)))
1540 (cons mark (format entry cycle (diary-ordinal-suffix cycle))))))
1542 (defun diary-ordinal-suffix (n)
1543 "Ordinal suffix for N. (That is, `st', `nd', `rd', or `th', as appropriate.)"
1544 (if (or (memq (% n 100) '(11 12 13))
1545 (< 3 (% n 10)))
1546 "th"
1547 (aref ["th" "st" "nd" "rd"] (% n 10))))
1549 (defun diary-day-of-year ()
1550 "Day of year and number of days remaining in the year of date diary entry."
1551 (calendar-day-of-year-string date))
1553 (defcustom diary-remind-message
1554 '("Reminder: Only "
1555 (if (= 0 (% days 7))
1556 (concat (int-to-string (/ days 7)) (if (= 7 days) " week" " weeks"))
1557 (concat (int-to-string days) (if (= 1 days) " day" " days")))
1558 " until "
1559 diary-entry)
1560 "*Pseudo-pattern giving form of reminder messages in the fancy diary
1561 display.
1563 Used by the function `diary-remind', a pseudo-pattern is a list of
1564 expressions that can involve the keywords `days' (a number), `date' (a list of
1565 month, day, year), and `diary-entry' (a string)."
1566 :type 'sexp
1567 :group 'diary)
1569 (defun diary-remind (sexp days &optional marking)
1570 "Provide a reminder of a diary entry.
1571 SEXP is a diary-sexp. DAYS is either a single number or a list of numbers
1572 indicating the number(s) of days before the event that the warning(s) should
1573 occur on. If the current date is (one of) DAYS before the event indicated by
1574 SEXP, then a suitable message (as specified by `diary-remind-message' is
1575 returned.
1577 In addition to the reminders beforehand, the diary entry also appears on the
1578 date itself.
1580 A `diary-nonmarking-symbol' at the beginning of the line of the diary-remind
1581 entry specifies that the diary entry (not the reminder) is non-marking.
1582 Marking of reminders is independent of whether the entry itself is a marking
1583 or nonmarking; if optional parameter MARKING is non-nil then the reminders are
1584 marked on the calendar."
1585 (let ((diary-entry (eval sexp)))
1586 (cond
1587 ;; Diary entry applies on date
1588 ((and diary-entry
1589 (or (not marking-diary-entries) marking-diary-entry))
1590 diary-entry)
1591 ;; Diary entry may apply to `days' before date
1592 ((and (integerp days)
1593 (not diary-entry); Diary entry does not apply to date
1594 (or (not marking-diary-entries) marking))
1595 (let ((date (calendar-gregorian-from-absolute
1596 (+ (calendar-absolute-from-gregorian date) days))))
1597 (if (setq diary-entry (eval sexp))
1598 (mapconcat 'eval diary-remind-message ""))))
1599 ;; Diary entry may apply to one of a list of days before date
1600 ((and (listp days) days)
1601 (or (diary-remind sexp (car days) marking)
1602 (diary-remind sexp (cdr days) marking))))))
1604 (defun add-to-diary-list (date string specifier marker &optional globcolor)
1605 "Add the entry (DATE STRING SPECIFIER MARKER GLOBCOLOR) to `diary-entries-list'.
1606 Do nothing if DATE or STRING is nil."
1607 (when (and date string)
1608 (if diary-file-name-prefix
1609 (let ((prefix (funcall diary-file-name-prefix-function
1610 (buffer-file-name))))
1611 (or (string= prefix "")
1612 (setq string (format "[%s] %s" prefix string)))))
1613 (setq diary-entries-list
1614 (append diary-entries-list
1615 (list (list date string specifier marker globcolor))))))
1617 (defun make-diary-entry (string &optional nonmarking file)
1618 "Insert a diary entry STRING which may be NONMARKING in FILE.
1619 If omitted, NONMARKING defaults to nil and FILE defaults to diary-file."
1620 (find-file-other-window
1621 (substitute-in-file-name (if file file diary-file)))
1622 (widen)
1623 (goto-char (point-max))
1624 (when (let ((case-fold-search t))
1625 (search-backward "Local Variables:"
1626 (max (- (point-max) 3000) (point-min))
1628 (beginning-of-line)
1629 (insert "\n")
1630 (previous-line 1))
1631 (insert
1632 (if (bolp) "" "\n")
1633 (if nonmarking diary-nonmarking-symbol "")
1634 string " "))
1636 (defun insert-diary-entry (arg)
1637 "Insert a diary entry for the date indicated by point.
1638 Prefix arg will make the entry nonmarking."
1639 (interactive "P")
1640 (make-diary-entry (calendar-date-string (calendar-cursor-to-date t) t t)
1641 arg))
1643 (defun insert-weekly-diary-entry (arg)
1644 "Insert a weekly diary entry for the day of the week indicated by point.
1645 Prefix arg will make the entry nonmarking."
1646 (interactive "P")
1647 (make-diary-entry (calendar-day-name (calendar-cursor-to-date t))
1648 arg))
1650 (defun insert-monthly-diary-entry (arg)
1651 "Insert a monthly diary entry for the day of the month indicated by point.
1652 Prefix arg will make the entry nonmarking."
1653 (interactive "P")
1654 (let* ((calendar-date-display-form
1655 (if european-calendar-style
1656 '(day " * ")
1657 '("* " day))))
1658 (make-diary-entry (calendar-date-string (calendar-cursor-to-date t) t)
1659 arg)))
1661 (defun insert-yearly-diary-entry (arg)
1662 "Insert an annual diary entry for the day of the year indicated by point.
1663 Prefix arg will make the entry nonmarking."
1664 (interactive "P")
1665 (let* ((calendar-date-display-form
1666 (if european-calendar-style
1667 '(day " " monthname)
1668 '(monthname " " day))))
1669 (make-diary-entry (calendar-date-string (calendar-cursor-to-date t) t)
1670 arg)))
1672 (defun insert-anniversary-diary-entry (arg)
1673 "Insert an anniversary diary entry for the date given by point.
1674 Prefix arg will make the entry nonmarking."
1675 (interactive "P")
1676 (let* ((calendar-date-display-form
1677 (if european-calendar-style
1678 '(day " " month " " year)
1679 '(month " " day " " year))))
1680 (make-diary-entry
1681 (format "%s(diary-anniversary %s)"
1682 sexp-diary-entry-symbol
1683 (calendar-date-string (calendar-cursor-to-date t) nil t))
1684 arg)))
1686 (defun insert-block-diary-entry (arg)
1687 "Insert a block diary entry for the days between the point and marked date.
1688 Prefix arg will make the entry nonmarking."
1689 (interactive "P")
1690 (let* ((calendar-date-display-form
1691 (if european-calendar-style
1692 '(day " " month " " year)
1693 '(month " " day " " year)))
1694 (cursor (calendar-cursor-to-date t))
1695 (mark (or (car calendar-mark-ring)
1696 (error "No mark set in this buffer")))
1697 (start)
1698 (end))
1699 (if (< (calendar-absolute-from-gregorian mark)
1700 (calendar-absolute-from-gregorian cursor))
1701 (setq start mark
1702 end cursor)
1703 (setq start cursor
1704 end mark))
1705 (make-diary-entry
1706 (format "%s(diary-block %s %s)"
1707 sexp-diary-entry-symbol
1708 (calendar-date-string start nil t)
1709 (calendar-date-string end nil t))
1710 arg)))
1712 (defun insert-cyclic-diary-entry (arg)
1713 "Insert a cyclic diary entry starting at the date given by point.
1714 Prefix arg will make the entry nonmarking."
1715 (interactive "P")
1716 (let* ((calendar-date-display-form
1717 (if european-calendar-style
1718 '(day " " month " " year)
1719 '(month " " day " " year))))
1720 (make-diary-entry
1721 (format "%s(diary-cyclic %d %s)"
1722 sexp-diary-entry-symbol
1723 (calendar-read "Repeat every how many days: "
1724 (lambda (x) (> x 0)))
1725 (calendar-date-string (calendar-cursor-to-date t) nil t))
1726 arg)))
1728 ;;;###autoload
1729 (define-derived-mode diary-mode text-mode
1730 "Diary"
1731 "Major mode for editing the diary file."
1732 (set (make-local-variable 'font-lock-defaults)
1733 '(diary-font-lock-keywords t)))
1735 (define-derived-mode fancy-diary-display-mode text-mode
1736 "Diary"
1737 "Major mode used while displaying diary entries using Fancy Display."
1738 (set (make-local-variable 'font-lock-defaults)
1739 '(fancy-diary-font-lock-keywords t))
1740 (define-key (current-local-map) "q" 'quit-window))
1743 (defvar fancy-diary-font-lock-keywords
1744 (list
1745 (cons
1746 (concat
1747 (let ((dayname
1748 (concat "\\("
1749 (diary-name-pattern calendar-day-name-array t)
1750 "\\)"))
1751 (monthname
1752 (concat "\\("
1753 (diary-name-pattern calendar-month-name-array t)
1754 "\\)"))
1755 (day "[0-9]+")
1756 (month "[0-9]+")
1757 (year "-?[0-9]+"))
1758 (mapconcat 'eval calendar-date-display-form ""))
1759 "\\(\\(: .*\\)\\|\\(\n +.*\\)\\)*\n=+$")
1760 'diary-face)
1761 '("^.*anniversary.*$" . font-lock-keyword-face)
1762 '("^.*birthday.*$" . font-lock-keyword-face)
1763 '("^.*Yahrzeit.*$" . font-lock-reference-face)
1764 '("^\\(Erev \\)?Rosh Hodesh.*" . font-lock-function-name-face)
1765 '("^Day.*omer.*$" . font-lock-builtin-face)
1766 '("^Parashat.*$" . font-lock-comment-face)
1767 '("^[ \t]*[0-9]?[0-9]\\(:?[0-9][0-9]\\)?\\(am\\|pm\\|AM\\|PM\\)?\\(-[0-9]?[0-9]\\(:?[0-9][0-9]\\)?\\(am\\|pm\\|AM\\|PM\\)?\\)?"
1768 . font-lock-variable-name-face))
1769 "Keywords to highlight in fancy diary display")
1772 (defun font-lock-diary-sexps (limit)
1773 "Recognize sexp diary entry for font-locking."
1774 (if (re-search-forward
1775 (concat "^" (regexp-quote diary-nonmarking-symbol)
1776 "?\\(" (regexp-quote sexp-diary-entry-symbol) "\\)")
1777 limit t)
1778 (condition-case nil
1779 (save-restriction
1780 (narrow-to-region (point-min) limit)
1781 (let ((start (point)))
1782 (forward-sexp 1)
1783 (store-match-data (list start (point)))
1785 (error t))))
1787 (defun font-lock-diary-date-forms (month-list &optional symbol noabbrev)
1788 "Create a list of font-lock patterns for `diary-date-forms' with MONTH-LIST.
1789 If given, optional SYMBOL must be a prefix to entries.
1790 If optional NOABBREV is t, do not allow abbreviations in names."
1791 (let* ((dayname
1792 (concat "\\(" (diary-name-pattern calendar-day-name-array) "\\)"))
1793 (monthname (concat "\\("
1794 (diary-name-pattern month-list noabbrev)
1795 "\\|\\*\\)"))
1796 (month "\\([0-9]+\\|\\*\\)")
1797 (day "\\([0-9]+\\|\\*\\)")
1798 (year "-?\\([0-9]+\\|\\*\\)"))
1799 (mapcar '(lambda (x)
1800 (cons
1801 (concat "^" (regexp-quote diary-nonmarking-symbol) "?"
1802 (if symbol (regexp-quote symbol) "") "\\("
1803 (mapconcat 'eval
1804 ;; If backup, omit first item (backup)
1805 ;; and last item (not part of date)
1806 (if (equal (car x) 'backup)
1807 (reverse (cdr (reverse (cdr x))))
1810 ;; With backup, last item is not part of date
1811 (if (equal (car x) 'backup)
1812 (concat "\\)" (eval (car (reverse x))))
1813 "\\)"))
1814 '(1 diary-face)))
1815 diary-date-forms)))
1817 (defvar diary-font-lock-keywords
1818 (append
1819 (font-lock-diary-date-forms calendar-month-name-array)
1820 (if (or (memq 'mark-hebrew-diary-entries
1821 nongregorian-diary-marking-hook)
1822 (memq 'list-hebrew-diary-entries
1823 nongregorian-diary-listing-hook))
1824 (progn
1825 (require 'cal-hebrew)
1826 (font-lock-diary-date-forms
1827 calendar-hebrew-month-name-array-leap-year
1828 hebrew-diary-entry-symbol t)))
1829 (if (or (memq 'mark-islamic-diary-entries
1830 nongregorian-diary-marking-hook)
1831 (memq 'list-islamic-diary-entries
1832 nongregorian-diary-listing-hook))
1833 (progn
1834 (require 'cal-islamic)
1835 (font-lock-diary-date-forms
1836 calendar-islamic-month-name-array-leap-year
1837 islamic-diary-entry-symbol t)))
1838 (list
1839 (cons
1840 (concat "^" (regexp-quote diary-include-string) ".*$")
1841 'font-lock-keyword-face)
1842 (cons
1843 (concat "^" (regexp-quote diary-nonmarking-symbol)
1844 "?\\(" (regexp-quote sexp-diary-entry-symbol) "\\)")
1845 '(1 font-lock-reference-face))
1846 (cons
1847 (concat "^" (regexp-quote diary-nonmarking-symbol))
1848 'font-lock-reference-face)
1849 (cons
1850 (concat "^" (regexp-quote diary-nonmarking-symbol)
1851 "?\\(" (regexp-quote hebrew-diary-entry-symbol) "\\)")
1852 '(1 font-lock-reference-face))
1853 (cons
1854 (concat "^" (regexp-quote diary-nonmarking-symbol)
1855 "?\\(" (regexp-quote islamic-diary-entry-symbol) "\\)")
1856 '(1 font-lock-reference-face))
1857 '(font-lock-diary-sexps . font-lock-keyword-face)
1858 '("[0-9]?[0-9]\\(:?[0-9][0-9]\\)?\\(am\\|pm\\|AM\\|PM\\)\\(-[0-9]?[0-9]\\(:?[0-9][0-9]\\)?\\(am\\|pm\\|AM\\|PM\\)\\)?"
1859 . font-lock-function-name-face)))
1860 "Forms to highlight in diary-mode")
1863 (provide 'diary-lib)
1865 ;;; diary-lib.el ends here