* gnus-art.el (gnus-header-from, gnus-header-subject, gnus-header-name)
[gnus] / lisp / gnus-sum.el
index 5be74ba..f1d0a48 100644 (file)
@@ -1,7 +1,7 @@
 ;;; gnus-sum.el --- summary mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006 Free Software Foundation, Inc.
+;;   2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
@@ -15,7 +15,7 @@
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
@@ -62,6 +62,14 @@ it will be killed sometime later."
   :group 'gnus-summary-exit
   :type 'boolean)
 
+(defcustom gnus-summary-next-group-on-exit t
+  "If non-nil, go to the next unread newsgroup on summary exit.
+See `gnus-group-goto-unread'."
+  :link '(custom-manual "(gnus)Group Maneuvering")
+  :group 'gnus-summary-exit
+  :version "23.0" ;; No Gnus
+  :type 'boolean)
+
 (defcustom gnus-fetch-old-headers nil
   "*Non-nil means that Gnus will try to build threads by grabbing old headers.
 If an unread article in the group refers to an older, already
@@ -114,7 +122,7 @@ have all the sub-threads as children.
 If this variable is `adopt', Gnus will make one of the \"children\"
 the parent and mark all the step-children as such.
 If this variable is `empty', the \"children\" are printed with empty
-subject fields.         (Or rather, they will be printed with a string
+subject fields.  (Or rather, they will be printed with a string
 given by the `gnus-summary-same-subject' variable.)"
   :group 'gnus-thread
   :type '(choice (const :tag "off" nil)
@@ -308,12 +316,20 @@ equal will be included."
   :type 'boolean)
 
 (defcustom gnus-auto-select-first t
-  "*If non-nil, select the article under point.
-Which article this is is controlled by the `gnus-auto-select-subject'
-variable.
+  "If non-nil, select an article on group entry.
+An article is selected automatically when entering a group
+e.g. with \\<gnus-group-mode-map>\\[gnus-group-read-group], or via `gnus-summary-next-page' or
+`gnus-summary-catchup-and-goto-next-group'.
+
+Which article is selected is controlled by the variable
+`gnus-auto-select-subject'.
 
 If you want to prevent automatic selection of articles in some
 newsgroups, set the variable to nil in `gnus-select-group-hook'."
+  ;; Commands include...
+  ;; \\<gnus-group-mode-map>\\[gnus-group-read-group]
+  ;; \\<gnus-summary-mode-map>\\[gnus-summary-next-page]
+  ;; \\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]
   :group 'gnus-group-select
   :type '(choice (const :tag "none" nil)
                 (sexp :menu-tag "first" t)))
@@ -362,6 +378,28 @@ the first unread article."
   :group 'gnus-summary-maneuvering
   :type 'boolean)
 
+(defcustom gnus-auto-select-on-ephemeral-exit 'next-noselect
+  "What article should be selected after exiting an ephemeral group.
+Valid values include:
+
+`next'
+  Select the next article.
+`next-unread'
+  Select the next unread article.
+`next-noselect'
+  Move the cursor to the next article.  This is the default.
+`next-unread-noselect'
+  Move the cursor to the next unread article.
+
+If it has any other value or there is no next (unread) article, the
+article selected before entering to the ephemeral group will appear."
+  :version "23.0" ;; No Gnus
+  :group 'gnus-summary-maneuvering
+  :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
+                (const next) (const next-unread)
+                (const next-noselect) (const next-unread-noselect)
+                (sexp :tag "other" :value nil)))
+
 (defcustom gnus-auto-goto-ignores 'unfetched
   "*Says how to handle unfetched articles when maneuvering.
 
@@ -1052,7 +1090,11 @@ which it may alter in any way."
   :group 'gnus-summary)
 
 (defvar gnus-decode-encoded-word-function 'mail-decode-encoded-word-string
-  "Variable that says which function should be used to decode a string with encoded words.")
+  "Function used to decode a string with encoded words.")
+
+(defvar gnus-decode-encoded-address-function
+  'mail-decode-encoded-address-string
+  "Function used to decode addresses with encoded words.")
 
 (defcustom gnus-extra-headers '(To Newsgroups)
   "*Extra headers to parse."
@@ -1061,13 +1103,18 @@ which it may alter in any way."
   :type '(repeat symbol))
 
 (defcustom gnus-ignored-from-addresses
-  (and user-mail-address  
+  (and user-mail-address
        (not (string= user-mail-address ""))
        (regexp-quote user-mail-address))
-  "*Regexp of From headers that may be suppressed in favor of To headers."
+  "*From headers that may be suppressed in favor of To headers.
+This can be a regexp or a list of regexps."
   :version "21.1"
   :group 'gnus-summary
-  :type 'regexp)
+  :type '(choice regexp
+                (repeat :tag "Regexp List" regexp)))
+
+(defsubst gnus-ignored-from-addresses ()
+  (gmm-regexp-concat gnus-ignored-from-addresses))
 
 (defcustom gnus-summary-to-prefix "-> "
   "*String prefixed to the To field in the summary line when
@@ -1322,6 +1369,7 @@ the type of the variable (string, integer, character, etc).")
 (defvar gnus-newsgroup-last-mail nil)
 (defvar gnus-newsgroup-last-folder nil)
 (defvar gnus-newsgroup-last-file nil)
+(defvar gnus-newsgroup-last-directory nil)
 (defvar gnus-newsgroup-auto-expire nil)
 (defvar gnus-newsgroup-active nil)
 
@@ -1437,6 +1485,7 @@ This list will always be a subset of gnus-newsgroup-undownloaded.")
     gnus-newsgroup-begin gnus-newsgroup-end
     gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
     gnus-newsgroup-last-folder gnus-newsgroup-last-file
+    gnus-newsgroup-last-directory
     gnus-newsgroup-auto-expire gnus-newsgroup-unreads
     gnus-newsgroup-unselected gnus-newsgroup-marked
     gnus-newsgroup-spam-marked
@@ -1659,8 +1708,8 @@ See `gnus-simplify-buffer-fuzzy' for details."
    ((eq gnus-summary-gather-subject-limit 'fuzzy)
     (gnus-simplify-subject-fuzzy subject))
    ((numberp gnus-summary-gather-subject-limit)
-    (gnus-limit-string (gnus-simplify-subject-re subject)
-                      gnus-summary-gather-subject-limit))
+    (truncate-string-to-width (gnus-simplify-subject-re subject)
+                             gnus-summary-gather-subject-limit))
    (t
     subject)))
 
@@ -1846,6 +1895,7 @@ increase the score of each group you read."
   "/" gnus-summary-limit-to-subject
   "n" gnus-summary-limit-to-articles
   "b" gnus-summary-limit-to-bodies
+  "h" gnus-summary-limit-to-headers
   "w" gnus-summary-pop-limit
   "s" gnus-summary-limit-to-subject
   "a" gnus-summary-limit-to-author
@@ -1866,6 +1916,7 @@ increase the score of each group you read."
   "C" gnus-summary-limit-mark-excluded-as-read
   "o" gnus-summary-insert-old-articles
   "N" gnus-summary-insert-new-articles
+  "S" gnus-summary-limit-to-singletons
   "r" gnus-summary-limit-to-replied
   "R" gnus-summary-limit-to-recipient)
 
@@ -1908,7 +1959,8 @@ increase the score of each group you read."
 (gnus-define-keys (gnus-summary-buffer-map "Y" gnus-summary-mode-map)
   "g" gnus-summary-prepare
   "c" gnus-summary-insert-cached-articles
-  "d" gnus-summary-insert-dormant-articles)
+  "d" gnus-summary-insert-dormant-articles
+  "t" gnus-summary-insert-ticked-articles)
 
 (gnus-define-keys (gnus-summary-exit-map "Z" gnus-summary-mode-map)
   "c" gnus-summary-catchup-and-exit
@@ -1954,6 +2006,7 @@ increase the score of each group you read."
   "e" gnus-article-emphasize
   "w" gnus-article-fill-cited-article
   "Q" gnus-article-fill-long-lines
+  "L" gnus-article-toggle-truncate-lines
   "C" gnus-article-capitalize-sentences
   "c" gnus-article-remove-cr
   "q" gnus-article-de-quoted-unreadable
@@ -2073,6 +2126,7 @@ increase the score of each group you read."
   "r" gnus-summary-save-article-rmail
   "f" gnus-summary-save-article-file
   "b" gnus-summary-save-article-body-file
+  "B" gnus-summary-write-article-body-file
   "h" gnus-summary-save-article-folder
   "v" gnus-summary-save-article-vm
   "p" gnus-summary-pipe-output
@@ -2092,6 +2146,7 @@ increase the score of each group you read."
   "c" gnus-article-copy-part
   "C" gnus-article-view-part-as-charset
   "e" gnus-article-view-part-externally
+  "H" gnus-article-browse-html-article
   "E" gnus-article-encrypt-body
   "i" gnus-article-inline-part
   "|" gnus-article-pipe-part)
@@ -2241,6 +2296,7 @@ increase the score of each group you read."
               ,@(if (featurep 'xemacs) nil
                   '(:help "Encrypt the message body on disk"))]
               ["View part externally" gnus-article-view-part-externally t]
+              ["View HTML parts in browser" gnus-article-browse-html-article t]
               ["View part with charset..." gnus-article-view-part-as-charset t]
               ["Copy part" gnus-article-copy-part t]
               ["Save part..." gnus-article-save-part t]
@@ -2295,6 +2351,7 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
              ["Emphasis" gnus-article-emphasize t]
              ["Word wrap" gnus-article-fill-cited-article t]
              ["Fill long lines" gnus-article-fill-long-lines t]
+             ["Toggle truncate long lines" gnus-article-toggle-truncate-lines t]
              ["Capitalize sentences" gnus-article-capitalize-sentences t]
              ["Remove CR" gnus-article-remove-cr t]
              ["Quoted-Printable" gnus-article-de-quoted-unreadable t]
@@ -2521,9 +2578,10 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
         ["Display Predicate" gnus-summary-limit-to-display-predicate t]
         ["Unread" gnus-summary-limit-to-unread t]
         ["Unseen" gnus-summary-limit-to-unseen t]
+        ["Singletons" gnus-summary-limit-to-singletons t]
         ["Replied" gnus-summary-limit-to-replied t]
         ["Non-dormant" gnus-summary-limit-exclude-dormant t]
-        ["Next articles" gnus-summary-limit-to-articles t]
+        ["Next or process marked articles" gnus-summary-limit-to-articles t]
         ["Pop limit" gnus-summary-pop-limit t]
         ["Show dormant" gnus-summary-limit-include-dormant t]
         ["Hide childless dormant"
@@ -2604,6 +2662,7 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
         ["Regenerate" gnus-summary-prepare t]
         ["Insert cached articles" gnus-summary-insert-cached-articles t]
         ["Insert dormant articles" gnus-summary-insert-dormant-articles t]
+        ["Insert ticked articles" gnus-summary-insert-ticked-articles t]
         ["Toggle threading" gnus-summary-toggle-threads t])
        ["See old articles" gnus-summary-insert-old-articles t]
        ["See new articles" gnus-summary-insert-new-articles t]
@@ -3431,16 +3490,17 @@ buffer that was in action when the last article was fetched."
       t
     (not (cdr (gnus-data-find-list article)))))
 
-(defun gnus-make-thread-indent-array ()
-  (let ((n 200))
-    (unless (and gnus-thread-indent-array
-                (= gnus-thread-indent-level gnus-thread-indent-array-level))
-      (setq gnus-thread-indent-array (make-vector 201 "")
-           gnus-thread-indent-array-level gnus-thread-indent-level)
-      (while (>= n 0)
-       (aset gnus-thread-indent-array n
-             (make-string (* n gnus-thread-indent-level) ? ))
-       (setq n (1- n))))))
+(defun gnus-make-thread-indent-array (&optional n)
+  (when (or n
+           (progn (setq n 200) nil)
+           (null gnus-thread-indent-array)
+           (/= gnus-thread-indent-level gnus-thread-indent-array-level))
+    (setq gnus-thread-indent-array (make-vector (1+ n) "")
+         gnus-thread-indent-array-level gnus-thread-indent-level)
+    (while (>= n 0)
+      (aset gnus-thread-indent-array n
+           (make-string (* n gnus-thread-indent-level) ? ))
+      (setq n (1- n)))))
 
 (defun gnus-update-summary-mark-positions ()
   "Compute where the summary marks are to go."
@@ -3512,14 +3572,15 @@ buffer that was in action when the last article was fetched."
 
 (defun gnus-summary-from-or-to-or-newsgroups (header gnus-tmp-from)
   (let ((mail-parse-charset gnus-newsgroup-charset)
+       (ignored-from-addresses (gnus-ignored-from-addresses))
        ; Is it really necessary to do this next part for each summary line?
        ; Luckily, doesn't seem to slow things down much.
        (mail-parse-ignored-charsets
         (with-current-buffer gnus-summary-buffer
           gnus-newsgroup-ignored-charsets)))
     (or
-     (and gnus-ignored-from-addresses
-         (string-match gnus-ignored-from-addresses gnus-tmp-from)
+     (and ignored-from-addresses
+         (string-match ignored-from-addresses gnus-tmp-from)
          (let ((extra-headers (mail-header-extra header))
                to
                newsgroups)
@@ -3528,7 +3589,7 @@ buffer that was in action when the last article was fetched."
              (concat gnus-summary-to-prefix
                      (inline
                        (gnus-summary-extract-address-component
-                        (funcall gnus-decode-encoded-word-function to)))))
+                        (funcall gnus-decode-encoded-address-function to)))))
             ((setq newsgroups
                    (or
                     (cdr (assq 'Newsgroups extra-headers))
@@ -3546,6 +3607,9 @@ buffer that was in action when the last article was fetched."
                                 gnus-tmp-expirable gnus-tmp-subject-or-nil
                                 &optional gnus-tmp-dummy gnus-tmp-score
                                 gnus-tmp-process)
+  (if (>= gnus-tmp-level (length gnus-thread-indent-array))
+      (gnus-make-thread-indent-array (max (* 2 (length gnus-thread-indent-array))
+                                         gnus-tmp-level)))
   (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level))
         (gnus-tmp-lines (mail-header-lines gnus-tmp-header))
         (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0))
@@ -4066,7 +4130,7 @@ If NO-DISPLAY, don't generate a summary buffer."
     infloop))
 
 (defun gnus-make-threads ()
-  "Go through the dependency hashtb and find the roots.         Return all threads."
+  "Go through the dependency hashtb and find the roots.  Return all threads."
   (let (threads)
     (while (catch 'infloop
             (mapatoms
@@ -4276,7 +4340,7 @@ Returns HEADER if it was entered in the DEPENDENCIES.  Returns nil otherwise."
                   (error x))
                 (condition-case ()     ; from
                     (gnus-remove-odd-characters
-                     (funcall gnus-decode-encoded-word-function
+                     (funcall gnus-decode-encoded-address-function
                               (setq x (nnheader-nov-field))))
                   (error x))
                 (nnheader-nov-field)   ; date
@@ -4570,7 +4634,7 @@ If LINE, insert the rebuilt thread starting on line LINE."
     ;; First go up in this thread until we find the root.
     (setq last-id (gnus-root-id id)
          headers (message-flatten-list (gnus-id-to-thread last-id)))
-    ;; We have now found the real root of this thread. It might have
+    ;; We have now found the real root of this thread.  It might have
     ;; been gathered into some loose thread, so we have to search
     ;; through the threads to find the thread we wanted.
     (let ((threads gnus-newsgroup-threads)
@@ -4638,23 +4702,46 @@ If LINE, insert the rebuilt thread starting on line LINE."
              (1+ (point-at-eol))
            (gnus-delete-line)))))))
 
-(defun gnus-sort-threads-1 (threads func)
+(defun gnus-sort-threads-recursive (threads func)
   (sort (mapcar (lambda (thread)
                  (cons (car thread)
                        (and (cdr thread)
-                            (gnus-sort-threads-1 (cdr thread) func))))
+                            (gnus-sort-threads-recursive (cdr thread) func))))
                threads) func))
 
+(defun gnus-sort-threads-loop (threads func)
+  (let* ((superthread (cons nil threads))
+        (stack (list (cons superthread threads)))
+        remaining-threads thread)
+    (while stack
+      (setq remaining-threads (cdr (car stack)))
+      (if remaining-threads
+         (progn (setq thread (car remaining-threads))
+                (setcdr (car stack) (cdr remaining-threads))
+                (if (cdr thread)
+                    (push (cons thread (cdr thread)) stack)))
+       (setq thread (caar stack))
+       (setcdr thread (sort (cdr thread) func))
+       (pop stack)))
+    (cdr superthread)))
+
 (defun gnus-sort-threads (threads)
   "Sort THREADS."
   (if (not gnus-thread-sort-functions)
       threads
     (gnus-message 8 "Sorting threads...")
-    (let ((max-lisp-eval-depth 5000))
-      (prog1 (gnus-sort-threads-1
-        threads
-        (gnus-make-sort-function gnus-thread-sort-functions))
-        (gnus-message 8 "Sorting threads...done")))))
+    (prog1
+       (condition-case nil
+           (let ((max-lisp-eval-depth (max max-lisp-eval-depth 5000)))
+             (gnus-sort-threads-recursive
+              threads (gnus-make-sort-function gnus-thread-sort-functions)))
+         ;; Even after binding max-lisp-eval-depth, the recursive
+         ;; sorter might fail for very long threads.  In that case,
+         ;; try using a (less well-tested) non-recursive sorter.
+         (error (gnus-sort-threads-loop
+                 threads (gnus-make-sort-function
+                          gnus-thread-sort-functions))))
+      (gnus-message 8 "Sorting threads...done"))))
 
 (defun gnus-sort-articles (articles)
   "Sort ARTICLES."
@@ -5102,6 +5189,10 @@ or a straight list of headers."
                      gnus-tmp-closing-bracket ?\>)
              (setq gnus-tmp-opening-bracket ?\[
                    gnus-tmp-closing-bracket ?\]))
+           (if (>= gnus-tmp-level (length gnus-thread-indent-array))
+               (gnus-make-thread-indent-array
+                (max (* 2 (length gnus-thread-indent-array))
+                     gnus-tmp-level)))
            (setq
             gnus-tmp-indentation
             (aref gnus-thread-indent-array gnus-tmp-level)
@@ -5317,13 +5408,13 @@ If SELECT-ARTICLES, only select those articles from GROUP."
          (when (equal major-mode 'gnus-summary-mode)
            (gnus-kill-buffer (current-buffer)))
          (error "Couldn't activate group %s: %s"
-                group (gnus-status-message group))))
+                (gnus-group-decoded-name group) (gnus-status-message group))))
 
     (unless (gnus-request-group group t)
       (when (equal major-mode 'gnus-summary-mode)
        (gnus-kill-buffer (current-buffer)))
       (error "Couldn't request group %s: %s"
-            group (gnus-status-message group)))
+            (gnus-group-decoded-name group) (gnus-status-message group)))
 
     (when gnus-agent
       (gnus-agent-possibly-alter-active group (gnus-active group) info)
@@ -5446,7 +5537,8 @@ If SELECT-ARTICLES, only select those articles from GROUP."
       (setq gnus-newsgroup-auto-expire
            (gnus-group-auto-expirable-p group))
       ;; Set up the article buffer now, if necessary.
-      (unless gnus-single-article-buffer
+      (unless (and gnus-single-article-buffer
+                  (equal gnus-article-buffer "*Article*"))
        (gnus-article-setup-buffer))
       ;; First and last article in this newsgroup.
       (when gnus-newsgroup-headers
@@ -5572,9 +5664,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
                          (read-string
                           (format
                            "How many articles from %s (%s %d): "
-                           (gnus-limit-string
-                            (gnus-group-decoded-name gnus-newsgroup-name)
-                            35)
+                           (gnus-group-decoded-name gnus-newsgroup-name)
                            (if initial "max" "default")
                            number)
                           (if initial
@@ -5820,8 +5910,9 @@ If WHERE is `summary', the summary mode line format will be used."
        (let* ((mformat (symbol-value
                         (intern
                          (format "gnus-%s-mode-line-format-spec" where))))
-              (gnus-tmp-group-name (gnus-group-decoded-name
-                                    gnus-newsgroup-name))
+              (gnus-tmp-group-name (gnus-mode-string-quote
+                                    (gnus-group-decoded-name
+                                     gnus-newsgroup-name)))
               (gnus-tmp-article-number (or gnus-current-article 0))
               (gnus-tmp-unread gnus-newsgroup-unreads)
               (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads))
@@ -6033,7 +6124,7 @@ The resulting hash table is returned, or nil if no Xrefs were found."
       (let ((case-fold-search t)
            in-reply-to header p lines chars)
        (goto-char (point-min))
-       ;; Search to the beginning of the next header.  Error messages
+       ;; Search to the beginning of the next header.  Error messages
        ;; do not begin with 2 or 3.
        (while (re-search-forward "^[23][0-9]+ " nil t)
          (setq id nil
@@ -6041,7 +6132,7 @@ The resulting hash table is returned, or nil if no Xrefs were found."
          ;; This implementation of this function, with nine
          ;; search-forwards instead of the one re-search-forward and
          ;; a case (which basically was the old function) is actually
-         ;; about twice as fast, even though it looks messier.  You
+         ;; about twice as fast, even though it looks messier.  You
          ;; can't have everything, I guess.  Speed and elegance
          ;; doesn't always go hand in hand.
          (setq
@@ -6067,7 +6158,7 @@ The resulting hash table is returned, or nil if no Xrefs were found."
            (progn
              (goto-char p)
              (if (search-forward "\nfrom:" nil t)
-                 (funcall gnus-decode-encoded-word-function
+                 (funcall gnus-decode-encoded-address-function
                           (nnheader-header-value))
                "(nobody)"))
            ;; Date.
@@ -6554,7 +6645,7 @@ displayed, no centering will be performed."
        (let ((top-pos (save-excursion (forward-line (- top)) (point))))
          (if (> bottom top-pos)
              ;; Keep the second line from the top visible
-             (set-window-start window top-pos t)
+             (set-window-start window top-pos)
            ;; Try to keep the bottom line visible; if it's partially
            ;; obscured, either scroll one more line to make it fully
            ;; visible, or revert to using TOP-POS.
@@ -6870,6 +6961,7 @@ If FORCE (the prefix), also save the .newsrc file(s)."
       (gnus-group-jump-to-group group))
     (gnus-run-hooks 'gnus-summary-exit-hook)
     (unless (or quit-config
+               (not gnus-summary-next-group-on-exit)
                ;; If this group has disappeared from the summary
                ;; buffer, don't skip forwards.
                (not (string= group (gnus-group-group-name))))
@@ -6988,6 +7080,21 @@ The state which existed when entering the ephemeral is reset."
          (gnus-configure-windows (cdr quit-config) 'force))
       (gnus-configure-windows (cdr quit-config) 'force))
     (when (eq major-mode 'gnus-summary-mode)
+      (if (memq gnus-auto-select-on-ephemeral-exit '(next-noselect
+                                                    next-unread-noselect))
+         (when (zerop (cond ((eq gnus-auto-select-on-ephemeral-exit
+                                 'next-noselect)
+                             (gnus-summary-next-subject 1 nil t))
+                            ((eq gnus-auto-select-on-ephemeral-exit
+                                 'next-unread-noselect)
+                             (gnus-summary-next-subject 1 t t))))
+           ;; Hide the article buffer which displays the article different
+           ;; from the one that the cursor points to in the summary buffer.
+           (gnus-configure-windows 'summary 'force))
+       (cond ((eq gnus-auto-select-on-ephemeral-exit 'next)
+              (gnus-summary-next-subject 1))
+             ((eq gnus-auto-select-on-ephemeral-exit 'next-unread)
+              (gnus-summary-next-subject 1 t))))
       (gnus-summary-recenter)
       (gnus-summary-position-point))))
 
@@ -7387,6 +7494,9 @@ If UNREAD, only unread articles are selected.
 If SUBJECT, only articles with SUBJECT are selected.
 If BACKWARD, the previous article is selected instead of the next."
   (interactive "P")
+  ;; Make sure we are in the summary buffer.
+  (unless (eq major-mode 'gnus-summary-mode)
+    (set-buffer gnus-summary-buffer))
   (cond
    ;; Is there such an article?
    ((and (gnus-summary-search-forward unread subject backward)
@@ -7895,6 +8005,27 @@ To and Cc headers are checked.  You need to include them in
             (gnus-summary-limit articles))
       (gnus-summary-position-point))))
 
+(defun gnus-summary-limit-strange-charsets-predicate (header)
+  (let ((string (concat (mail-header-subject header)
+                       (mail-header-from header)))
+       charset found)
+    (dotimes (i (1- (length string)))
+      (setq charset (format "%s" (char-charset (aref string (1+ i)))))
+      (when (string-match "unicode\\|big\\|japanese" charset)
+       (setq found t)))
+    found))
+
+(defun gnus-summary-limit-to-predicate (predicate)
+  "Limit to articles where PREDICATE returns non-nil.
+PREDICATE will be called with the header structures of the
+articles."
+  (let ((articles nil)
+       (case-fold-search t))
+    (dolist (header gnus-newsgroup-headers)
+      (when (funcall predicate header)
+       (push (mail-header-number header) articles)))
+    (gnus-summary-limit (nreverse articles))))
+
 (defun gnus-summary-limit-to-age (age &optional younger-p)
   "Limit the summary buffer to articles that are older than (or equal) AGE days.
 If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to
@@ -8000,7 +8131,13 @@ If ALL is non-nil, limit strictly to unread articles."
           gnus-duplicate-mark gnus-souped-mark)
      'reverse)))
 
-(defun gnus-summary-limit-to-bodies (match &optional reverse)
+(defun gnus-summary-limit-to-headers (match &optional reverse)
+  "Limit the summary buffer to articles that have headers that match MATCH.
+If REVERSE (the prefix), limit to articles that don't match."
+  (interactive "sMatch headers (regexp): \nP")
+  (gnus-summary-limit-to-bodies match reverse t))
+
+(defun gnus-summary-limit-to-bodies (match &optional reverse headersp)
   "Limit the summary buffer to articles that have bodies that match MATCH.
 If REVERSE (the prefix), limit to articles that don't match."
   (interactive "sMatch body (regexp): \nP")
@@ -8021,7 +8158,9 @@ If REVERSE (the prefix), limit to articles that don't match."
        (set-buffer gnus-article-buffer)
        (article-goto-body)
        (let* ((case-fold-search t)
-              (found (re-search-forward match nil t)))
+              (found (if headersp
+                         (re-search-backward match nil t)
+                       (re-search-forward match nil t))))
          (when (or (and found
                         (not reverse))
                    (and (not found)
@@ -8032,6 +8171,30 @@ If REVERSE (the prefix), limit to articles that don't match."
       (gnus-summary-limit articles)))
   (gnus-summary-position-point))
 
+(defun gnus-summary-limit-to-singletons (&optional threadsp)
+  "Limit the summary buffer to articles that aren't part on any thread.
+If THREADSP (the prefix), limit to articles that are in threads."
+  (interactive "P")
+  (let ((articles nil)
+       thread-articles
+       threads)
+    (dolist (thread gnus-newsgroup-threads)
+      (if (stringp (car thread))
+         (dolist (thread (cdr thread))
+           (push thread threads))
+       (push thread threads)))
+    (dolist (thread threads)
+      (setq thread-articles (gnus-articles-in-thread thread))
+      (when (or (and threadsp
+                    (> (length thread-articles) 1))
+               (and (not threadsp)
+                    (= (length thread-articles) 1)))
+       (setq articles (nconc thread-articles articles))))
+    (if (not articles)
+       (message "No messages matched")
+      (gnus-summary-limit articles))
+    (gnus-summary-position-point)))
+
 (defun gnus-summary-limit-to-replied (&optional unreplied)
   "Limit the summary buffer to replied articles.
 If UNREPLIED (the prefix), limit to unreplied articles."
@@ -8125,9 +8288,17 @@ article."
   (interactive)
   (let ((gnus-verbose (max 6 gnus-verbose)))
     (if (not gnus-newsgroup-dormant)
-       (gnus-message 3 "No cached articles for this group")
+       (gnus-message 3 "No dormant articles for this group")
       (gnus-summary-goto-subjects gnus-newsgroup-dormant))))
 
+(defun gnus-summary-insert-ticked-articles ()
+  "Insert ticked articles for this group into the current buffer."
+  (interactive)
+  (let ((gnus-verbose (max 6 gnus-verbose)))
+    (if (not gnus-newsgroup-marked)
+       (gnus-message 3 "No ticked articles for this group")
+      (gnus-summary-goto-subjects gnus-newsgroup-marked))))
+
 (defun gnus-summary-limit-include-dormant ()
   "Display all the hidden articles that are marked as dormant.
 Note that this command only works on a subset of the articles currently
@@ -8334,7 +8505,7 @@ fetch-old-headers verbiage, and so on."
   ;; will really go down to a leaf article first, before slowly
   ;; working its way up towards the root.
   (when thread
-    (let* ((max-lisp-eval-depth 5000)
+    (let* ((max-lisp-eval-depth (max 5000 max-lisp-eval-depth))
           (children
           (if (cdr thread)
               (apply '+ (mapcar 'gnus-summary-limit-children
@@ -8621,10 +8792,11 @@ to guess what the document format is."
        ;; the parent article.
        (when (setq to-address (or (gnus-fetch-field "reply-to")
                                   (gnus-fetch-field "from")))
-         (setq params (append
-                       (list (cons 'to-address
-                                   (funcall gnus-decode-encoded-word-function
-                                            to-address))))))
+         (setq params
+               (append
+         &