* nnsoup.el: Use expand-file-name throughtout.
[gnus] / lisp / gnus-art.el
index b642692..0a4a183 100644 (file)
@@ -1,5 +1,5 @@
 ;;; gnus-art.el --- article mode commands for Gnus
-;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 (eval-when-compile (require 'cl))
 
-(require 'custom)
 (require 'gnus)
 (require 'gnus-sum)
 (require 'gnus-spec)
 (require 'gnus-int)
-(require 'browse-url)
 (require 'mm-bodies)
 (require 'mail-parse)
 (require 'mm-decode)
@@ -169,8 +167,8 @@ Possible values in this list are `empty', `newsgroups', `followup-to',
              (const :tag "Followup-to identical to newsgroups." followup-to)
              (const :tag "Reply-to identical to from." reply-to)
              (const :tag "Date less than four days old." date)
-             (const :tag "Very long To header." long-to)
-             (const :tag "Multiple To headers." many-to))
+             (const :tag "Very long To and/or Cc header." long-to)
+             (const :tag "Multiple To and/or Cc headers." many-to))
   :group 'gnus-article-hiding)
 
 (defcustom gnus-signature-separator '("^-- $" "^-- *$")
@@ -200,12 +198,19 @@ regexp.  If it matches, the text in question is not a signature."
   :type 'sexp
   :group 'gnus-article-hiding)
 
+;; Fixme: This isn't the right thing for mixed graphical and and
+;; non-graphical frames in a session.
 (defcustom gnus-article-x-face-command
-  "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | display -"
+  (if (and (fboundp 'image-type-available-p)
+          (image-type-available-p 'xbm))
+      'gnus-article-display-xface
+    "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | display -")
   "*String or function to be executed to display an X-Face header.
 If it is a string, the command will be executed in a sub-shell
 asynchronously.         The compressed face will be piped to this command."
-  :type 'string                                ;Leave function case to Lisp.
+  :type '(choice string
+                (function-item gnus-article-display-xface)
+                function)
   :group 'gnus-article-washing)
 
 (defcustom gnus-article-x-face-too-ugly nil
@@ -249,6 +254,14 @@ is the face used for highlighting."
                       face))
   :group 'gnus-article-emphasis)
 
+(defcustom gnus-emphasize-whitespace-regexp "^[ \t]+\\|[ \t]*\n"
+  "A regexp to describe whitespace which should not be emphasized.
+Typical values are \"^[ \\t]+\\\\|[ \\t]*\\n\" and \"[ \\t]+\\\\|[ \\t]*\\n\".
+The former avoids underlining of leading and trailing whitespace,
+and the latter avoids underlining any whitespace at all."
+  :group 'gnus-article-emphasis
+  :type 'regexp)
+
 (defface gnus-emphasis-bold '((t (:bold t)))
   "Face used for displaying strong emphasized text (*word*)."
   :group 'gnus-article-emphasis)
@@ -1168,11 +1181,15 @@ always hide."
                              4))
                  (gnus-article-hide-header "date"))))
             ((eq elem 'long-to)
-             (let ((to (message-fetch-field "to")))
+             (let ((to (message-fetch-field "to"))
+                   (cc (message-fetch-field "cc")))
                (when (> (length to) 1024)
-                 (gnus-article-hide-header "to"))))
+                 (gnus-article-hide-header "to"))
+               (when (> (length cc) 1024)
+                 (gnus-article-hide-header "cc"))))
             ((eq elem 'many-to)
-             (let ((to-count 0))
+             (let ((to-count 0)
+                   (cc-count 0))
                (goto-char (point-min))
                (while (re-search-forward "^to:" nil t)
                  (setq to-count (1+ to-count)))
@@ -1184,7 +1201,19 @@ always hide."
                      (forward-line -1)
                      (narrow-to-region (point) (point-max))
                      (gnus-article-hide-header "to"))
-                   (setq to-count (1- to-count)))))))))))))
+                   (setq to-count (1- to-count))))
+               (goto-char (point-min))
+               (while (re-search-forward "^cc:" nil t)
+                 (setq cc-count (1+ cc-count)))
+               (when (> cc-count 1)
+                 (while (> cc-count 0)
+                   (goto-char (point-min))
+                   (save-restriction
+                     (re-search-forward "^cc:" nil nil cc-count)
+                     (forward-line -1)
+                     (narrow-to-region (point) (point-max))
+                     (gnus-article-hide-header "cc"))
+                   (setq cc-count (1- cc-count)))))))))))))
 
 (defun gnus-article-hide-header (header)
   (save-excursion
@@ -1426,7 +1455,7 @@ If PROMPT (the prefix), prompt for a coding system to use."
                             (set-buffer gnus-summary-buffer)
                           (error))
                         gnus-newsgroup-ignored-charsets))
-       ct cte ctl charset)
+       ct cte ctl charset format)
   (save-excursion
     (save-restriction
       (article-narrow-to-head)
@@ -1438,15 +1467,23 @@ If PROMPT (the prefix), prompt for a coding system to use."
                     (prompt
                      (mm-read-coding-system "Charset to decode: "))
                     (ctl
-                     (mail-content-type-get ctl 'charset))))
+                     (mail-content-type-get ctl 'charset)))
+           format (and ctl (mail-content-type-get ctl 'format)))
+      (when cte
+       (setq cte (mail-header-strip cte)))
       (if (and ctl (not (string-match "/" (car ctl)))) 
          (setq ctl nil))
       (goto-char (point-max)))
     (forward-line 1)
     (save-restriction
       (narrow-to-region (point) (point-max))
+      (when (and (eq mail-parse-charset 'gnus-decoded)
+                (eq (mm-body-7-or-8) '8bit))
+       ;; The text code could have been decoded.
+       (setq charset mail-parse-charset))
       (when (and (or (not ctl)
-                    (equal (car ctl) "text/plain")))
+                    (equal (car ctl) "text/plain"))
+                (not format)) ;; article with format will decode later.
        (mm-decode-body
         charset (and cte (intern (downcase
                                   (gnus-strip-whitespace cte))))
@@ -1470,6 +1507,19 @@ If PROMPT (the prefix), prompt for a coding system to use."
   "Translate a quoted-printable-encoded article.
 If FORCE, decode the article whether it is marked as quoted-printable
 or not."
+  (interactive (list 'force))
+  (save-excursion
+    (let ((buffer-read-only nil)
+         (type (gnus-fetch-field "content-transfer-encoding"))
+         (charset gnus-newsgroup-charset))
+      (when (or force
+               (and type (string-match "quoted-printable" (downcase type))))
+       (article-goto-body)
+       (quoted-printable-decode-region (point) (point-max) charset)))))
+
+(defun article-de-base64-unreadable (&optional force)
+  "Translate a base64 article.
+If FORCE, decode the article whether it is marked as base64 not."
   (interactive (list 'force))
   (save-excursion
     (let ((buffer-read-only nil)
@@ -1480,9 +1530,9 @@ or not."
        (article-goto-body)
        (save-restriction
          (narrow-to-region (point) (point-max))
-         (quoted-printable-decode-region (point-min) (point-max))
-         (when charset
-           (mm-decode-body charset)))))))
+         (base64-decode-region (point-min) (point-max))
+         (if (mm-coding-system-p charset)
+             (mm-decode-coding-region (point-min) (point-max) charset)))))))
 
 (eval-when-compile
   (require 'rfc1843))
@@ -1495,6 +1545,23 @@ or not."
     (let ((buffer-read-only nil))
       (rfc1843-decode-region (point-min) (point-max)))))
 
+(defun article-wash-html ()
+  "Format an html article."
+  (interactive)
+  (save-excursion
+    (let ((buffer-read-only nil)
+         (charset gnus-newsgroup-charset))
+      (article-goto-body)
+      (save-window-excursion
+       (save-restriction
+         (narrow-to-region (point) (point-max))
+         (mm-setup-w3)
+         (let ((w3-strict-width (window-width))
+               (url-standalone-mode t))
+           (condition-case var
+               (w3-region (point-min) (point-max))
+             (error))))))))
+
 (defun article-hide-list-identifiers ()
   "Remove list identifies from the Subject header.
 The `gnus-list-identifiers' variable specifies what to do."
@@ -1509,9 +1576,14 @@ The `gnus-list-identifiers' variable specifies what to do."
          (when regexp
            (goto-char (point-min))
            (when (re-search-forward
-                  (concat "^Subject: +\\(Re: +\\)?\\(" regexp " *\\)")
+                  (concat "^Subject: +\\(\\(\\(Re: +\\)?\\(" regexp 
+                          " *\\)\\)+\\(Re: +\\)?\\)")
                   nil t)
-             (delete-region (match-beginning 2) (match-end 0)))))))))
+             (let ((s (or (match-string 3) (match-string 5))))
+               (delete-region (match-beginning 1) (match-end 1))
+               (when s
+                 (goto-char (match-beginning 1))
+                 (insert s))))))))))
 
 (defun article-hide-pgp ()
   "Remove any PGP headers and signatures in the current article."
@@ -2407,17 +2479,16 @@ If variable `gnus-use-long-file-name' is non-nil, it is
                 gfunc (cdr func))
         (setq afunc func
               gfunc (intern (format "gnus-%s" func))))
-       (fset gfunc
-            (if (not (fboundp afunc))
-                nil
-              `(lambda (&optional interactive &rest args)
-                 ,(documentation afunc t)
-                 (interactive (list t))
-                 (save-excursion
-                   (set-buffer gnus-article-buffer)
-                   (if interactive
-                       (call-interactively ',afunc)
-                     (apply ',afunc args))))))))
+       (defalias gfunc
+        (if (fboundp afunc)
+          `(lambda (&optional interactive &rest args)
+             ,(documentation afunc t)
+             (interactive (list t))
+             (save-excursion
+               (set-buffer gnus-article-buffer)
+               (if interactive
+                   (call-interactively ',afunc)
+                 (apply ',afunc args))))))))
    '(article-hide-headers
      article-hide-boring-headers
      article-treat-overstrike
@@ -2426,7 +2497,9 @@ If variable `gnus-use-long-file-name' is non-nil, it is
      article-remove-cr
      article-display-x-face
      article-de-quoted-unreadable
+     article-de-base64-unreadable
      article-decode-HZ
+     article-wash-html
      article-mime-decode-quoted-printable
      article-hide-list-identifiers
      article-hide-pgp
@@ -2474,7 +2547,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
   "s" gnus-article-show-summary
   "\C-c\C-m" gnus-article-mail
   "?" gnus-article-describe-briefly
-  "e" gnus-summary-article-edit
+  "e" gnus-summary-edit-article
   "<" beginning-of-buffer
   ">" end-of-buffer
   "\C-c\C-i" gnus-info-find-node
@@ -2511,6 +2584,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is
        ["Treat overstrike" gnus-article-treat-overstrike t]
        ["Remove carriage return" gnus-article-remove-cr t]
        ["Remove quoted-unreadable" gnus-article-de-quoted-unreadable t]
+       ["Remove base64" gnus-article-de-base64-unreadable t]
+       ["Treat html" gnus-article-wash-html t]
        ["Decode HZ" gnus-article-decode-HZ t]))
 
     ;; Note "Commands" menu is defined in gnus-sum.el for consistency
@@ -2552,7 +2627,7 @@ commands:
   (make-local-variable 'gnus-article-mime-handles)
   (make-local-variable 'gnus-article-decoded-p)
   (make-local-variable 'gnus-article-mime-handle-alist)
-  (make-local-variable 'gnus-article-washed-types)
+  (make-local-variable 'gnus-article-wash-types)
   (gnus-set-default-directory)
   (buffer-disable-undo)
   (setq buffer-read-only t)
@@ -2588,9 +2663,8 @@ commands:
     (if (get-buffer name)
        (save-excursion
          (set-buffer name)
-         (if gnus-article-mime-handles
-             (mm-destroy-parts gnus-article-mime-handles))
-         (kill-all-local-variables)
+         (when gnus-article-mime-handles
+           (mm-destroy-parts gnus-article-mime-handles))
          (buffer-disable-undo)
          (setq buffer-read-only t)
          (unless (eq major-mode 'gnus-article-mode)
@@ -2657,8 +2731,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
                      (message "Message marked for downloading"))
                  (gnus-summary-mark-article article gnus-canceled-mark)
                  (unless (memq article gnus-newsgroup-sparse)
-                   (gnus-error 1
-                               "No such article (may have expired or been canceled)")))))
+                   (gnus-error 1 "No such article (may have expired or been canceled)")))))
          (if (or (eq result 'pseudo)
                  (eq result 'nneething))
              (progn
@@ -2805,7 +2878,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
                                           (cons (caddr c) (car c)))
                                         gnus-mime-button-commands))))))
        (if response
-           (funcall response))))))
+           (call-interactively response))))))
 
 (defun gnus-mime-view-all-parts (&optional handles)
   "View all the MIME parts."
@@ -2842,14 +2915,33 @@ If ALL-HEADERS is non-nil, no headers are hidden."
   (let ((data (get-text-property (point) 'gnus-data)))
     (mm-interactively-view-part data)))
 
-(defun gnus-mime-view-part-as-type ()
+(defun gnus-mime-view-part-as-type-internal ()
+  (gnus-article-check-buffer)
+  (let* ((name (mail-content-type-get
+               (mm-handle-type (get-text-property (point) 'gnus-data))
+               'name))
+        (def-type (and name (mm-default-file-encoding name))))
+    (and def-type (cons def-type 0))))
+
+(defun gnus-mime-view-part-as-type (mime-type)
   "Choose a MIME media type, and view the part as such."
   (interactive
-   (list (completing-read "View as MIME type: "
-                         (mapcar 'list (mailcap-mime-types)))))
+   (list (completing-read
+         "View as MIME type: "
+         (mapcar (lambda (i) (list i i)) (mailcap-mime-types))
+         nil nil
+         (gnus-mime-view-part-as-type-internal))))
   (gnus-article-check-buffer)
   (let ((handle (get-text-property (point) 'gnus-data)))
-    (gnus-mm-display-part handle)))
+    (gnus-mm-display-part
+     (mm-make-handle (mm-handle-buffer handle)
+                    (cons mime-type (cdr (mm-handle-type handle)))
+                    (mm-handle-encoding handle)
+                    (mm-handle-undisplayer handle)
+                    (mm-handle-disposition handle)
+                    (mm-handle-description handle)
+                    (mm-handle-cache handle)
+                    (mm-handle-id handle)))))
 
 (defun gnus-mime-copy-part (&optional handle)
   "Put the the MIME part under point into a new buffer."
@@ -2895,7 +2987,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
   (gnus-article-check-buffer)
   (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
         (mm-user-display-methods nil)
-        (mm-inline-large-images nil)
+        (mm-inlined-types nil)
         (mail-parse-charset gnus-newsgroup-charset)
         (mail-parse-ignored-charsets 
          (save-excursion (set-buffer gnus-summary-buffer)
@@ -3141,13 +3233,13 @@ In no internal viewer is available, use an external viewer."
            (narrow-to-region (point) (point-max))
            (gnus-treat-article nil 1 1)
            (widen)))
-       (if (not ihandles)
-           ;; Highlight the headers.
-           (save-excursion
-             (save-restriction
-               (article-goto-body)
-               (narrow-to-region (point-min) (point))
-               (gnus-treat-article 'head))))))))
+       (unless ihandles
+         ;; Highlight the headers.
+         (save-excursion
+           (save-restriction
+             (article-goto-body)
+             (narrow-to-region (point-min) (point))
+             (gnus-treat-article 'head))))))))
 
 (defvar gnus-mime-display-multipart-as-mixed nil)
 
@@ -3211,11 +3303,11 @@ In no internal viewer is available, use an external viewer."
          (push (cons id handle) gnus-article-mime-handle-alist)
          (when (or (not display)
                    (not (gnus-unbuttonized-mime-type-p type)))
-           (gnus-article-insert-newline)
+           ;(gnus-article-insert-newline)
            (gnus-insert-mime-button
             handle id (list (or display (and not-attachment text))))
            (gnus-article-insert-newline)
-           (gnus-article-insert-newline)
+           ;(gnus-article-insert-newline)
            (setq move t)))
        (let ((beg (point)))
          (cond
@@ -3373,7 +3465,7 @@ In no internal viewer is available, use an external viewer."
              (if overstrike ?o ? )
              (if emphasis ?e ? )))))
 
-(fset 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers)
+(defalias 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers)
 
 (defun gnus-article-maybe-hide-headers ()
   "Hide unwanted headers if `gnus-have-all-headers' is nil.
@@ -3585,7 +3677,8 @@ Argument LINES specifies lines to be scrolled down."
             ;; We disable the pick minor mode commands.
             (let (gnus-pick-mode)
               (setq func (lookup-key (current-local-map) keys))))
-          (if (not func)
+          (if (or (not func)
+                 (numberp func))
               (ding)
             (unless (member keys nosave-in-article)
               (set-buffer gnus-article-current-summary))
@@ -3743,7 +3836,8 @@ If given a prefix, show the hidden text instead."
                 (gnus-cache-request-article article group))
            'article)
           ;; Get the article and put into the article buffer.
-          ((or (stringp article) (numberp article))
+          ((or (stringp article)
+               (numberp article))
            (let ((gnus-override-method gnus-override-method)
                  (methods (and (stringp article) 
                                gnus-refer-article-method))
@@ -3751,11 +3845,14 @@ If given a prefix, show the hidden text instead."
                  (buffer-read-only nil))
              (setq methods
                    (if (listp methods)
-                       (delq 'current methods)
+                       methods
                      (list methods)))
-             (if (and (null gnus-override-method) methods)
-                 (setq gnus-override-method (pop methods)))
+             (when (and (null gnus-override-method)
+                        methods)
+               (setq gnus-override-method (pop methods)))
              (while (not result)
+               (when (eq gnus-override-method 'current)
+                 (setq gnus-override-method gnus-current-select-method))
                (erase-buffer)
                (gnus-kill-all-overlays)
                (let ((gnus-newsgroup-name group))
@@ -3936,7 +4033,7 @@ groups."
   "Exit the article editing without updating."
   (interactive)
   ;; We remove all&