(expand_and_dir_to_file): Remove final / by copying abspath.
[bpt/emacs.git] / lisp / diary-lib.el
CommitLineData
c0274f38 1;;; diary.el --- diary functions.
d1c7011d 2
9e2b097b 3;; Copyright (C) 1989, 1990, 1992 Free Software Foundation, Inc.
3a801d0c 4
d1c7011d 5;; Author: Edward M. Reingold <reingold@cs.uiuc.edu>
e9571d2a 6;; Keywords: calendar
d1c7011d 7
1802278a
JB
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is distributed in the hope that it will be useful,
11;; but WITHOUT ANY WARRANTY. No author or distributor
12;; accepts responsibility to anyone for the consequences of using it
13;; or for whether it serves any particular purpose or works at all,
14;; unless he says so in writing. Refer to the GNU Emacs General Public
15;; License for full details.
16
17;; Everyone is granted permission to copy, modify and redistribute
18;; GNU Emacs, but only under the conditions described in the
19;; GNU Emacs General Public License. A copy of this license is
20;; supposed to have been given to you along with GNU Emacs so you
21;; can know your rights and responsibilities. It should be in a
22;; file named COPYING. Among other things, the copyright notice
23;; and this notice must be preserved on all copies.
24
d1c7011d
ER
25;;; Commentary:
26
1802278a
JB
27;; This collection of functions implements the diary features as described
28;; in calendar.el.
29
30;; Comments, corrections, and improvements should be sent to
31;; Edward M. Reingold Department of Computer Science
32;; (217) 333-6733 University of Illinois at Urbana-Champaign
33;; reingold@cs.uiuc.edu 1304 West Springfield Avenue
34;; Urbana, Illinois 61801
35
d1c7011d
ER
36;;; Code:
37
1802278a 38(require 'calendar)
e5d77022
JB
39
40;;;###autoload
1802278a
JB
41(defun diary (&optional arg)
42 "Generate the diary window for ARG days starting with the current date.
43If no argument is provided, the number of days of diary entries is governed
44by the variable `number-of-diary-entries'. This function is suitable for
604ea1aa 45execution in a `.emacs' file."
1802278a
JB
46 (interactive "P")
47 (let ((d-file (substitute-in-file-name diary-file))
48 (date (calendar-current-date)))
49 (if (and d-file (file-exists-p d-file))
50 (if (file-readable-p d-file)
51 (list-diary-entries
52 date
53 (cond
54 (arg (prefix-numeric-value arg))
55 ((vectorp number-of-diary-entries)
56 (aref number-of-diary-entries (calendar-day-of-week date)))
57 (t number-of-diary-entries)))
58 (error "Your diary file is not readable!"))
59 (error "You don't have a diary file!"))))
60
61(defun view-diary-entries (arg)
62 "Prepare and display a buffer with diary entries.
604ea1aa
RS
63Searches the file named in `diary-file' for entries that
64match ARG days starting with the date indicated by the cursor position
65in the displayed three-month calendar."
1802278a
JB
66 (interactive "p")
67 (let ((d-file (substitute-in-file-name diary-file)))
68 (if (and d-file (file-exists-p d-file))
69 (if (file-readable-p d-file)
70 (list-diary-entries (or (calendar-cursor-to-date)
71 (error "Cursor is not on a date!"))
72 arg)
73 (error "Your diary file is not readable!"))
74 (error "You don't have a diary file!"))))
75
76(autoload 'check-calendar-holidays "holidays"
77 "Check the list of holidays for any that occur on DATE.
78The value returned is a list of strings of relevant holiday descriptions.
604ea1aa 79The holidays are those in the list `calendar-holidays'."
9e2b097b
JB
80 t)
81
1802278a
JB
82
83(autoload 'calendar-holiday-list "holidays"
84 "Form the list of holidays that occur on dates in the calendar window.
604ea1aa 85The holidays are those in the list `calendar-holidays'."
9e2b097b
JB
86 t)
87
88(autoload 'diary-french-date "cal-french"
89 "French calendar equivalent of date diary entry."
90 t)
91
92(autoload 'diary-mayan-date "cal-mayan"
93 "Mayan calendar equivalent of date diary entry."
94 t)
95
96(autoload 'diary-phases-of-moon "lunar" "Moon phases diary entry." t)
97
98(autoload 'diary-sunrise-sunset "solar"
99 "Local time of sunrise and sunset as a diary entry."
100 t)
101
102(autoload 'diary-sabbath-candles "solar"
103 "Local time of candle lighting diary entry--applies if date is a Friday.
104No diary entry if there is no sunset on that date."
105 t)
1802278a
JB
106
107(defvar diary-syntax-table
108 (standard-syntax-table)
109 "The syntax table used when parsing dates in the diary file.
110It is the standard syntax table used in Fundamental mode, but with the
111syntax of `*' changed to be a word constituent.")
112
113(modify-syntax-entry ?* "w" diary-syntax-table)
114
115(defun list-diary-entries (date number)
116 "Create and display a buffer containing the relevant lines in diary-file.
c7784735
RS
117The arguments are DATE and NUMBER; the entries selected are those
118for NUMBER days starting with date DATE. The other entries are hidden
119using selective display.
1802278a
JB
120
121Returns a list of all relevant diary entries found, if any, in order by date.
122The list entries have the form ((month day year) string). If the variable
c7784735
RS
123`diary-list-include-blanks' is t, this list includes a dummy diary entry
124\(consisting of the empty string) for a date with no diary entries.
1802278a
JB
125
126After the list is prepared, the hooks `nongregorian-diary-listing-hook',
127`list-diary-entries-hook', and `diary-display-hook' are run. These hooks
128have the following distinct roles:
129
130 `nongregorian-diary-listing-hook' can cull dates from the diary
131 and each included file. Usually used for Hebrew or Islamic
132 diary entries in files. Applied to *each* file.
133
134 `list-diary-entries-hook' adds or manipulates diary entries from
135 external sources. Used, for example, to include diary entries
136 from other files or to sort the diary entries. Invoked *once* only.
137
138 `diary-display-hook' does the actual display of information. Could be
139 used also for an appointment notification function."
140
141 (if (< 0 number)
142 (let* ((original-date date);; save for possible use in the hooks
143 (old-diary-syntax-table)
144 (diary-entries-list)
145 (date-string (calendar-date-string date))
146 (d-file (substitute-in-file-name diary-file)))
147 (message "Preparing diary...")
148 (save-excursion
149 (let ((diary-buffer (get-file-buffer d-file)))
150 (set-buffer (if diary-buffer
151 diary-buffer
152 (find-file-noselect d-file t))))
153 (setq selective-display t)
154 (setq selective-display-ellipses nil)
155 (setq old-diary-syntax-table (syntax-table))
156 (set-syntax-table diary-syntax-table)
157 (unwind-protect
158 (let ((buffer-read-only nil)
159 (diary-modified (buffer-modified-p))
160 (mark (regexp-quote diary-nonmarking-symbol)))
161 (goto-char (1- (point-max)))
162 (if (not (looking-at "\^M\\|\n"))
163 (progn
164 (forward-char 1)
165 (insert-string "\^M")))
166 (goto-char (point-min))
167 (if (not (looking-at "\^M\\|\n"))
168 (insert-string "\^M"))
169 (subst-char-in-region (point-min) (point-max) ?\n ?\^M t)
170 (calendar-for-loop i from 1 to number do
171 (let ((d diary-date-forms)
172 (month (extract-calendar-month date))
173 (day (extract-calendar-day date))
174 (year (extract-calendar-year date))
175 (entry-found (list-sexp-diary-entries date)))
176 (while d
177 (let*
178 ((date-form (if (equal (car (car d)) 'backup)
179 (cdr (car d))
180 (car d)))
181 (backup (equal (car (car d)) 'backup))
182 (dayname
183 (concat
184 (calendar-day-name date) "\\|"
185 (substring (calendar-day-name date) 0 3) ".?"))
186 (monthname
187 (concat
188 "\\*\\|"
189 (calendar-month-name month) "\\|"
190 (substring (calendar-month-name month) 0 3) ".?"))
191 (month (concat "\\*\\|0*" (int-to-string month)))
192 (day (concat "\\*\\|0*" (int-to-string day)))
193 (year
194 (concat
195 "\\*\\|0*" (int-to-string year)
196 (if abbreviated-calendar-year
197 (concat "\\|" (int-to-string (% year 100)))
198 "")))
199 (regexp
200 (concat
201 "\\(\\`\\|\^M\\|\n\\)" mark "?\\("
202 (mapconcat 'eval date-form "\\)\\(")
203 "\\)"))
204 (case-fold-search t))
205 (goto-char (point-min))
206 (while (re-search-forward regexp nil t)
207 (if backup (re-search-backward "\\<" nil t))
208 (if (and (or (char-equal (preceding-char) ?\^M)
209 (char-equal (preceding-char) ?\n))
210 (not (looking-at " \\|\^I")))
211 ;; Diary entry that consists only of date.
212 (backward-char 1)
213 ;; Found a nonempty diary entry--make it visible and
214 ;; add it to the list.
215 (setq entry-found t)
216 (let ((entry-start (point))
217 (date-start))
218 (re-search-backward "\^M\\|\n\\|\\`")
219 (setq date-start (point))
220 (re-search-forward "\^M\\|\n" nil t 2)
221 (while (looking-at " \\|\^I")
222 (re-search-forward "\^M\\|\n" nil t))
223 (backward-char 1)
224 (subst-char-in-region date-start
225 (point) ?\^M ?\n t)
226 (add-to-diary-list
227 date (buffer-substring entry-start (point)))))))
228 (setq d (cdr d)))
229 (or entry-found
230 (not diary-list-include-blanks)
231 (setq diary-entries-list
232 (append diary-entries-list
233 (list (list date "")))))
234 (setq date
235 (calendar-gregorian-from-absolute
236 (1+ (calendar-absolute-from-gregorian date))))
237 (setq entry-found nil)))
238 (set-buffer-modified-p diary-modified))
239 (set-syntax-table old-diary-syntax-table))
240 (goto-char (point-min))
241 (run-hooks 'nongregorian-diary-listing-hook
242 'list-diary-entries-hook
243 'diary-display-hook)
244 diary-entries-list))))
245
246(defun include-other-diary-files ()
247 "Include the diary entries from other diary files with those of diary-file.
604ea1aa
RS
248This function is suitable for use in `list-diary-entries-hook';
249it enables you to use shared diary files together with your own.
250The files included are specified in the diaryfile by lines of this form:
1802278a
JB
251 #include \"filename\"
252This is recursive; that is, #include directives in diary files thus included
604ea1aa 253are obeyed. You can change the `#include' to some other string by
1802278a
JB
254changing the variable `diary-include-string'."
255 (goto-char (point-min))
256 (while (re-search-forward
257 (concat
258 "\\(\\`\\|\^M\\|\n\\)"
259 (regexp-quote diary-include-string)
260 " \"\\([^\"]*\\)\"")
261 nil t)
262 (let ((diary-file (substitute-in-file-name
263 (buffer-substring (match-beginning 2) (match-end 2))))
264 (diary-list-include-blanks nil)
265 (list-diary-entries-hook 'include-other-diary-files)
266 (diary-display-hook nil))
267 (if (file-exists-p diary-file)
268 (if (file-readable-p diary-file)
269 (unwind-protect
270 (setq diary-entries-list
271 (append diary-entries-list
272 (list-diary-entries original-date number)))
273 (kill-buffer (get-file-buffer diary-file)))
274 (beep)
275 (message "Can't read included diary file %s" diary-file)
276 (sleep-for 2))
277 (beep)
278 (message "Can't find included diary file %s" diary-file)
279 (sleep-for 2))))
280 (goto-char (point-min)))
281
282(defun simple-diary-display ()
283 "Display the diary buffer if there are any relevant entries or holidays."
284 (let* ((holiday-list (if holidays-in-diary-buffer
285 (check-calendar-holidays original-date)))
286 (msg (format "No diary entries for %s %s"
287 (concat date-string (if holiday-list ":" ""))
288 (mapconcat 'identity holiday-list "; "))))
289 (if (or (not diary-entries-list)
290 (and (not (cdr diary-entries-list))
291 (string-equal (car (cdr (car diary-entries-list))) "")))
f98955ea 292 (if (<= (length msg) (frame-width))
1802278a
JB
293 (message msg)
294 (set-buffer (get-buffer-create holiday-buffer))
295 (setq buffer-read-only nil)
9e2b097b 296 (calendar-set-mode-line date-string)
1802278a
JB
297 (erase-buffer)
298 (insert (mapconcat 'identity holiday-list "\n"))
299 (goto-char (point-min))
300 (set-buffer-modified-p nil)
301 (setq buffer-read-only t)
302 (display-buffer holiday-buffer)
303 (message "No diary entries for %s" date-string))
9e2b097b
JB
304 (calendar-set-mode-line
305 (concat "Diary for " date-string
306 (if holiday-list ": " "")
307 (mapconcat 'identity holiday-list "; ")))
1802278a
JB
308 (display-buffer (get-file-buffer d-file))
309 (message "Preparing diary...done"))))
310
311(defun fancy-diary-display ()
312 "Prepare a diary buffer with relevant entries in a fancy, noneditable form.
604ea1aa 313This function is provided for optional use as the `diary-display-hook'."
1802278a
JB
314 (if (or (not diary-entries-list)
315 (and (not (cdr diary-entries-list))
316 (string-equal (car (cdr (car diary-entries-list))) "")))
317 (let* ((holiday-list (if holidays-in-diary-buffer
318 (check-calendar-holidays original-date)))
319 (msg (format "No diary entries for %s %s"
320 (concat date-string (if holiday-list ":" ""))
321 (mapconcat 'identity holiday-list "; "))))
f98955ea 322 (if (<= (length msg) (frame-width))
1802278a
JB
323 (message msg)
324 (set-buffer (get-buffer-create holiday-buffer))
325 (setq buffer-read-only nil)
9e2b097b 326 (calendar-set-mode-line date-string)
1802278a
JB
327 (erase-buffer)
328 (insert (mapconcat 'identity holiday-list "\n"))
329 (goto-char (point-min))
330 (set-buffer-modified-p nil)
331 (setq buffer-read-only t)
332 (display-buffer holiday-buffer)
333 (message "No diary entries for %s" date-string)))
334 (save-excursion;; Turn off selective-display in the diary file's buffer.
335 (set-buffer (get-file-buffer (substitute-in-file-name diary-file)))
336 (let ((diary-modified (buffer-modified-p)))
337 (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)
338 (setq selective-display nil)
339 (kill-local-variable 'mode-line-format)
340 (set-buffer-modified-p diary-modified)))
341 (save-excursion;; Prepare the fancy diary buffer.
342 (set-buffer (get-buffer-create fancy-diary-buffer))
343 (setq buffer-read-only nil)
344 (make-local-variable 'mode-line-format)
9e2b097b 345 (calendar-set-mode-line "Diary Entries")
1802278a
JB
346 (erase-buffer)
347 (let ((entry-list diary-entries-list)
348 (holiday-list)
349 (holiday-list-last-month 1)
350 (holiday-list-last-year 1)
351 (date (list 0 0 0)))
352 (while entry-list
353 (if (not (calendar-date-equal date (car (car entry-list))))
354 (progn
355 (setq date (car (car entry-list)))
356 (and holidays-in-diary-buffer
357 (calendar-date-compare
358 (list (list holiday-list-last-month
359 (calendar-last-day-of-month
360 holiday-list-last-month
361 holiday-list-last-year)
362 holiday-list-last-year))
363 (list date))
364 ;; We need to get the holidays for the next 3 months.
365 (setq holiday-list-last-month
366 (extract-calendar-month date))
367 (setq holiday-list-last-year
368 (extract-calendar-year date))
369 (increment-calendar-month
370 holiday-list-last-month holiday-list-last-year 1)
371 (setq holiday-list
372 (let ((displayed-month holiday-list-last-month)
373 (displayed-year holiday-list-last-year))
374 (calendar-holiday-list)))
375 (increment-calendar-month
376 holiday-list-last-month holiday-list-last-year 1))
377 (let* ((date-string (calendar-date-string date))
378 (date-holiday-list
379 (let ((h holiday-list)
380 (d))
381 ;; Make a list of all holidays for date.
382 (while h
383 (if (calendar-date-equal date (car (car h)))
384 (setq d (append d (cdr (car h)))))
385 (setq h (cdr h)))
386 d)))
387 (insert (if (= (point) (point-min)) "" ?\n) date-string)
388 (if date-holiday-list (insert ": "))
389 (let ((l (current-column)))
390 (insert (mapconcat 'identity date-holiday-list
391 (concat "\n" (make-string l ? )))))
392 (let ((l (current-column)))
393 (insert ?\n (make-string l ?=) ?\n)))))
394 (if (< 0 (length (car (cdr (car entry-list)))))
395 (insert (car (cdr (car entry-list))) ?\n))
396 (setq entry-list (cdr entry-list))))
397 (set-buffer-modified-p nil)
398 (goto-char (point-min))
399 (setq buffer-read-only t)
400 (display-buffer fancy-diary-buffer)
401 (message "Preparing diary...done"))))
402
403(defun print-diary-entries ()
9e2b097b
JB
404 "Print a hard copy of the diary display.
405
406If the simple diary display is being used, prepare a temp buffer with the
407visible lines of the diary buffer, add a heading line composed from the mode
408line, print the temp buffer, and destroy it.
409
410If the fancy diary display is being used, just print the buffer.
411
412The hooks given by the variable `print-diary-entries-hook' are called to do
413the actual printing."
1802278a 414 (interactive)
9e2b097b
JB
415 (if (bufferp (get-buffer fancy-diary-buffer))
416 (save-excursion
417 (set-buffer (get-buffer fancy-diary-buffer))
418 (run-hooks 'print-diary-entries-hook))
419 (let ((diary-buffer
420 (get-file-buffer (substitute-in-file-name diary-file))))
421 (if diary-buffer
422 (let ((temp-buffer (get-buffer-create "*Printable Diary Entries*"))
423 (heading))
424 (save-excursion
425 (set-buffer diary-buffer)
426 (setq heading
427 (if (not (stringp mode-line-format))
428 "All Diary Entries"
429 (string-match "^-*\\([^-].*[^-]\\)-*$" mode-line-format)
430 (substring mode-line-format
431 (match-beginning 1) (match-end 1))))
432 (copy-to-buffer temp-buffer (point-min) (point-max))
433 (set-buffer temp-buffer)
434 (while (re-search-forward "\^M.*$" nil t)
435 (replace-match ""))
436 (goto-char (point-min))
437 (insert heading "\n"
438 (make-string (length heading) ?=) "\n")
439 (run-hooks 'print-diary-entries-hook)
440 (kill-buffer temp-buffer)))
441 (error "You don't have a diary buffer!")))))
1802278a
JB
442
443(defun show-all-diary-entries ()
604ea1aa
RS
444 "Show all of the diary entries in the diary file.
445This function gets rid of the selective display of the diary file so that
1802278a
JB
446all entries, not just some, are visible. If there is no diary buffer, one
447is created."
448 (interactive)
449 (let ((d-file (substitute-in-file-name diary-file)))
450 (if (and d-file (file-exists-p d-file))
451 (if (file-readable-p d-file)
452 (save-excursion
453 (let ((diary-buffer (get-file-buffer d-file)))
454 (set-buffer (if diary-buffer
455 diary-buffer
456 (find-file-noselect d-file t)))
457 (let ((buffer-read-only nil)
458 (diary-modified (buffer-modified-p)))
459 (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)
460 (setq selective-display nil)
461 (make-local-variable 'mode-line-format)
9e2b097b 462 (setq mode-line-format default-mode-line-format)
1802278a
JB
463 (display-buffer (current-buffer))
464 (set-buffer-modified-p diary-modified))))
465 (error "Your diary file is not readable!"))
466 (error "You don't have a diary file!"))))
467
468(defun diary-name-pattern (string-array &optional fullname)
469 "Convert an STRING-ARRAY, an array of strings to a pattern.
470The pattern will match any of the strings, either entirely or abbreviated
471to three characters. An abbreviated form will match with or without a period;
472If the optional FULLNAME is t, abbreviations will not match, just the full
473name."
474 (let ((pattern ""))
475 (calendar-for-loop i from 0 to (1- (length string-array)) do
476 (setq pattern
477 (concat
478 pattern
479 (if (string-equal pattern "") "" "\\|")
480 (aref string-array i)
481 (if fullname
482 ""
483 (concat
484 "\\|"
485 (substring (aref string-array i) 0 3) ".?")))))
486 pattern))
487
488(defun mark-diary-entries ()
489 "Mark days in the calendar window that have diary entries.
604ea1aa
RS
490Each entry in the diary file visible in the calendar window is marked.
491After the entries are marked, the hooks `nongregorian-diary-marking-hook' and
1802278a
JB
492`mark-diary-entries-hook' are run."
493 (interactive)
494 (setq mark-diary-entries-in-calendar t)
495 (let ((d-file (substitute-in-file-name diary-file)))
496 (if (and d-file (file-exists-p d-file))
497 (if (file-readable-p d-file)
498 (save-excursion
499 (message "Marking diary entries...")
500 (set-buffer (find-file-noselect d-file t))
501 (let ((d diary-date-forms)
502 (old-diary-syntax-table))
503 (setq old-diary-syntax-table (syntax-table))
504 (set-syntax-table diary-syntax-table)
505 (while d
506 (let*
507 ((date-form (if (equal (car (car d)) 'backup)
508 (cdr (car d))
509 (car d)));; ignore 'backup directive
510 (dayname (diary-name-pattern calendar-day-name-array))
511 (monthname
512 (concat
513 (diary-name-pattern calendar-month-name-array)
514 "\\|\\*"))
515 (month "[0-9]+\\|\\*")
516 (day "[0-9]+\\|\\*")
517 (year "[0-9]+\\|\\*")
518 (l (length date-form))
519 (d-name-pos (- l (length (memq 'dayname date-form))))
520 (d-name-pos (if (/= l d-name-pos) (+ 2 d-name-pos)))
521 (m-name-pos (- l (length (memq 'monthname date-form))))
522 (m-name-pos (if (/= l m-name-pos) (+ 2 m-name-pos)))
523 (d-pos (- l (length (memq 'day date-form))))
524 (d-pos (if (/= l d-pos) (+ 2 d-pos)))
525 (m-pos (- l (length (memq 'month date-form))))
526 (m-pos (if (/= l m-pos) (+ 2 m-pos)))
527 (y-pos (- l (length (memq 'year date-form))))
528 (y-pos (if (/= l y-pos) (+ 2 y-pos)))
529 (regexp
530 (concat
531 "\\(\\`\\|\^M\\|\n\\)\\("
532 (mapconcat 'eval date-form "\\)\\(")
533 "\\)"))
534 (case-fold-search t))
535 (goto-char (point-min))
536 (while (re-search-forward regexp nil t)
537 (let* ((dd-name
538 (if d-name-pos
539 (buffer-substring
540 (match-beginning d-name-pos)
541 (match-end d-name-pos))))
542 (mm-name
543 (if m-name-pos
544 (buffer-substring
545 (match-beginning m-name-pos)
546 (match-end m-name-pos))))
547 (mm (string-to-int
548 (if m-pos
549 (buffer-substring
550 (match-beginning m-pos)
551 (match-end m-pos))
552 "")))
553 (dd (string-to-int
554 (if d-pos
555 (buffer-substring
556 (match-beginning d-pos)
557 (match-end d-pos))
558 "")))
559 (y-str (if y-pos
560 (buffer-substring
561 (match-beginning y-pos)
562 (match-end y-pos))))
563 (yy (if (not y-str)
564 0
565 (if (and (= (length y-str) 2)
566 abbreviated-calendar-year)
567 (let* ((current-y
568 (extract-calendar-year
569 (calendar-current-date)))
570 (y (+ (string-to-int y-str)
571 (* 100
572 (/ current-y 100)))))
573 (if (> (- y current-y) 50)
574 (- y 100)
575 (if (> (- current-y y) 50)
576 (+ y 100)
577 y)))
578 (string-to-int y-str)))))
579 (if dd-name
580 (mark-calendar-days-named
581 (cdr (assoc (capitalize (substring dd-name 0 3))
582 (calendar-make-alist
583 calendar-day-name-array
584 0
585 '(lambda (x) (substring x 0 3))))))
586 (if mm-name
587 (if (string-equal mm-name "*")
588 (setq mm 0)
589 (setq mm
590 (cdr (assoc
591 (capitalize
592 (substring mm-name 0 3))
593 (calendar-make-alist
594 calendar-month-name-array
595 1
596 '(lambda (x) (substring x 0 3)))
597 )))))
598 (mark-calendar-date-pattern mm dd yy))))
599 (setq d (cdr d))))
600 (mark-sexp-diary-entries)
601 (run-hooks 'nongregorian-diary-marking-hook
602 'mark-diary-entries-hook)
603 (set-syntax-table old-diary-syntax-table)
604 (message "Marking diary entries...done")))
605 (error "Your diary file is not readable!"))
606 (error "You don't have a diary file!"))))
607
608(defun mark-sexp-diary-entries ()
609 "Mark days in the calendar window that have sexp diary entries.
604ea1aa 610Each entry in the diary file (or included files) visible in the calendar window
1802278a
JB
611is marked. See the documentation for the function `list-sexp-diary-entries'."
612 (let* ((sexp-mark (regexp-quote sexp-diary-entry-symbol))
613 (s-entry (concat "\\(\\`\\|\^M\\|\n\\)" sexp-mark "("))
614 (m)
615 (y)
616 (first-date)
617 (last-date))
618 (save-excursion
619 (set-buffer calendar-buffer)
620 (setq m displayed-month)
621 (setq y displayed-year))
622 (increment-calendar-month m y -1)
623 (setq first-date
624 (calendar-absolute-from-gregorian (list m 1 y)))
625 (increment-calendar-month m y 2)
626 (setq last-date
627 (calendar-absolute-from-gregorian
628 (list m (calendar-last-day-of-month m y) y)))
629 (goto-char (point-min))
630 (while (re-search-forward s-entry nil t)
631 (backward-char 1)
632 (let ((sexp-start (point))
633 (sexp)
634 (entry)
635 (entry-start)
636 (line-start))
637 (forward-sexp)
638 (setq sexp (buffer-substring sexp-start (point)))
639 (save-excursion
640 (re-search-backward "\^M\\|\n\\|\\`")
641 (setq line-start (point)))
642 (forward-char 1)
643 (if (and (or (char-equal (preceding-char) ?\^M)
644 (char-equal (preceding-char) ?\n))
645 (not (looking-at " \\|\^I")))
646 (progn;; Diary entry consists only of the sexp
647 (backward-char 1)
648 (setq entry ""))
649 (setq entry-start (point))
650 (re-search-forward "\^M\\|\n" nil t)
651 (while (looking-at " \\|\^I")
652 (re-search-forward "\^M\\|\n" nil t))
653 (backward-char 1)
654 (setq entry (buffer-substring entry-start (point)))
655 (while (string-match "[\^M]" entry)
656 (aset entry (match-beginning 0) ?\n )))
657 (calendar-for-loop date from first-date to last-date do
658 (if (diary-sexp-entry sexp entry
659 (calendar-gregorian-from-absolute date))
660 (mark-visible-calendar-date
661 (calendar-gregorian-from-absolute date))))))))
662
663(defun mark-included-diary-files ()
604ea1aa
RS
664 "Mark the diary entries from other diary files with those of the diary file.
665This function is suitable for use as the `mark-diary-entries-hook'; it enables
1802278a 666you to use shared diary files together with your own. The files included are
604ea1aa 667specified in the diary-file by lines of this form:
1802278a
JB
668 #include \"filename\"
669This is recursive; that is, #include directives in diary files thus included
604ea1aa 670are obeyed. You can change the `#include' to some other string by
1802278a
JB
671changing the variable `diary-include-string'."
672 (goto-char (point-min))
673 (while (re-search-forward
674 (concat
675 "\\(\\`\\|\^M\\|\n\\)"
676 (regexp-quote diary-include-string)
677 " \"\\([^\"]*\\)\"")
678 nil t)
679 (let ((diary-file (substitute-in-file-name
680 (buffer-substring (match-beginning 2) (match-end 2))))
681 (mark-diary-entries-hook 'mark-included-diary-files))
682 (if (file-exists-p diary-file)
683 (if (file-readable-p diary-file)
684 (progn
685 (mark-diary-entries)
686 (kill-buffer (get-file-buffer diary-file)))
687 (beep)
688 (message "Can't read included diary file %s" diary-file)
689 (sleep-for 2))
690 (beep)
691 (message "Can't find included diary file %s" diary-file)
692 (sleep-for 2))))
693 (goto-char (point-min)))
694
695(defun mark-calendar-days-named (dayname)
696 "Mark all dates in the calendar window that are day DAYNAME of the week.
6970 means all Sundays, 1 means all Mondays, and so on."
698 (save-excursion
699 (set-buffer calendar-buffer)
700 (let ((prev-month displayed-month)
701 (prev-year displayed-year)
702 (succ-month displayed-month)
703 (succ-year displayed-year)
704 (last-day)
705 (day))
706 (increment-calendar-month succ-month succ-year 1)
707 (increment-calendar-month prev-month prev-year -1)
708 (setq day (calendar-absolute-from-gregorian
709 (calendar-nth-named-day 1 dayname prev-month prev-year)))
710 (setq last-day (calendar-absolute-from-gregorian
711 (calendar-nth-named-day -1 dayname succ-month succ-year)))
712 (while (<= day last-day)
713 (mark-visible-calendar-date (calendar-gregorian-from-absolute day))
714 (setq day (+ day 7))))))
715
716(defun mark-calendar-date-pattern (month day year)
717 "Mark all dates in the calendar window that conform to MONTH/DAY/YEAR.
604ea1aa 718A value of 0 in any position is a wildcard."
1802278a
JB
719 (save-excursion
720 (set-buffer calendar-buffer)
721 (let ((m displayed-month)
722 (y displayed-year))
723 (increment-calendar-month m y -1)
724 (calendar-for-loop i from 0 to 2 do
725 (mark-calendar-month m y month day year)
726 (increment-calendar-month m y 1)))))
727
728(defun mark-calendar-month (month year p-month p-day p-year)
729 "Mark dates in the MONTH/YEAR that conform to pattern P-MONTH/P_DAY/P-YEAR.
604ea1aa 730A value of 0 in any position of the pattern is a wildcard."
1802278a
JB
731 (if (or (and (= month p-month)
732 (or (= p-year 0) (= year p-year)))
733 (and (= p-month 0)
734 (or (= p-year 0) (= year p-year))))
735 (if (= p-day 0)
736 (calendar-for-loop
737 i from 1 to (calendar-last-day-of-month month year) do
738 (mark-visible-calendar-date (list month i year)))
739 (mark-visible-calendar-date (list month p-day year)))))
740
9e2b097b
JB
741(defun sort-diary-entries ()
742 "Sort the list of diary entries by time of day."
743 (setq diary-entries-list (sort diary-entries-list 'diary-entry-compare)))
744
1802278a
JB
745(defun diary-entry-compare (e1 e2)
746 "Returns t if E1 is earlier than E2."
747 (or (calendar-date-compare e1 e2)
748 (and (calendar-date-equal (car e1) (car e2))
749 (< (diary-entry-time (car (cdr e1)))
750 (diary-entry-time (car (cdr e2)))))))
751
752(defun diary-entry-time (s)
753 "Time at the beginning of the string S in a military-style integer.
754For example, returns 1325 for 1:25pm. Returns -9999 if no time is recognized.
755The recognized forms are XXXX or X:XX or XX:XX (military time), XXam or XXpm,
756and XX:XXam or XX:XXpm."
757 (cond ((string-match;; Military time
758 "^ *\\([0-9]?[0-9]\\):?\\([0-9][0-9]\\)\\(\\>\\|[^ap]\\)" s)
759 (+ (* 100 (string-to-int
760 (substring s (match-beginning 1) (match-end 1))))
761 (string-to-int (substring s (match-beginning 2) (match-end 2)))))
762 ((string-match;; Hour only XXam or XXpm
763 "^ *\\([0-9]?[0-9]\\)\\([ap]\\)m\\>" s)
764 (+ (* 100 (% (string-to-int
765 (substring s (match-beginning 1) (match-end 1)))
766 12))
767 (if (string-equal "a"
768 (substring s (match-beginning 2) (match-end 2)))
769 0 1200)))
770 ((string-match;; Hour and minute XX:XXam or XX:XXpm
771 "^ *\\([0-9]?[0-9]\\):\\([0-9][0-9]\\)\\([ap]\\)m\\>" s)
772 (+ (* 100 (% (string-to-int
773 (substring s (match-beginning 1) (match-end 1)))
774 12))
775 (string-to-int (substring s (match-beginning 2) (match-end 2)))
776 (if (string-equal "a"
777 (substring s (match-beginning 3) (match-end 3)))
778 0 1200)))
779 (t -9999)));; Unrecognizable
780
781(defun list-hebrew-diary-entries ()
604ea1aa
RS
782 "Add any Hebrew date entries from the diary file to `diary-entries-list'.
783Hebrew date diary entries must be prefaced by `hebrew-diary-entry-symbol'
784(normally an `H'). The same diary date forms govern the style of the Hebrew
1802278a
JB
785calendar entries, except that the Hebrew month names must be spelled in full.
786The Hebrew months are numbered from 1 to 13 with Nisan being 1, 12 being
787Adar I and 13 being Adar II; you must use `Adar I' if you want Adar of a
788common Hebrew year. If a Hebrew date diary entry begins with a
604ea1aa 789`diary-nonmarking-symbol', the entry will appear in the diary listing, but will
1802278a 790not be marked in the calendar. This function is provided for use with the
604ea1aa 791`nongregorian-diary-listing-hook'."
1802278a
JB
792 (if (< 0 number)
793 (let ((buffer-read-only nil)
794 (diary-modified (buffer-modified-p))
795 (gdate original-date)
796 (mark (regexp-quote diary-nonmarking-symbol)))
797 (calendar-for-loop i from 1 to number do
798 (let* ((d diary-date-forms)
799 (hdate (calendar-hebrew-from-absolute
800 (calendar-absolute-from-gregorian gdate)))
801 (month (extract-calendar-month hdate))
802 (day (extract-calendar-day hdate))
803 (year (extract-calendar-year hdate)))
804 (while d
805 (let*
806 ((date-form (if (equal (car (car d)) 'backup)
807 (cdr (car d))
808 (car d)))
809 (backup (equal (car (car d)) 'backup))
810 (dayname
811 (concat
812 (calendar-day-name gdate) "\\|"
813 (substring (calendar-day-name gdate) 0 3) ".?"))
814 (calendar-month-name-array
815 calendar-hebrew-month-name-array-leap-year)
816 (monthname
817 (concat
818 "\\*\\|"
819 (calendar-month-name month)))
820 (month (concat "\\*\\|0*" (int-to-string month)))
821 (day (concat "\\*\\|0*" (int-to-string day)))
822 (year
823 (concat
824 "\\*\\|0*" (int-to-string year)
825 (if abbreviated-calendar-year
826 (concat "\\|" (int-to-string (% year 100)))
827 "")))
828 (regexp
829 (concat
830 "\\(\\`\\|\^M\\|\n\\)" mark "?"
831 (regexp-quote hebrew-diary-entry-symbol)
832 "\\("
833 (mapconcat 'eval date-form "\\)\\(")
834 "\\)"))
835 (case-fold-search t))
836 (goto-char (point-min))
837 (while (re-search-forward regexp nil t)
838 (if backup (re-search-backward "\\<" nil t))
839 (if (and (or (char-equal (preceding-char) ?\^M)
840 (char-equal (preceding-char) ?\n))
841 (not (looking-at " \\|\^I")))
842 ;; Diary entry that consists only of date.
843 (backward-char 1)
844 ;; Found a nonempty diary entry--make it visible and
845 ;; add it to the list.
846 (let ((entry-start (point))
847 (date-start))
848 (re-search-backward "\^M\\|\n\\|\\`")
849 (setq date-start (point))
850 (re-search-forward "\^M\\|\n" nil t 2)
851 (while (looking-at " \\|\^I")
852 (re-search-forward "\^M\\|\n" nil t))
853 (backward-char 1)
854 (subst-char-in-region date-start (point) ?\^M ?\n t)
855 (add-to-diary-list
856 gdate (buffer-substring entry-start (point)))))))
857 (setq d (cdr d))))
858 (setq gdate
859 (calendar-gregorian-from-absolute
860 (1+ (calendar-absolute-from-gregorian gdate)))))
861 (set-buffer-modified-p diary-modified))
862 (goto-char (point-min))))
863
864(defun mark-hebrew-diary-entries ()
865 "Mark days in the calendar window that have Hebrew date diary entries.
866Each entry in diary-file (or included files) visible in the calendar window
867is marked. Hebrew date entries are prefaced by a hebrew-diary-entry-symbol
9e2b097b 868(normally an `H'). The same diary-date-forms govern the style of the Hebrew
1802278a
JB
869calendar entries, except that the Hebrew month names must be spelled in full.
870The Hebrew months are numbered from 1 to 13 with Nisan being 1, 12 being
871Adar I and 13 being Adar II; you must use `Adar I' if you want Adar of a
872common Hebrew year. Hebrew date diary entries that begin with a
873diary-nonmarking symbol will not be marked in the calendar. This function
874is provided for use as part of the nongregorian-diary-marking-hook."
875 (let ((d diary-date-forms))
876 (while d
877 (let*
878 ((date-form (if (equal (car (car d)) 'backup)
879 (cdr (car d))
880 (car d)));; ignore 'backup directive
881 (dayname (diary-name-pattern calendar-day-name-array))
882 (monthname
883 (concat
884 (diary-name-pattern calendar-hebrew-month-name-array-leap-year t)
885 "\\|\\*"))
886 (month "[0-9]+\\|\\*")
887 (day "[0-9]+\\|\\*")
888 (year "[0-9]+\\|\\*")
889 (l (length date-form))
890 (d-name-pos (- l (length (memq 'dayname date-form))))
891 (d-name-pos (if (/= l d-name-pos) (+ 2 d-name-pos)))
892 (m-name-pos (- l (length (memq 'monthname date-form))))
893 (m-name-pos (if (/= l m-name-pos) (+ 2 m-name-pos)))
894 (d-pos (- l (length (memq 'day date-form))))
895 (d-pos (if (/= l d-pos) (+ 2 d-pos)))
896 (m-pos (- l (length (memq 'month date-form))))
897 (m-pos (if (/= l m-pos) (+ 2 m-pos)))
898 (y-pos (- l (length (memq 'year date-form))))
899 (y-pos (if (/= l y-pos) (+ 2 y-pos)))
900 (regexp
901 (concat
902 "\\(\\`\\|\^M\\|\n\\)"
903 (regexp-quote hebrew-diary-entry-symbol)
904 "\\("
905 (mapconcat 'eval date-form "\\)\\(")
906 "\\)"))
907 (case-fold-search t))
908 (goto-char (point-min))
909 (while (re-search-forward regexp nil t)
910 (let* ((dd-name
911 (if d-name-pos
912 (buffer-substring
913 (match-beginning d-name-pos)
914 (match-end d-name-pos))))
915 (mm-name
916 (if m-name-pos
917 (buffer-substring
918 (match-beginning m-name-pos)
919 (match-end m-name-pos))))
920 (mm (string-to-int
921 (if m-pos
922 (buffer-substring
923 (match-beginning m-pos)
924 (match-end m-pos))
925 "")))
926 (dd (string-to-int
927 (if d-pos
928 (buffer-substring
929 (match-beginning d-pos)
930 (match-end d-pos))
931 "")))
932 (y-str (if y-pos
933 (buffer-substring
934 (match-beginning y-pos)
935 (match-end y-pos))))
936 (yy (if (not y-str)
937 0
938 (if (and (= (length y-str) 2)
939 abbreviated-calendar-year)
940 (let* ((current-y
941 (extract-calendar-year
942 (calendar-hebrew-from-absolute
943 (calendar-absolute-from-gregorian
944 (calendar-current-date)))))
945 (y (+ (string-to-int y-str)
946 (* 100 (/ current-y 100)))))
947 (if (> (- y current-y) 50)
948 (- y 100)
949 (if (> (- current-y y) 50)
950 (+ y 100)
951 y)))
952 (string-to-int y-str)))))
953 (if dd-name
954 (mark-calendar-days-named
955 (cdr (assoc (capitalize (substring dd-name 0 3))
956 (calendar-make-alist
957 calendar-day-name-array
958 0
959 '(lambda (x) (substring x 0 3))))))
960 (if mm-name
961 (if (string-equal mm-name "*")
962 (setq mm 0)
963 (setq
964 mm
965 (cdr
966 (assoc
967 (capitalize mm-name)
968 (calendar-make-alist
969 calendar-hebrew-month-name-array-leap-year))))))
970 (mark-hebrew-calendar-date-pattern mm dd yy)))))
971 (setq d (cdr d)))))
972
973(defun mark-hebrew-calendar-date-pattern (month day year)
eabf7f96 974 "Mark dates in calendar window that conform to Hebrew date MONTH/DAY/YEAR.
604ea1aa 975A value of 0 in any position is a wildcard."
1802278a
JB
976 (save-excursion
977 (set-buffer calendar-buffer)
978 (if (and (/= 0 month) (/= 0 day))
979 (if (/= 0 year)
980 ;; Fully specified Hebrew date.
981 (let ((date (calendar-gregorian-from-absolute
982 (calendar-absolute-from-hebrew
983 (list month day year)))))
984 (if (calendar-date-is-visible-p date)
985 (mark-visible-calendar-date date)))
986 ;; Month and day in any year--this taken from the holiday stuff.
987 (if (memq displayed-month;; This test is only to speed things up a
988 (list ;; bit; it works fine without the test too.
989 (if (< 11 month) (- month 11) (+ month 1))
990 (if (< 10 month) (- month 10) (+ month 2))
991 (if (< 9 month) (- month 9) (+ month 3))
992 (if (< 8 month) (- month 8) (+ month 4))
993 (if (< 7 month) (- month 7) (+ month 5))))
994 (let ((m1 displayed-month)
995 (y1 displayed-year)
996 (m2 displayed-month)
997 (y2 displayed-year)
998 (year))
999 (increment-calendar-month m1 y1 -1)
1000 (increment-calendar-month m2 y2 1)
1001 (let* ((start-date (calendar-absolute-from-gregorian
1002 (list m1 1 y1)))
1003 (end-date (calendar-absolute-from-gregorian
1004 (list m2
1005 (calendar-last-day-of-month m2 y2)
1006 y2)))
1007 (hebrew-start
1008 (calendar-hebrew-from-absolute start-date))
1009 (hebrew-end (calendar-hebrew-from-absolute end-date))
1010 (hebrew-y1 (extract-calendar-year hebrew-start))
1011 (hebrew-y2 (extract-calendar-year hebrew-end)))
1012 (setq year (if (< 6 month) hebrew-y2 hebrew-y1))
1013 (let ((date (calendar-gregorian-from-absolute
1014 (calendar-absolute-from-hebrew
1015 (list month day year)))))
1016 (if (calendar-date-is-visible-p date)
1017 (mark-visible-calendar-date date)))))))
1018 ;; Not one of the simple cases--check all visible dates for match.
1019 ;; Actually, the following code takes care of ALL of the cases, but
1020 ;; it's much too slow to be used for the simple (common) cases.
1021 (let ((m displayed-month)
1022 (y displayed-year)
1023 (first-date)
1024 (last-date))
1025 (increment-calendar-month m y -1)
1026 (setq first-date
1027 (calendar-absolute-from-gregorian
1028 (list m 1 y)))
1029 (increment-calendar-month m y 2)
1030 (setq last-date
1031 (calendar-absolute-from-gregorian
1032 (list m (calendar-last-day-of-month m y) y)))
1033 (calendar-for-loop date from first-date to last-date do
1034 (let* ((h-date (calendar-hebrew-from-absolute date))
1035 (h-month (extract-calendar-month h-date))
1036 (h-day (extract-calendar-day h-date))
1037 (h-year (extract-calendar-year h-date)))
1038 (and (or (zerop month)
1039 (= month h-month))
1040 (or (zerop day)
1041 (= day h-day))
1042 (or (zerop year)
1043 (= year h-year))
1044 (mark-visible-calendar-date
1045 (calendar-gregorian-from-absolute date)))))))))
1046
1047(defun list-sexp-diary-entries (date)
604ea1aa 1048 "Add sexp entries for DATE from the diary file to `diary-entries-list'.
1bd410e5
JB
1049Also, Make them visible in the diary file. Returns t if any entries were
1050found.
1802278a 1051
604ea1aa 1052Sexp diary entries must be prefaced by a `sexp-diary-entry-symbol' (normally
1802278a
JB
1053`%%'). The form of a sexp diary entry is
1054
1055 %%(SEXP) ENTRY
1056
1057Both ENTRY and DATE are globally available when the SEXP is evaluated. If the
1058SEXP yields the value nil, the diary entry does not apply. If it yields a
1059non-nil value, ENTRY will be taken to apply to DATE; if the non-nil value is a
1060string, that string will be the diary entry in the fancy diary display.
1061
1062For example, the following diary entry will apply to the 21st of the month
1063if it is a weekday and the Friday before if the 21st is on a weekend:
1064
1065 &%%(let ((dayname (calendar-day-of-week date))
1066 (day (extract-calendar-day date)))
1067 (or
1068 (and (= day 21) (memq dayname '(1 2 3 4 5)))
1069 (and (memq day '(19 20)) (= dayname 5)))
1070 ) UIUC pay checks deposited
1071
1072A number of built-in functions are available for this type of diary entry:
1073
1074 %%(diary-float MONTH DAYNAME N) text
1075 Entry will appear on the Nth DAYNAME of MONTH.
1076 (DAYNAME=0 means Sunday, 1 means Monday, and so on;
1077 if N is negative it counts backward from the end of
1078 the month. MONTH can be a list of months, a single
1079 month, or t to specify all months.
1080
1081 %%(diary-block M1 D1 Y1 M2 D2 Y2) text
1082 Entry will appear on dates between M1/D1/Y1 and M2/D2/Y2,
1083 inclusive. (If `european-calendar-style' is t, the
1084 order of the parameters should be changed to D1, M1, Y1,
1085 D2, M2, Y2.)
1086
1087 %%(diary-anniversary MONTH DAY YEAR) text
1088 Entry will appear on anniversary dates of MONTH DAY, YEAR.
1089 (If `european-calendar-style' is t, the order of the
1090 parameters should be changed to DAY, MONTH, YEAR.) Text
1091 can contain %d or %d%s; %d will be replaced by the number
1092 of years since the MONTH DAY, YEAR and %s will be replaced
1093 by the ordinal ending of that number (that is, `st', `nd',
1094 `rd' or `th', as appropriate. The anniversary of February
1095 29 is considered to be March 1 in a non-leap year.
1096
1097 %%(diary-cyclic N MONTH DAY YEAR) text
1098 Entry will appear every N days, starting MONTH DAY, YEAR.
1099 (If `european-calendar-style' is t, the order of the
1100 parameters should be changed to N, DAY, MONTH, YEAR.) Text
1101 can contain %d or %d%s; %d will be replaced by the number
1102 of repetitions since the MONTH DAY, YEAR and %s will
1103 be replaced by the ordinal ending of that number (that is,
1104 `st', `nd', `rd' or `th', as appropriate.
1105
1106 %%(diary-day-of-year)
1107 Diary entries giving the day of the year and the number of
1108 days remaining in the year will be made every day. Note
1109 that since there is no text, it makes sense only if the
1110 fancy diary display is used.
1111
1112 %%(diary-iso-date)
1113 Diary entries giving the corresponding ISO commercial date
1114 will be made every day. Note that since there is no text,
1115 it makes sense only if the fancy diary display is used.
1116
1117 %%(diary-french-date)
1118 Diary entries giving the corresponding French Revolutionary
1119 date will be made every day. Note that since there is no
1120 text, it makes sense only if the fancy diary display is used.
1121
1122 %%(diary-islamic-date)
1123 Diary entries giving the corresponding Islamic date will be
1124 made every day. Note that since there is no text, it
1125 makes sense only if the fancy diary display is used.
1126
1127 %%(diary-hebrew-date)
1128 Diary entries giving the corresponding Hebrew date will be
1129 made every day. Note that since there is no text, it
1130 makes sense only if the fancy diary display is used.
1131
9e2b097b
JB
1132 %%(diary-astro-day-number) Diary entries giving the corresponding
1133 astronomical (Julian) day number will be made every day.
1134 Note that since there is no text, it makes sense only if the
1135 fancy diary display is used.
1136
1137 %%(diary-julian-date) Diary entries giving the corresponding
1138 Julian date will be made every day. Note that since
1139 there is no text, it makes sense only if the fancy diary
1140 display is used.
1141
1142 %%(diary-sunrise-sunset)
1143 Diary entries giving the local times of sunrise and sunset
1144 will be made every day. Note that since there is no text,
1145 it makes sense only if the fancy diary display is used.
1146 Floating point required.
1147
1148 %%(diary-phases-of-moon)
1149 Diary entries giving the times of the phases of the moon
1150 will be when appropriate. Note that since there is no text,
1151 it makes sense only if the fancy diary display is used.
1152 Floating point required.
1153
1802278a
JB
1154 %%(diary-yahrzeit MONTH DAY YEAR) text
1155 Text is assumed to be the name of the person; the date is
1156 the date of death on the *civil* calendar. The diary entry
1157 will appear on the proper Hebrew-date anniversary and on the
1158 day before. (If `european-calendar-style' is t, the order
1159 of the parameters should be changed to DAY, MONTH, YEAR.)
1160
9e2b097b
JB
1161 %%(diary-sunrise-sunset)
1162 Diary entries giving the local times of Sabbath candle
1163 lighting will be made every day. Note that since there is
1164 no text, it makes sense only if the fancy diary display is
1165 used. Floating point required.
1166
1802278a
JB
1167 %%(diary-rosh-hodesh)
1168 Diary entries will be made on the dates of Rosh Hodesh on
1169 the Hebrew calendar. Note that since there is no text, it
1170 makes sense only if the fancy diary display is used.
1171
1172 %%(diary-parasha)
1173 Diary entries giving the weekly parasha will be made on
1174 every Saturday. Note that since there is no text, it
1175 makes sense only if the fancy diary display is used.
1176
1177 %%(diary-omer)
1178 Diary entries giving the omer count will be made every day
1179 from Passover to Shavuoth. Note that since there is no text,
1180 it makes sense only if the fancy diary display is used.
1181
1182Marking these entries is *extremely* time consuming, so these entries are
1183best if they are nonmarking."
1184 (let* ((mark (regexp-quote diary-nonmarking-symbol))
1185 (sexp-mark (regexp-quote sexp-diary-entry-symbol))
1186 (s-entry (concat "\\(\\`\\|\^M\\|\n\\)" mark "?" sexp-mark "("))
1187 (entry-found))
1188 (goto-char (point-min))
1189 (while (re-search-forward s-entry nil t)
1190 (backward-char 1)
1191 (let ((sexp-start (point))
1192 (sexp)
1193 (entry)
1194 (entry-start)
1195 (line-start))
1196 (forward-sexp)
1197 (setq sexp (buffer-substring sexp-start (point)))
1198 (save-excursion
1199 (re-search-backward "\^M\\|\n\\|\\`")
1200 (setq line-start (point)))
1201 (forward-char 1)
1202 (if (and (or (char-equal (preceding-char) ?\^M)
1203 (char-equal (preceding-char) ?\n))
1204 (not (looking-at " \\|\^I")))
1205 (progn;; Diary entry consists only of the sexp
1206 (backward-char 1)
1207 (setq entry ""))
1208 (setq entry-start (point))
1209 (re-search-forward "\^M\\|\n" nil t)
1210 (while (looking-at " \\|\^I")
1211 (re-search-forward "\^M\\|\n" nil t))
1212 (backward-char 1)
1213 (setq entry (buffer-substring entry-start (point)))
1214 (while (string-match "[\^M]" entry)
1215 (aset entry (match-beginning 0) ?\n )))
1216 (let ((diary-entry (diary-sexp-entry sexp entry date)))
1217 (if diary-entry
1218 (subst-char-in-region line-start (point) ?\^M ?\n t))
1219 (add-to-diary-list date diary-entry)
1220 (setq entry-found (or entry-found diary-entry)))))
1221 entry-found))
1222
1223(defun diary-sexp-entry (sexp entry date)
1224 "Process a SEXP diary ENTRY for DATE."
1bd410e5
JB
1225 (let ((result (if calendar-debug-sexp
1226 (let ((stack-trace-on-error t))
1227 (eval (car (read-from-string sexp))))
1228 (condition-case nil
1229 (eval (car (read-from-string sexp)))
1230 (error
1231 (beep)
1232 (message "Bad sexp at line %d in %s: %s"
1233 (save-excursion
1234 (save-restriction
1235 (narrow-to-region 1 (point))
1236 (goto-char (point-min))
1237 (let ((lines 1))
1238 (while (re-search-forward "\n\\|\^M" nil t)
1239 (setq lines (1+ lines)))
1240 lines)))
1241 diary-file sexp)
1242 (sleep-for 2))))))
1802278a
JB
1243 (if (stringp result)
1244 result
1245 (if result
1246 entry
1247 nil))))
1248
1249(defun diary-block (m1 d1 y1 m2 d2 y2)
eabf7f96
CZ
1250 "Block diary entry.
1251Entry applies if date is between two dates. Order of the parameters is
1252M1, D1, Y1, M2, D2, Y2 `european-calendar-style' is nil, and
1802278a
JB
1253D1, M1, Y1, D2, M2, Y2 if `european-calendar-style' is t."
1254 (let ((date1 (calendar-absolute-from-gregorian
1255 (if european-calendar-style
1256 (list d1 m1 y1)
1257 (list m1 d1 y1))))
1258 (date2 (calendar-absolute-from-gregorian
1259 (if european-calendar-style
1260 (list d2 m2 y2)
1261 (list m2 d2 y2))))
1262 (d (calendar-absolute-from-gregorian date)))
1263 (if (and (<= date1 d) (<= d date2))
1264 entry)))
1265
1266(defun diary-float (month dayname n)
1267 "Floating diary entry--entry applies if date is the nth dayname of month.
1268Parameters are MONTH, DAYNAME, N. MONTH can be a list of months, the constant
1269t, or an integer. The constant t means all months. If N is negative, count
1270backward from the end of the month."
1271 (let ((m (extract-calendar-month date))
1272 (y (extract-calendar-year date)))
1273 (if (and
1274 (or (and (listp month) (memq m month))
1275 (equal m month)
1276 (eq month t))
1277 (calendar-date-equal date (calendar-nth-named-day n dayname m y)))
1278 entry)))
1279
1280(defun diary-anniversary (month day year)
eabf7f96
CZ
1281 "Anniversary diary entry.
1282Entry applies if date is the anniversary of MONTH, DAY, YEAR if
1283`european-calendar-style' is nil, and DAY, MONTH, YEAR if
1284`european-calendar-style' is t. Diary entry can contain `%d' or `%d%s'; the
1802278a
JB
1285%d will be replaced by the number of years since the MONTH DAY, YEAR and the
1286%s will be replaced by the ordinal ending of that number (that is, `st', `nd',
1287`rd' or `th', as appropriate. The anniversary of February 29 is considered
1288to be March 1 in non-leap years."
1289 (let* ((d (if european-calendar-style
1290 month
1291 day))
1292 (m (if european-calendar-style
1293 day
1294 month))
1295 (y (extract-calendar-year date))
1296 (diff (- y year)))
1297 (if (and (= m 2) (= d 29) (not (calendar-leap-year-p y)))
1298 (setq m 3
1299 d 1))
1300 (if (and (> diff 0) (calendar-date-equal (list m d y) date))
1301 (format entry diff (diary-ordinal-suffix diff)))))
1302
1303(defun diary-cyclic (n month day year)
1304 "Cycle diary entry--entry applies every N days starting at MONTH, DAY, YEAR.
1305If `european-calendar-style' is t, parameters are N, DAY, MONTH, YEAR.
1306ENTRY can contain `%d' or `%d%s'; the %d will be replaced by the number of
1307years since the MONTH DAY, YEAR and the %s will be replaced by the ordinal
1308ending of that number (that is, `st', `nd', `rd' or `th', as appropriate."
1309 (let* ((d (if european-calendar-style
1310 month
1311 day))
1312 (m (if european-calendar-style
1313 day
1314 month))
1315 (diff (- (calendar-absolute-from-gregorian date)
1316 (calendar-absolute-from-gregorian
1317 (list m d year))))
1318 (cycle (/ diff n)))
1319 (if (and (>= diff 0) (zerop (% diff n)))
1320 (format entry cycle (diary-ordinal-suffix cycle)))))
1321
1322(defun diary-ordinal-suffix (n)
1323 "Ordinal suffix for N. (That is, `st', `nd', `rd', or `th', as appropriate.)"
e5d77022
JB
1324 (if (or (memq (% n 100) '(11 12 13))
1325 (< 3 (% n 10)))
1802278a
JB
1326 "th"
1327 (aref ["th" "st" "nd" "rd"] (% n 10))))
1328
1329(defun diary-day-of-year ()
1330 "Day of year and number of days remaining in the year of date diary entry."
1331 (let* ((year (extract-calendar-year date))
1332 (day (calendar-day-number date))
1333 (days-remaining (- (calendar-day-number (list 12 31 year)) day)))
1334 (format "Day %d of %d; %d day%s remaining in the year"
1335 day year days-remaining (if (= days-remaining 1) "" "s"))))
1336
1337(defun diary-iso-date ()
1338 "ISO calendar equivalent of date diary entry."
1339 (let ((day (% (calendar-absolute-from-gregorian date) 7))
1340 (iso-date (calendar-iso-from-absolute
1341 (calendar-absolute-from-gregorian date))))
1342 (format "ISO date: Day %s of week %d of %d."
1343 (if (zerop day) 7 day)
1344 (extract-calendar-month iso-date)
1345 (extract-calendar-year iso-date))))
1346
1347(defun diary-islamic-date ()
1348 "Islamic calendar equivalent of date diary entry."
9e2b097b 1349 (let* ((i-date (calendar-islamic-from-absolute
1802278a
JB
1350 (calendar-absolute-from-gregorian date)))
1351 (calendar-month-name-array calendar-islamic-month-name-array))
1352 (if (>= (extract-calendar-year i-date) 1)
9e2b097b 1353 (format "Islamic date: %s" (calendar-date-string i-date nil t)))))
1802278a
JB
1354
1355(defun diary-hebrew-date ()
1356 "Hebrew calendar equivalent of date diary entry."
9e2b097b 1357 (let* ((h-date (calendar-hebrew-from-absolute
1802278a
JB
1358 (calendar-absolute-from-gregorian date)))
1359 (calendar-month-name-array
1360 (if (hebrew-calendar-leap-year-p
1361 (extract-calendar-year h-date))
1362 calendar-hebrew-month-name-array-leap-year
1363 calendar-hebrew-month-name-array-common-year)))
9e2b097b 1364 (format "Hebrew date: %s" (calendar-date-string h-date nil t))))
1802278a 1365
9e2b097b
JB
1366(defun diary-julian-date ()
1367 "Julian calendar equivalent of date diary entry."
1368 (format "Julian date: %s"
1369 (calendar-date-string
1370 (calendar-julian-from-absolute
1371 (calendar-absolute-from-gregorian date)))
1372 nil t))
1373
1374(defun diary-astro-day-number ()
1375 "Astronomical (Julian) day number diary entry."
1376 (format "Astronomical (Julian) day number %d"
1377 (+ 1721425 (calendar-absolute-from-gregorian date))))
1802278a
JB
1378
1379(defun diary-omer ()
eabf7f96
CZ
1380 "Omer count diary entry.
1381Entry applies if date is within 50 days after Passover."
1802278a
JB
1382 (let* ((passover
1383 (calendar-absolute-from-hebrew
1384 (list 1 15 (+ (extract-calendar-year date) 3760))))
1385 (omer (- (calendar-absolute-from-gregorian date) passover))
1386 (week (/ omer 7))
1387 (day (% omer 7)))
1388 (if (and (> omer 0) (< omer 50))
1389 (format "Day %d%s of the omer (until sunset)"
1390 omer
1391 (if (zerop week)
1392 ""
1393 (format ", that is, %d week%s%s"
1394 week
1395 (if (= week 1) "" "s")
1396 (if (zerop day)
1397 ""
1398 (format " and %d day%s"
1399 day (if (= day 1) "" "s")))))))))
1400
1401(defun diary-yahrzeit (death-month death-day death-year)
1402 "Yahrzeit diary entry--entry applies if date is yahrzeit or the day before.
1403Parameters are DEATH-MONTH, DEATH-DAY, DEATH-YEAR; the diary entry is assumed
1404to be the name of the person. Date of death is on the *civil* calendar;
1405although the date of death is specified by the civil calendar, the proper
604ea1aa 1406Hebrew calendar yahrzeit is determined. If `european-calendar-style' is t, the
1802278a
JB
1407order of the parameters is changed to DEATH-DAY, DEATH-MONTH, DEATH-YEAR."
1408 (let* ((h-date (calendar-hebrew-from-absolute
1409 (calendar-absolute-from-gregorian
1410 (if european-calendar-style
1411 (list death-day death-month death-year)
1412 (list death-month death-day death-year)))))
1413 (h-month (extract-calendar-month h-date))
1414 (h-day (extract-calendar-day h-date))
1415 (h-year (extract-calendar-year h-date))
1416 (d (calendar-absolute-from-gregorian date))
1417 (yr (extract-calendar-year (calendar-hebrew-from-absolute d)))
1418 (diff (- yr h-year))
1419 (y (hebrew-calendar-yahrzeit h-date yr)))
1420 (if (and (> diff 0) (or (= y d) (= y (1+ d))))
1421 (format "Yahrzeit of %s%s: %d%s anniversary"
1422 entry
1423 (if (= y d) "" " (evening)")
1424 diff
1425 (cond ((= (% diff 10) 1) "st")
1426 ((= (% diff 10) 2) "nd")
1427 ((= (% diff 10) 3) "rd")
1428 (t "th"))))))
1429
1430(defun diary-rosh-hodesh ()
eabf7f96
CZ
1431 "Rosh Hodesh diary entry.
1432Entry applies if date is Rosh Hodesh, the day before, or the Saturday before."
1802278a
JB
1433 (let* ((d (calendar-absolute-from-gregorian date))
1434 (h-date (calendar-hebrew-from-absolute d))
1435 (h-month (extract-calendar-month h-date))
1436 (h-day (extract-calendar-day h-date))
1437 (h-year (extract-calendar-year h-date))
1438 (leap-year (hebrew-calendar-leap-year-p h-year))
1439 (last-day (hebrew-calendar-last-day-of-month h-month h-year))
1440 (h-month-names
1441 (if leap-year
1442 calendar-hebrew-month-name-array-leap-year
1443 calendar-hebrew-month-name-array-common-year))
1444 (this-month (aref h-month-names (1- h-month)))
1445 (h-yesterday (extract-calendar-day
1446 (calendar-hebrew-from-absolute (1- d)))))
1447 (if (or (= h-day 30) (and (= h-day 1) (/= h-month 7)))
1448 (format
1449 "Rosh Hodesh %s"
1450 (if (= h-day 30)
1451 (format
1452 "%s (first day)"
1453 ;; next month must be in the same year since this
1454 ;; month can't be the last month of the year since
1455 ;; it has 30 days
1456 (aref h-month-names h-month))
1457 (if (= h-yesterday 30)
1458 (format "%s (second day)" this-month)
1459 this-month)))
9e2b097b 1460 (if (= (% d 7) 6);; Saturday--check for Shabbat Mevarhim
1802278a
JB
1461 (cond ((and (> h-day 22) (/= h-month 6) (= 29 last-day))
1462 (format "Mevarhim Rosh Hodesh %s (%s)"
1463 (aref h-month-names
1464 (if (= h-month
1465 (hebrew-calendar-last-month-of-year
1466 h-year))
1467 0 h-month))
1468 (aref calendar-day-name-array (- 29 h-day))))
1469 ((and (< h-day 30) (> h-day 22) (= 30 last-day))
1470 (format "Mevarhim Rosh Hodesh %s (%s-%s)"
1471 (aref h-month-names h-month)
e5d77022
JB
1472 (if (= h-day 29)
1473 "tomorrow"
1474 (aref calendar-day-name-array (- 29 h-day)))
1802278a 1475 (aref calendar-day-name-array
9e2b097b 1476 (% (- 30 h-day) 7)))))
e5d77022
JB
1477 (if (and (= h-day 29) (/= h-month 6))
1478 (format "Erev Rosh Hodesh %s"
1479 (aref h-month-names
1480 (if (= h-month
1481 (hebrew-calendar-last-month-of-year
1482 h-year))
1483 0 h-month))))))))
1802278a
JB
1484
1485(defun diary-parasha ()
1486 "Parasha diary entry--entry applies if date is a Saturday."
1487 (let ((d (calendar-absolute-from-gregorian date)))
1488 (if (= (% d 7) 6);; Saturday
1489 (let*
1490 ((h-year (extract-calendar-year
1491 (calendar-hebrew-from-absolute d)))
1492 (rosh-hashannah
1493 (calendar-absolute-from-hebrew (list 7 1 h-year)))
1494 (passover
1495 (calendar-absolute-from-hebrew (list 1 15 h-year)))
1496 (rosh-hashannah-day
1497 (aref calendar-day-name-array (% rosh-hashannah 7)))
1498 (passover-day
1499 (aref calendar-day-name-array (% passover 7)))
1500 (long-h (hebrew-calendar-long-heshvan-p h-year))
1501 (short-k (hebrew-calendar-short-kislev-p h-year))
1502 (type (cond ((and long-h (not short-k)) "complete")
1503 ((and (not long-h) short-k) "incomplete")
1504 (t "regular")))
1505 (year-format
1506 (symbol-value
1507 (intern (format "hebrew-calendar-year-%s-%s-%s";; keviah
1508 rosh-hashannah-day type passover-day))))
1509 (first-saturday;; of Hebrew year
1510 (calendar-dayname-on-or-before 6 (+ 6 rosh-hashannah)))
1511 (saturday;; which Saturday of the Hebrew year
1512 (/ (- d first-saturday) 7))
1513 (parasha (aref year-format saturday)))
1514 (if parasha
1515 (format
1516 "Parashat %s"
1517 (if (listp parasha);; Israel differs from diaspora
1518 (if (car parasha)
1519 (format "%s (diaspora), %s (Israel)"
1520 (hebrew-calendar-parasha-name (car parasha))
1521 (hebrew-calendar-parasha-name (cdr parasha)))
1522 (format "%s (Israel)"
1523 (hebrew-calendar-parasha-name (cdr parasha))))
1524 (hebrew-calendar-parasha-name parasha))))))))
1525
1526(defun add-to-diary-list (date string)
604ea1aa 1527 "Add the entry (DATE STRING) to `diary-entries-list'.
1802278a
JB
1528Do nothing if DATE or STRING is nil."
1529 (and date string
1530 (setq diary-entries-list
1531 (append diary-entries-list (list (list date string))))))
1532
1533(defconst hebrew-calendar-parashiot-names
1534["Bereshith" "Noah" "Lech L'cha" "Vayera" "Hayei Sarah" "Toledoth"
1535 "Vayetze" "Vayishlah" "Vayeshev" "Mikketz" "Vayiggash" "Vayhi"
1536 "Shemoth" "Vaera" "Bo" "Beshallah" "Yithro" "Mishpatim"
1537 "Terumah" "Tetzavveh" "Ki Tissa" "Vayakhel" "Pekudei" "Vayikra"
1538 "Tzav" "Shemini" "Tazria" "Metzora" "Aharei Moth" "Kedoshim"
1539 "Emor" "Behar" "Behukkotai" "Bemidbar" "Naso" "Behaalot'cha"
1540 "Shelah L'cha" "Korah" "Hukkath" "Balak" "Pinhas" "Mattoth"
1541 "Masei" "Devarim" "Vaethanan" "Ekev" "Reeh" "Shofetim"
1542 "Ki Tetze" "Ki Tavo" "Nitzavim" "Vayelech" "Haazinu"]
1543 "The names of the parashiot in the Torah.")
1544
1545;; The seven ordinary year types (keviot)
1546
1547(defconst hebrew-calendar-year-Saturday-incomplete-Sunday
1548 [nil 52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21 22]
1549 23 24 nil 25 [26 27] [28 29] 30 [31 32] 33 34 35 36 37 38 39 40 [41 42]
1550 43 44 45 46 47 48 49 50]
1bd410e5
JB
1551 "The structure of the parashiot.
1552Hebrew year starts on Saturday, is `incomplete' (Heshvan and Kislev each have
155329 days), and has Passover start on Sunday.")
1802278a
JB
1554
1555(defconst hebrew-calendar-year-Saturday-complete-Tuesday
1556 [nil 52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21 22]
1557 23 24 nil 25 [26 27] [28 29] 30 [31 32] 33 34 35 36 37 38 39 40 [41 42]
1558 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1559 "The structure of the parashiot.
1560Hebrew year that starts on Saturday, is `complete' (Heshvan and Kislev each
1561have 30 days), and has Passover start on Tuesday.")
1802278a
JB
1562
1563(defconst hebrew-calendar-year-Monday-incomplete-Tuesday
1564 [51 52 nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21 22]
1565 23 24 nil 25 [26 27] [28 29] 30 [31 32] 33 34 35 36 37 38 39 40 [41 42]
1566 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1567 "The structure of the parashiot.
1568Hebrew year that starts on Monday, is `incomplete' (Heshvan and Kislev each
1569have 29 days), and has Passover start on Tuesday.")
1802278a
JB
1570
1571(defconst hebrew-calendar-year-Monday-complete-Thursday
1572 [51 52 nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21 22]
9e2b097b
JB
1573 23 24 nil 25 [26 27] [28 29] 30 [31 32] 33 (nil . 34) (34 . 35) (35 . 36)
1574 (36 . 37) (37 . 38) ([38 39] . 39) 40 [41 42] 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1575 "The structure of the parashiot.
1576Hebrew year that starts on Monday, is `complete' (Heshvan and Kislev each have
157730 days), and has Passover start on Thursday.")
1802278a
JB
1578
1579(defconst hebrew-calendar-year-Tuesday-regular-Thursday
1580 [51 52 nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21 22]
9e2b097b
JB
1581 23 24 nil 25 [26 27] [28 29] 30 [31 32] 33 (nil . 34) (34 . 35) (35 . 36)
1582 (36 . 37) (37 . 38) ([38 39] . 39) 40 [41 42] 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1583 "The structure of the parashiot.
1584Hebrew year that starts on Tuesday, is `regular' (Heshvan has 29 days and
1585Kislev has 30 days), and has Passover start on Thursday.")
1802278a
JB
1586
1587(defconst hebrew-calendar-year-Thursday-regular-Saturday
9e2b097b
JB
1588 [52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21 22] 23
1589 24 nil (nil . 25) (25 . [26 27]) ([26 27] . [28 29]) ([28 29] . 30)
1590 (30 . 31) ([31 32] . 32) 33 34 35 36 37 38 39 40 [41 42] 43 44 45 46 47 48
1591 49 50]
1bd410e5
JB
1592 "The structure of the parashiot.
1593Hebrew year that starts on Thursday, is `regular' (Heshvan has 29 days and
1594Kislev has 30 days), and has Passover start on Saturday.")
1802278a
JB
1595
1596(defconst hebrew-calendar-year-Thursday-complete-Sunday
1597 [52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
1598 23 24 nil 25 [26 27] [28 29] 30 [31 32] 33 34 35 36 37 38 39 40 [41 42]
1599 43 44 45 46 47 48 49 50]
1bd410e5
JB
1600 "The structure of the parashiot.
1601Hebrew year that starts on Thursday, is `complete' (Heshvan and Kislev each
1602have 30 days), and has Passover start on Sunday.")
1802278a
JB
1603
1604;; The seven leap year types (keviot)
1605
1606(defconst hebrew-calendar-year-Saturday-incomplete-Tuesday
1607 [nil 52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
1608 23 24 25 26 27 nil 28 29 30 31 32 33 34 35 36 37 38 39 40 [41 42]
1609 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1610 "The structure of the parashiot.
1611Hebrew year that starts on Saturday, is `incomplete' (Heshvan and Kislev each
1612have 29 days), and has Passover start on Tuesday.")
1802278a
JB
1613
1614(defconst hebrew-calendar-year-Saturday-complete-Thursday
1615 [nil 52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
9e2b097b
JB
1616 23 24 25 26 27 nil 28 29 30 31 32 33 (nil . 34) (34 . 35) (35 . 36)
1617 (36 . 37) (37 . 38) ([38 39] . 39) 40 [41 42] 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1618 "The structure of the parashiot.
1619Hebrew year that starts on Saturday, is `complete' (Heshvan and Kislev each
1620have 30 days), and has Passover start on Thursday.")
1802278a
JB
1621
1622(defconst hebrew-calendar-year-Monday-incomplete-Thursday
1623 [51 52 nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
9e2b097b
JB
1624 23 24 25 26 27 nil 28 29 30 31 32 33 (nil . 34) (34 . 35) (35 . 36)
1625 (36 . 37) (37 . 38) ([38 39] . 39) 40 [41 42] 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1626 "The structure of the parashiot.
1627Hebrew year that starts on Monday, is `incomplete' (Heshvan and Kislev each
1628have 29 days), and has Passover start on Thursday.")
1802278a
JB
1629
1630(defconst hebrew-calendar-year-Monday-complete-Saturday
1631 [51 52 nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
9e2b097b
JB
1632 23 24 25 26 27 nil (nil . 28) (28 . 29) (29 . 30) (30 . 31) (31 . 32)
1633 (32 . 33) (33 . 34) (34 . 35) (35 . 36) (36 . 37) (37 . 38) (38 . 39)
1634 (39 . 40) (40 . 41) ([41 42] . 42) 43 44 45 46 47 48 49 50]
1bd410e5
JB
1635 "The structure of the parashiot.
1636Hebrew year that starts on Monday, is `complete' (Heshvan and Kislev each have
163730 days), and has Passover start on Saturday.")
1802278a
JB
1638
1639(defconst hebrew-calendar-year-Tuesday-regular-Saturday
1640 [51 52 nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
9e2b097b
JB
1641 23 24 25 26 27 nil (nil . 28) (28 . 29) (29 . 30) (30 . 31) (31 . 32)
1642 (32 . 33) (33 . 34) (34 . 35) (35 . 36) (36 . 37) (37 . 38) (38 . 39)
1643 (39 . 40) (40 . 41) ([41 42] . 42) 43 44 45 46 47 48 49 50]
1bd410e5
JB
1644 "The structure of the parashiot.
1645Hebrew year that starts on Tuesday, is `regular' (Heshvan has 29 days and
1646Kislev has 30 days), and has Passover start on Saturday.")
1802278a
JB
1647
1648(defconst hebrew-calendar-year-Thursday-incomplete-Sunday
1649 [52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
1650 23 24 25 26 27 28 nil 29 30 31 32 33 34 35 36 37 38 39 40 41 42
1651 43 44 45 46 47 48 49 50]
1bd410e5
JB
1652 "The structure of the parashiot.
1653Hebrew year that starts on Thursday, is `incomplete' (Heshvan and Kislev both
1654have 29 days), and has Passover start on Sunday.")
1802278a
JB
1655
1656(defconst hebrew-calendar-year-Thursday-complete-Tuesday
1657 [52 nil nil 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
1658 23 24 25 26 27 28 nil 29 30 31 32 33 34 35 36 37 38 39 40 41 42
1659 43 44 45 46 47 48 49 [50 51]]
1bd410e5
JB
1660 "The structure of the parashiot.
1661Hebrew year that starts on Thursday, is `complete' (Heshvan and Kislev both
1662have 30 days), and has Passover start on Tuesday.")
1802278a
JB
1663
1664(defun hebrew-calendar-parasha-name (p)
1665 "Name(s) corresponding to parasha P."
1666 (if (arrayp p);; combined parasha
1667 (format "%s/%s"
1668 (aref hebrew-calendar-parashiot-names (aref p 0))
1669 (aref hebrew-calendar-parashiot-names (aref p 1)))
1670 (aref hebrew-calendar-parashiot-names p)))
1671
1802278a 1672(defun list-islamic-diary-entries ()
604ea1aa
RS
1673 "Add any Islamic date entries from the diary file to `diary-entries-list'.
1674Islamic date diary entries must be prefaced by an `islamic-diary-entry-symbol'
1675(normally an `I'). The same diary date forms govern the style of the Islamic
1802278a
JB
1676calendar entries, except that the Islamic month names must be spelled in full.
1677The Islamic months are numbered from 1 to 12 with Muharram being 1 and 12 being
1678Dhu al-Hijjah. If an Islamic date diary entry begins with a
604ea1aa 1679`diary-nonmarking-symbol', the entry will appear in the diary listing, but will
1802278a 1680not be marked in the calendar. This function is provided for use with the
604ea1aa 1681`nongregorian-diary-listing-hook'."
1802278a
JB
1682 (if (< 0 number)
1683 (let ((buffer-read-only nil)
1684 (diary-modified (buffer-modified-p))
1685 (gdate original-date)
1686 (mark (regexp-quote diary-nonmarking-symbol)))
1687 (calendar-for-loop i from 1 to number do
1688 (let* ((d diary-date-forms)
1689 (idate (calendar-islamic-from-absolute
1690 (calendar-absolute-from-gregorian gdate)))
1691 (month (extract-calendar-month idate))
1692 (day (extract-calendar-day idate))
1693 (year (extract-calendar-year idate)))
1694 (while d
1695 (let*
1696 ((date-form (if (equal (car (car d)) 'backup)
1697 (cdr (car d))
1698 (car d)))
1699 (backup (equal (car (car d)) 'backup))
1700 (dayname
1701 (concat
1702 (calendar-day-name gdate) "\\|"
1703 (substring (calendar-day-name gdate) 0 3) ".?"))
1704 (calendar-month-name-array
1705 calendar-islamic-month-name-array)
1706 (monthname
1707 (concat
1708 "\\*\\|"
1709 (calendar-month-name month)))
1710 (month (concat "\\*\\|0*" (int-to-string month)))
1711 (day (concat "\\*\\|0*" (int-to-string day)))
1712 (year
1713 (concat
1714 "\\*\\|0*" (int-to-string year)
1715 (if abbreviated-calendar-year
1716 (concat "\\|" (int-to-string (% year 100)))
1717 "")))
1718 (regexp
1719 (concat
1720 "\\(\\`\\|\^M\\|\n\\)" mark "?"
1721 (regexp-quote islamic-diary-entry-symbol)
1722 "\\("
1723 (mapconcat 'eval date-form "\\)\\(")
1724 "\\)"))
1725 (case-fold-search t))
1726 (goto-char (point-min))
1727 (while (re-search-forward regexp nil t)
1728 (if backup (re-search-backward "\\<" nil t))
1729 (if (and (or (char-equal (preceding-char) ?\^M)
1730 (char-equal (preceding-char) ?\n))
1731 (not (looking-at " \\|\^I")))
1732 ;; Diary entry that consists only of date.
1733 (backward-char 1)
1734 ;; Found a nonempty diary entry--make it visible and
1735 ;; add it to the list.
1736 (let ((entry-start (point))
1737 (date-start))
1738 (re-search-backward "\^M\\|\n\\|\\`")
1739 (setq date-start (point))
1740 (re-search-forward "\^M\\|\n" nil t 2)
1741 (while (looking-at " \\|\^I")
1742 (re-search-forward "\^M\\|\n" nil t))
1743 (backward-char 1)
1744 (subst-char-in-region date-start (point) ?\^M ?\n t)
1745 (add-to-diary-list
1746 gdate (buffer-substring entry-start (point)))))))
1747 (setq d (cdr d))))
1748 (setq gdate
1749 (calendar-gregorian-from-absolute
1750 (1+ (calendar-absolute-from-gregorian gdate)))))
1751 (set-buffer-modified-p diary-modified))
1752 (goto-char (point-min))))
1753
1754(defun mark-islamic-diary-entries ()
1755 "Mark days in the calendar window that have Islamic date diary entries.
1756Each entry in diary-file (or included files) visible in the calendar window
1757is marked. Islamic date entries are prefaced by a islamic-diary-entry-symbol
9e2b097b 1758(normally an `I'). The same diary-date-forms govern the style of the Islamic
1802278a
JB
1759calendar entries, except that the Islamic month names must be spelled in full.
1760The Islamic months are numbered from 1 to 12 with Muharram being 1 and 12 being
1761Dhu al-Hijjah. Islamic date diary entries that begin with a
1762diary-nonmarking-symbol will not be marked in the calendar. This function is
1763provided for use as part of the nongregorian-diary-marking-hook."
1764 (let ((d diary-date-forms))
1765 (while d
1766 (let*
1767 ((date-form (if (equal (car (car d)) 'backup)
1768 (cdr (car d))
1769 (car d)));; ignore 'backup directive
1770 (dayname (diary-name-pattern calendar-day-name-array))
1771 (monthname
1772 (concat
1773 (diary-name-pattern calendar-islamic-month-name-array t)
1774 "\\|\\*"))
1775 (month "[0-9]+\\|\\*")
1776 (day "[0-9]+\\|\\*")
1777 (year "[0-9]+\\|\\*")
1778 (l (length date-form))
1779 (d-name-pos (- l (length (memq 'dayname date-form))))
1780 (d-name-pos (if (/= l d-name-pos) (+ 2 d-name-pos)))
1781 (m-name-pos (- l (length (memq 'monthname date-form))))
1782 (m-name-pos (if (/= l m-name-pos) (+ 2 m-name-pos)))
1783 (d-pos (- l (length (memq 'day date-form))))
1784 (d-pos (if (/= l d-pos) (+ 2 d-pos)))
1785 (m-pos (- l (length (memq 'month date-form))))
1786 (m-pos (if (/= l m-pos) (+ 2 m-pos)))
1787 (y-pos (- l (length (memq 'year date-form))))
1788 (y-pos (if (/= l y-pos) (+ 2 y-pos)))
1789 (regexp
1790 (concat
1791 "\\(\\`\\|\^M\\|\n\\)"
1792 (regexp-quote islamic-diary-entry-symbol)
1793 "\\("
1794 (mapconcat 'eval date-form "\\)\\(")
1795 "\\)"))
1796 (case-fold-search t))
1797 (goto-char (point-min))
1798 (while (re-search-forward regexp nil t)
1799 (let* ((dd-name
1800 (if d-name-pos
1801 (buffer-substring
1802 (match-beginning d-name-pos)
1803 (match-end d-name-pos))))
1804 (mm-name
1805 (if m-name-pos
1806 (buffer-substring
1807 (match-beginning m-name-pos)
1808 (match-end m-name-pos))))
1809 (mm (string-to-int
1810 (if m-pos
1811 (buffer-substring
1812 (match-beginning m-pos)
1813 (match-end m-pos))
1814 "")))
1815 (dd (string-to-int
1816 (if d-pos
1817 (buffer-substring
1818 (match-beginning d-pos)
1819 (match-end d-pos))
1820 "")))
1821 (y-str (if y-pos
1822 (buffer-substring
1823 (match-beginning y-pos)
1824 (match-end y-pos))))
1825 (yy (if (not y-str)
1826 0
1827 (if (and (= (length y-str) 2)
1828 abbreviated-calendar-year)
1829 (let* ((current-y
1830 (extract-calendar-year
1831 (calendar-islamic-from-absolute
1832 (calendar-absolute-from-gregorian
1833 (calendar-current-date)))))
1834 (y (+ (string-to-int y-str)
1835 (* 100 (/ current-y 100)))))
1836 (if (> (- y current-y) 50)
1837 (- y 100)
1838 (if (> (- current-y y) 50)
1839 (+ y 100)
1840 y)))
1841 (string-to-int y-str)))))
1842 (if dd-name
1843 (mark-calendar-days-named
1844 (cdr (assoc (capitalize (substring dd-name 0 3))
1845 (calendar-make-alist
1846 calendar-day-name-array
1847 0
1848 '(lambda (x) (substring x 0 3))))))
1849 (if mm-name
1850 (if (string-equal mm-name "*")
1851 (setq mm 0)
1852 (setq mm
1853 (cdr (assoc
1854 (capitalize mm-name)
1855 (calendar-make-alist
1856 calendar-islamic-month-name-array))))))
1857 (mark-islamic-calendar-date-pattern mm dd yy)))))
1858 (setq d (cdr d)))))
1859
1860(defun mark-islamic-calendar-date-pattern (month day year)
eabf7f96 1861 "Mark dates in calendar window that conform to Islamic date MONTH/DAY/YEAR.
604ea1aa 1862A value of 0 in any position is a wildcard."
1802278a
JB
1863 (save-excursion
1864 (set-buffer calendar-buffer)
1865 (if (and (/= 0 month) (/= 0 day))
1866 (if (/= 0 year)
1867 ;; Fully specified Islamic date.
1868 (let ((date (calendar-gregorian-from-absolute
1869 (calendar-absolute-from-islamic
1870 (list month day year)))))
1871 (if (calendar-date-is-visible-p date)
1872 (mark-visible-calendar-date date)))
1873 ;; Month and day in any year--this taken from the holiday stuff.
1874 (let* ((islamic-date (calendar-islamic-from-absolute
1875 (calendar-absolute-from-gregorian
1876 (list displayed-month 15 displayed-year))))
1877 (m (extract-calendar-month islamic-date))
1878 (y (extract-calendar-year islamic-date))
1879 (date))
1880 (if (< m 1)
1881 nil;; Islamic calendar doesn't apply.
1882 (increment-calendar-month m y (- 10 month))
1883 (if (> m 7);; Islamic date might be visible
1884 (let ((date (calendar-gregorian-from-absolute
1885 (calendar-absolute-from-islamic
1886 (list month day y)))))
1887 (if (calendar-date-is-visible-p date)
1888 (mark-visible-calendar-date date)))))))
1889 ;; Not one of the simple cases--check all visible dates for match.
1890 ;; Actually, the following code takes care of ALL of the cases, but
1891 ;; it's much too slow to be used for the simple (common) cases.
1892 (let ((m displayed-month)
1893 (y displayed-year)
1894 (first-date)
1895 (last-date))
1896 (increment-calendar-month m y -1)
1897 (setq first-date
1898 (calendar-absolute-from-gregorian
1899 (list m 1 y)))
1900 (increment-calendar-month m y 2)
1901 (setq last-date
1902 (calendar-absolute-from-gregorian
1903 (list m (calendar-last-day-of-month m y) y)))
1904 (calendar-for-loop date from first-date to last-date do
1905 (let* ((i-date (calendar-islamic-from-absolute date))
1906 (i-month (extract-calendar-month i-date))
1907 (i-day (extract-calendar-day i-date))
1908 (i-year (extract-calendar-year i-date)))
1909 (and (or (zerop month)
1910 (= month i-month))
1911 (or (zerop day)
1912 (= day i-day))
1913 (or (zerop year)
1914 (= year i-year))
1915 (mark-visible-calendar-date
1916 (calendar-gregorian-from-absolute date)))))))))
1917
49116ac0
JB
1918(provide 'diary)
1919
c0274f38 1920;;; diary.el ends here