home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.006 / xemacs-1 / lib / xemacs-19.13 / lisp / utils / highlight-headers.el < prev    next >
Encoding:
Text File  |  1995-08-18  |  19.1 KB  |  545 lines

  1. ;;; highlight-headers.el --- highlighting message headers.
  2.  
  3. ;; Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
  4. ;; Copyright (C) 1995 Tinker Systems
  5.  
  6. ;; Keywords: mail, news
  7.  
  8. ;; This file is part of XEmacs.
  9.  
  10. ;; XEmacs is free software; you can redistribute it and/or modify it
  11. ;; under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation; either version 2, or (at your option)
  13. ;; any later version.
  14.  
  15. ;; XEmacs is distributed in the hope that it will be useful, but
  16. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18. ;; General Public License for more details.
  19.  
  20. ;; You should have received a copy of the GNU General Public License
  21. ;; along with XEmacs; see the file COPYING.  If not, write to the Free
  22. ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. ;; This code is shared by RMAIL, VM, and GNUS.
  25. ;;
  26. ;; Faces:
  27. ;;
  28. ;; message-headers            the part before the colon
  29. ;; message-header-contents        the part after the colon
  30. ;; message-highlighted-header-contents    contents of "special" headers
  31. ;; message-cited-text            quoted text from other messages
  32. ;;
  33. ;; Variables:
  34. ;;
  35. ;; highlight-headers-regexp            what makes a "special" header
  36. ;; highlight-headers-citation-regexp        matches lines of quoted text
  37. ;; highlight-headers-citation-header-regexp    matches headers for quoted text
  38.  
  39. (if (find-face 'message-headers)
  40.     nil
  41.   (make-face 'message-headers)
  42.   (or (face-differs-from-default-p 'message-headers)
  43.       (copy-face 'bold 'message-headers)))
  44.  
  45. (if (find-face 'message-header-contents)
  46.     nil
  47.   (make-face 'message-header-contents)
  48.   (or (face-differs-from-default-p 'message-header-contents)
  49.       (copy-face 'italic 'message-header-contents)))
  50.  
  51. (if (find-face 'message-highlighted-header-contents)
  52.     nil
  53.   (make-face 'message-highlighted-header-contents)
  54.   (or (face-differs-from-default-p 'message-highlighted-header-contents)
  55.       (progn
  56.     (copy-face 'message-header-contents
  57.            'message-highlighted-header-contents)
  58.     ;; Most people seem not to like underlining, so change
  59.     ;; the font instead.
  60.     ;; (set-face-underline-p 'message-highlighted-header-contents t)
  61.     (or (make-face-bold 'message-highlighted-header-contents)
  62.         (make-face-unbold 'message-highlighted-header-contents)
  63.         (make-face-italic 'message-highlighted-header-contents)
  64.         (make-face-unitalic 'message-highlighted-header-contents))
  65.     )))
  66.  
  67. (if (find-face 'message-cited-text)
  68.     nil
  69.   (make-face 'message-cited-text)
  70.   (or (face-differs-from-default-p 'message-cited-text)
  71.       (copy-face 'italic 'message-cited-text)))
  72.  
  73. (if (find-face 'x-face)
  74.     nil
  75.   (make-face 'x-face)
  76.   (or (face-differs-from-default-p 'x-face)
  77.       (progn 
  78.     (copy-face 'message-highlighted-header-contents 'x-face)
  79.     (set-face-background 'x-face "white")
  80.     (set-face-foreground 'x-face "black"))))
  81.  
  82. ;;(condition-case nil
  83. ;;    (face-name 'message-addresses)
  84. ;;  (wrong-type-argument
  85. ;;   (make-face 'message-addresses)
  86. ;;   (or (face-differs-from-default-p 'message-addresses)
  87. ;;       (progn
  88. ;;     (copy-face 'bold-italic 'message-addresses)
  89. ;;     (set-face-underline-p 'message-addresses
  90. ;;                   (face-underline-p
  91. ;;                'message-highlighted-header-contents))))))
  92.  
  93. (defvar highlight-headers-regexp "Subject[ \t]*:"
  94.   "*The headers whose contents should be emphasized more.
  95. The contents of these headers will be displayed in the face 
  96. `message-highlighted-header-contents' instead of `message-header-contents'.")
  97.  
  98. (defvar highlight-headers-citation-regexp
  99.   (concat "^\\("
  100.       (mapconcat 'identity
  101.        '("[ \t]*[a-zA-Z0-9_]+>+"    ; supercite
  102.          "[ \t]*[>]+"        ; ">" with leading spaces
  103.          "[]}<>|:]+[ \t]*"        ; other chars, no leading space
  104.          )
  105.        "\\|")
  106.       "\\)[ \t]*")
  107.   "*The pattern to match cited text.
  108. Text in the body of a message which matches this will be displayed in
  109. the face `message-cited-text'.")
  110.  
  111. (defvar highlight-headers-citation-header-regexp
  112.   (concat "^In article\\|^In message\\|"
  113.       "^[^ \t].*\\(writes\\|wrote\\|said\\):\n"
  114.       (substring highlight-headers-citation-regexp 1))
  115.   "*The pattern to match the prolog of a cited block.
  116. Text in the body of a message which matches this will be displayed in
  117. the `message-headers' face.")
  118.  
  119. (defvar highlight-headers-highlight-citation-too nil
  120.   "*Whether the whole citation line should go in the `mesage-cited-text' face.
  121. If nil, the text matched by `highlight-headers-citation-regexp' is in the
  122. default face, and the remainder of the line is in the message-cited-text face.")
  123.  
  124. (defvar highlight-headers-max-message-size 10000
  125.   "*If the message body is larger than this many chars, don't highlight it.
  126. This is to prevent us from wasting time trying to fontify things like
  127. uuencoded files and large digests.  If this is nil, all messages will
  128. be highlighted.")
  129.  
  130. (defvar highlight-headers-hack-x-face-p (featurep 'xface)
  131.   "*If true, then the bitmap in an X-Face header will be displayed
  132. in the buffer.  This assumes you have the `uncompface' and `icontopbm'
  133. programs on your path.")
  134.  
  135. (defvar highlight-headers-convert-quietly nil
  136.   "*Non-nil inhibits the message that is normally displayed when external
  137. filters are used to convert an X-Face header.  This has no affect if
  138. XEmacs is compiled with internal support for x-faces.")
  139.  
  140. (defvar highlight-headers-invert-x-face-data nil 
  141.   "*If true, causes the foreground and background bits in an X-Face
  142. header to be flipped before the image is displayed. If you use a
  143. light foreground color on a dark background color, you probably want
  144. to set this to t. This assumes that you have the `pnminvert' program
  145. on your path.  This doesn't presently work with internal xface support.")
  146.  
  147.  
  148. ;;;###autoload
  149. (defun highlight-headers (start end hack-sig)
  150.   "Highlight message headers between start and end.
  151. Faces used:
  152.   message-headers            the part before the colon
  153.   message-header-contents        the part after the colon
  154.   message-highlighted-header-contents    contents of \"special\" headers
  155.   message-cited-text            quoted text from other messages
  156.  
  157. Variables used:
  158.  
  159.   highlight-headers-regexp            what makes a \"special\" header
  160.   highlight-headers-citation-regexp        matches lines of quoted text
  161.   highlight-headers-citation-header-regexp    matches headers for quoted text
  162.  
  163. If HACK-SIG is true,then we search backward from END for something that
  164. looks like the beginning of a signature block, and don't consider that a
  165. part of the message (this is because signatures are often incorrectly
  166. interpreted as cited text.)"
  167.   (if (< end start)
  168.       (let ((s start)) (setq start end end s)))
  169.   (let* ((too-big (and highlight-headers-max-message-size
  170.                (> (- end start)
  171.               highlight-headers-max-message-size)))
  172.      (real-end end)
  173.      e p hend)
  174.     ;; delete previous highlighting
  175.     (map-extents (function (lambda (extent ignore)
  176.                  (if (extent-property extent 'headers)
  177.                  (delete-extent extent))
  178.                  nil))
  179.          (current-buffer) start end)
  180.     (save-excursion
  181.       (save-restriction
  182.     (widen)
  183.     ;; take off signature
  184.     (if (and hack-sig (not too-big))
  185.         (save-excursion
  186.           (goto-char end)
  187.           (if (re-search-backward "\n--+ *\n" start t)
  188.           (if (eq (char-after (point)) ?\n)
  189.               (setq end (1+ (point)))
  190.             (setq end (point))))))
  191.     (narrow-to-region start end)
  192.  
  193.     (save-restriction
  194.       ;; narrow down to just the headers...
  195.       (goto-char start)
  196.       ;; If this search fails then the narrowing performed above
  197.       ;; is sufficient
  198.       (if (re-search-forward "^$" nil t)
  199.           (narrow-to-region (point-min) (point)))
  200.       (goto-char start)
  201.       (while (not (eobp))
  202.         (cond
  203.          ((looking-at "^\\([^ \t\n:]+[ \t]*:\\) *\\(.*\\(\n[ \t].*\\)*\n\\)")
  204.           (setq hend (match-end 0))
  205.           (setq e (make-extent (match-beginning 1) (match-end 1)))
  206.           (set-extent-face e 'message-headers)
  207.           (set-extent-property e 'headers t)
  208.           (setq p (match-end 1))
  209.           (cond
  210.            ((and highlight-headers-hack-x-face-p
  211.              (save-match-data (looking-at "^X-Face: *")))
  212.         ;; make the whole header invisible
  213.         (setq e (make-extent (match-beginning 0) (match-end 0)))
  214.         (set-extent-property e 'invisible t)
  215.         (set-extent-property e 'headers t)
  216.         ;; now extract the xface and put it somewhere interesting
  217.         (let ((xface (highlight-headers-x-face-to-pixmap
  218.                   (match-beginning 2)
  219.                   (match-end 2))))
  220.           (if (not xface)
  221.               nil        ; just leave the header invisible if
  222.                     ; we can't convert the face for some
  223.                     ; reason 
  224.             (cond ((save-excursion
  225.                  (goto-char (point-min))
  226.                  (save-excursion (re-search-forward "^From: *"
  227.                                 nil t)))
  228.                (setq e (make-extent (match-end 0)
  229.                         (match-end 0))))
  230.               (t
  231.                ;; okay, make the beginning of the the invisible
  232.                ;; move forward to only hide the modem noise...
  233.                (set-extent-endpoints e
  234.                          (match-beginning 2)
  235.                          (1- (match-end 2)))
  236.                ;; kludge: if a zero-length extent exists at the
  237.                ;; starting point of an invisible extent, then
  238.                ;; it's invisible... even if the invisible extent
  239.                ;; is start-open.  
  240.                (setq e (make-extent (1- (match-beginning 2))
  241.                         (match-beginning 2)))
  242.                ))
  243.             (set-extent-property e 'headers t)
  244.             (set-extent-end-glyph e xface))
  245.           ))
  246. ;;; I don't think this is worth the effort
  247. ;;;           ((looking-at "\\(From\\|Resent-From\\)[ \t]*:")
  248. ;;;            (setq current 'message-highlighted-header-contents)
  249. ;;;            (goto-char (match-end 0))
  250. ;;;            (or (looking-at ".*(\\(.*\\))")
  251. ;;;                (looking-at "\\(.*\\)<")
  252. ;;;                (looking-at "\\(.*\\)[@%]")
  253. ;;;                (looking-at "\\(.*\\)"))
  254. ;;;            (end-of-line)
  255. ;;;            (setq e (make-extent p (match-beginning 1)))
  256. ;;;            (set-extent-face e current)
  257. ;;;            (set-extent-property e 'headers t)
  258. ;;;            (setq e (make-extent (match-beginning 1) (match-end 1)))
  259. ;;;            (set-extent-face e 'message-addresses)
  260. ;;;            (set-extent-property e 'headers t)
  261. ;;;            (setq e (make-extent (match-end 1) (point)))
  262. ;;;            (set-extent-face e current)
  263. ;;;            (set-extent-property e 'headers t)
  264. ;;;            )
  265.            ((and highlight-headers-regexp
  266.              (save-match-data (looking-at highlight-headers-regexp)))
  267.         (setq e (make-extent (match-beginning 2) (match-end 2)))
  268.         (set-extent-face e 'message-highlighted-header-contents)
  269.         (set-extent-property e 'headers t))
  270.            (t
  271.         (setq e (make-extent (match-beginning 2) (match-end 2)))
  272.         (set-extent-face e 'message-header-contents)
  273.         (set-extent-property e 'headers t))
  274.             )
  275.            (goto-char hend))
  276.           ;; ignore non-header field name lines
  277.           (t (forward-line 1)))))
  278.  
  279.     ;; now do the body, unless it's too big....
  280.     (if too-big
  281.         nil
  282.       (while (not (eobp))
  283.         (cond ((null highlight-headers-citation-regexp)
  284.            nil)
  285.           ((looking-at highlight-headers-citation-regexp)
  286.            (or highlight-headers-highlight-citation-too
  287.                (goto-char (match-end 0)))
  288.            (or (save-excursion
  289.              (beginning-of-line)
  290.              (let ((case-fold-search nil)) ; aaaaah, unix...
  291.                (looking-at "^>From ")))
  292.                (setq current 'message-cited-text)))
  293. ;;;                ((or (looking-at "^In article\\|^In message")
  294. ;;;                     (looking-at
  295. ;;;            "^[^ \t].*\\(writes\\|wrote\\|said\\):\n[ \t]+[A-Z]*[]}<>|]"))
  296. ;;;                 (setq current 'message-headers))
  297.           ((null highlight-headers-citation-header-regexp)
  298.            nil)
  299.           ((looking-at highlight-headers-citation-header-regexp)
  300.            (setq current 'message-headers))
  301.           (t (setq current nil)))
  302.         (cond (current
  303.            (setq p (point))
  304.            (forward-line 1) ; this is to put the \n in the face too
  305.            (setq e (make-extent p (point)))
  306.            (forward-char -1)
  307.            (set-extent-face e current)
  308.            (set-extent-property e 'headers t)
  309.            ))
  310.         (forward-line 1)))
  311.     ))
  312.     (save-excursion
  313.       (save-restriction
  314.     (widen)
  315.     (narrow-to-region start real-end)
  316.     (highlight-headers-mark-urls start real-end)))
  317.     ))
  318.  
  319.  
  320. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  321. ;;;
  322. ;;; X-Face header conversion:
  323.  
  324. ;;; This cache is only used if x-face conversion is done with external
  325. ;;; filters.  If XEmacs is compiled --with-xface, then it's better to
  326. ;;; convert it twice than to suck up memory for a potentially large cache of
  327. ;;; stuff that's not difficult to recreate.
  328. (defvar highlight-headers-x-face-to-pixmap-cache nil)
  329.  
  330. (defun highlight-headers-x-face-to-pixmap (start end)
  331.   (let* ((string (if (stringp start) start (buffer-substring start end)))
  332.      (data (assoc string highlight-headers-x-face-to-pixmap-cache)))
  333.     (if (featurep 'xface)
  334.     (let ((new-face (make-glyph (concat "X-Face: " string))))
  335.       (set-glyph-face new-face 'x-face)
  336.       new-face)
  337.       ;; YUCK this is the old two-external-filters-plus-a-bunch-of-lisp method
  338.       (if data
  339.       (cdr data)
  340.     (setq data (cons string
  341.              (condition-case c
  342.                  (highlight-headers-parse-x-face-data start end)
  343.                (error
  344.                 (display-error c nil)
  345.                 (sit-for 2)
  346.                 nil))))
  347.     (setq highlight-headers-x-face-to-pixmap-cache
  348.           (cons data highlight-headers-x-face-to-pixmap-cache))
  349.     (cdr data)))
  350.     ))
  351.  
  352. ;;; Kludge kludge kludge for displaying the bitmap in the X-Face header.
  353.  
  354. ;;; This depends on the following programs: icontopbm, from the pbmplus
  355. ;;; toolkit (available everywhere) and uncompface, which comes with
  356. ;;; several faces-related packages, and can also be had at ftp.clark.net
  357. ;;; in /pub/liebman/compface.tar.Z.  See also xfaces 3.*.  Not needed
  358. ;;; for this, but a very nice xbiff replacment.
  359.  
  360. (defconst highlight-headers-x-face-bitrev
  361.   (purecopy
  362.    (eval-when-compile
  363.      (let* ((v (make-string 256 0))
  364.         (i (1- (length v))))
  365.        (while (>= i 0)
  366.      (let ((j 7)
  367.            (k 0))
  368.        (while (>= j 0)
  369.          (if (/= 0 (logand i (lsh 1 (- 7 j))))
  370.          (setq k (logior k (lsh 1 j))))
  371.          (setq j (1- j)))
  372.        (aset v i k))
  373.      (setq i (1- i)))
  374.        v))))
  375.  
  376. (defun highlight-headers-parse-x-face-data (start end)
  377.   (save-excursion
  378.     (let ((b (current-buffer))
  379.       (lines 0)
  380.       p)
  381.       (or highlight-headers-convert-quietly
  382.       (message "Converting X-Face header to pixmap ..."))
  383.       (set-buffer (get-buffer-create " *x-face-tmp*"))
  384.       (buffer-disable-undo (current-buffer))
  385.       (erase-buffer)
  386.       (if (stringp start)
  387.       (insert start)
  388.     (insert-buffer-substring b start end))
  389.       (while (search-forward "\n" nil t)
  390.     (skip-chars-backward " \t\n")
  391.     (setq p (point))
  392.     (skip-chars-forward " \t\n")
  393.     (delete-region p (point)))
  394.       (call-process-region (point-min) (point-max) "uncompface" t t nil)
  395.       (goto-char (point-min))
  396.       (while (not (eobp))
  397.     (or (looking-at "0x....,0x....,0x...., *$")
  398.         (error "unexpected uncompface output"))
  399.     (forward-line 1)
  400.     (setq lines (1+ lines))
  401.     (delete-char -1))
  402.       (goto-char (point-min))
  403.       (insert (format "/* Format_version=1, Width=%d, Height=%d" lines lines))
  404.       (insert ", Depth=1, Valid_bits_per_item=16\n */\n")
  405.       (while (not (eobp))
  406.     (insert ?\t)
  407.     (forward-char 56) ; 7 groups per line
  408.     (insert ?\n))
  409.       (forward-char -1)
  410.       (delete-char -1)  ; take off last comma
  411.       ;;
  412.       ;; Ok, now we've got the format that "icontopbm" knows about.
  413.       (call-process-region (point-min) (point-max) "icontopbm" t t nil)
  414.       ;; Invert the image if the user wants us to...
  415.       (if highlight-headers-invert-x-face-data
  416.       (call-process-region (point-min) (point-max) "pnminvert" t t nil))
  417.       ;;
  418.       ;; If PBM is using binary mode, we're winning.
  419.       (goto-char (point-min))
  420.       (let ((new-face))
  421.     (cond ((looking-at "P4\n")
  422.            (forward-line 2)
  423.            (delete-region (point-min) (point))
  424.            (while (not (eobp))
  425.          (insert (aref highlight-headers-x-face-bitrev
  426.                    (following-char)))
  427.          (delete-char 1))
  428.            (setq new-face (make-glyph
  429.                    (vector 'xbm :data
  430.                        (list lines lines (prog1 (buffer-string)
  431.                                (erase-buffer))))))
  432.            (set-glyph-image new-face "[xface]" 'global 'tty)
  433.            (set-glyph-face new-face 'x-face))
  434.           (t ; fix me
  435.            (error "I only understand binary-format PBM...")))
  436.     (or highlight-headers-convert-quietly
  437.         (message "Converting X-Face header to pixmap ... done."))
  438.     new-face)
  439.       )))
  440.  
  441.  
  442. ;;; "The Internet's new BBS!" -Boardwatch Magazine
  443. ;;; URL support by jwz@netscape.com
  444.  
  445. (defvar highlight-headers-mark-urls (string-match "XEmacs" emacs-version)
  446.   "*Whether to make URLs clickable in message bodies.")
  447.  
  448. (defvar highlight-headers-follow-url-function 'w3-fetch
  449.   "The function to invoke to follow a URL.
  450. Possible values that work out of the box are:
  451.  
  452. 'w3-fetch                                == Use emacs-w3
  453. 'highlight-headers-follow-url-netscape   == Use Netscape 1.1
  454. 'highlight-headers-follow-url-mosaic     == Use Mosaic")
  455.  
  456. (defun highlight-headers-follow-url-netscape (url)
  457.   (message "Sending URL to Netscape...")
  458.   (save-excursion
  459.     (set-buffer (get-buffer-create "*Shell Command Output*"))
  460.     (erase-buffer)
  461.     (if (equal 0 (call-process "netscape" nil t nil
  462.                    "-remote"
  463.                    (concat "openURL(" url ")")))
  464.     ;; it worked
  465.     nil
  466.       ;; it didn't work, so start a new Netscape process.
  467.       (call-process "netscape" nil 0 nil url)))
  468.   (message "Sending URL to Netscape... done"))
  469.  
  470. (defun highlight-headers-follow-url-mosaic (url)
  471.   (message "Sending URL to Mosaic...")
  472.   (let ((pid-file "~/.mosaicpid")
  473.     (work-buffer " *mosaic work*")
  474.     (pid nil))
  475.     (cond ((file-readable-p pid-file)
  476.        (set-buffer (get-buffer-create work-buffer))
  477.        (erase-buffer)
  478.        (insert-file-contents pid-file)
  479.        (setq pid (int-to-string (string-to-int (buffer-string))))
  480.        (erase-buffer)
  481.        (insert "goto" ?\n)
  482.        (insert url ?\n)
  483.        (write-region (point-min) (point-max)
  484.              (concat "/tmp/Mosaic." pid)
  485.              nil 0)
  486.        (set-buffer-modified-p nil)
  487.        (kill-buffer work-buffer)))
  488.     (cond ((or (null pid)
  489.            (not (equal 0 (call-process "kill" nil nil nil "-USR1" pid))))
  490.        (call-process "Mosaic" nil 0 nil url))))
  491.   (message "Sending URL to Mosaic... done"))
  492.  
  493. (defvar highlight-headers-url-keymap
  494.   (let ((m (make-sparse-keymap)))
  495.     (set-keymap-name m 'highlight-headers-url-keymap)
  496.     (if (string-match "XEmacs" emacs-version)
  497.     (progn
  498.       (define-key m 'button2 'highlight-headers-follow-url)
  499.       ))
  500.     m))
  501.  
  502. (defun highlight-headers-follow-url (event)
  503.   (interactive "e")
  504.   (let* ((p (event-point event))
  505.      (buffer (window-buffer (event-window event)))
  506.      (extent (and p (extent-at p buffer 'highlight)))
  507.      (url (and extent
  508.            (save-excursion
  509.              (set-buffer buffer)
  510.              (buffer-substring (extent-start-position extent)
  511.                        (extent-end-position extent))))))
  512.     (if url
  513.     (funcall highlight-headers-follow-url-function url)
  514.       (beep))))
  515.  
  516.  
  517. (defconst highlight-headers-url-pattern
  518.   (concat "\\b\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\):"
  519.       "\\(//[-a-zA-Z0-9_.]+:[0-9]*\\)?"
  520.       "[-a-zA-Z0-9_=?#$@~`%&*+|\\/.,]+"
  521.       ))
  522.  
  523. (defun highlight-headers-mark-urls (start end)
  524.   (cond
  525.    (highlight-headers-mark-urls
  526.     (save-excursion
  527.       (goto-char start)
  528.       (while (re-search-forward highlight-headers-url-pattern nil t)
  529.     (let ((s (match-beginning 0))
  530.           e
  531.           extent)
  532.       (goto-char (match-end 0))
  533.       ;(skip-chars-forward "^ \t\n\r")
  534.       (skip-chars-backward ".?#!*()")
  535.       (setq e (point))
  536.       (setq extent (make-extent s e))
  537.       (set-extent-face extent 'bold)
  538.       (set-extent-property extent 'highlight t)
  539.       (set-extent-property extent 'headers t)
  540.       (set-extent-property extent 'keymap highlight-headers-url-keymap)
  541.       ))))))
  542.  
  543.  
  544. (provide 'highlight-headers)
  545.