Sync up with Pterodactyl Gnus v0.91, etc. See ChangeLog for more details.
[elisp/gnus.git-] / lisp / gnus-sum.el
index 3f60b0e..cc31531 100644 (file)
@@ -820,7 +820,7 @@ which it may alter in any way.")
     ("^cn\\>\\|\\<chinese\\>" cn-gb-2312)
     ("^fj\\>\\|^japan\\>" iso-2022-jp-2)
     ("^relcom\\>" koi8-r)
-    ("^\\(cz\\|hun\\|pl\\|sk\\)\\>" iso-8859-2)
+    ("^\\(cz\\|hun\\|pl\\|sk\\|hr\\)\\>" iso-8859-2)
     ("^israel\\>" iso-8859-1)
     ("^han\\>" euc-kr)
     ("^\\(comp\\|rec\\|alt\\|sci\\|soc\\|news\\|gnu\\|bofh\\)\\>" iso-8859-1)
@@ -837,6 +837,26 @@ default charset will be used instead."
   :type '(repeat symbol)
   :group 'gnus-charset)
 
+(defcustom gnus-group-ignored-charsets-alist 
+  '(("alt\\.chinese\\.text" iso-8859-1))
+  "Alist of regexps (to match group names) and charsets that should be ignored.
+When these charsets are used in the \"charset\" parameter, the
+default charset will be used instead."
+  :type '(repeat (cons (regexp :tag "Group")
+                      (repeat symbol)))
+  :group 'gnus-charset)
+
+(defcustom gnus-group-highlight-words-alist nil
+  "Alist of group regexps and highlight regexps.
+This variable uses the same syntax as `gnus-emphasis-alist'."
+  :type '(repeat (cons (regexp :tag "Group")
+                      (repeat (list (regexp :tag "Highlight regexp")
+                                    (number :tag "Group for entire word" 0)
+                                    (number :tag "Group for displayed part" 0)
+                                    (symbol :tag "Face" 
+                                            gnus-emphasis-highlight-words)))))
+  :group 'gnus-summary-visual)
+
 ;;; Internal variables
 
 (defvar gnus-scores-exclude-files nil)
@@ -1025,6 +1045,8 @@ variable (string, integer, character, etc).")
 (defvar gnus-last-article nil)
 (defvar gnus-newsgroup-history nil)
 (defvar gnus-newsgroup-charset nil)
+(defvar gnus-newsgroup-ephemeral-charset nil)
+(defvar gnus-newsgroup-ephemeral-ignored-charsets nil)
 
 (defconst gnus-summary-local-variables
   '(gnus-newsgroup-name
@@ -1046,7 +1068,8 @@ variable (string, integer, character, etc).")
     gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay
     gnus-newsgroup-scored gnus-newsgroup-kill-headers
     gnus-thread-expunge-below
-    gnus-score-alist gnus-current-score-file gnus-summary-expunge-below
+    gnus-score-alist gnus-current-score-file
+    (gnus-summary-expunge-below . global)
     (gnus-summary-mark-below . global)
     gnus-newsgroup-active gnus-scores-exclude-files
     gnus-newsgroup-history gnus-newsgroup-ancient
@@ -1285,7 +1308,7 @@ increase the score of each group you read."
     "a" gnus-summary-post-news
     "x" gnus-summary-limit-to-unread
     "s" gnus-summary-isearch-article
-    "t" gnus-article-hide-headers
+    "t" gnus-article-toggle-headers
     "g" gnus-summary-show-article
     "l" gnus-summary-goto-last-article
     "v" gnus-summary-preview-mime-message
@@ -1352,6 +1375,7 @@ increase the score of each group you read."
     "T" gnus-summary-limit-include-thread
     "d" gnus-summary-limit-exclude-dormant
     "t" gnus-summary-limit-to-age
+    "x" gnus-summary-limit-to-extra 
     "E" gnus-summary-limit-include-expunged
     "c" gnus-summary-limit-exclude-childless-dormant
     "C" gnus-summary-limit-mark-excluded-as-read)
@@ -1422,11 +1446,13 @@ increase the score of each group you read."
     "e" gnus-summary-end-of-article
     "^" gnus-summary-refer-parent-article
     "r" gnus-summary-refer-parent-article
+    "D" gnus-summary-enter-digest-group
     "R" gnus-summary-refer-references
     "T" gnus-summary-refer-thread
     "g" gnus-summary-show-article
     "s" gnus-summary-isearch-article
-    "P" gnus-summary-print-article)
+    "P" gnus-summary-print-article
+    "t" gnus-article-babel)
 
   (gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map)
     "b" gnus-article-add-buttons
@@ -1440,15 +1466,16 @@ increase the score of each group you read."
     "f" gnus-article-display-x-face
     "l" gnus-summary-stop-page-breaking
     "r" gnus-summary-caesar-message
-    "t" gnus-article-hide-headers
+    "t" gnus-article-toggle-headers
     "v" gnus-summary-verbose-headers
     "m" gnus-summary-toggle-mime
     "h" gnus-article-treat-html
+    "H" gnus-article-strip-headers-in-body
     "d" gnus-article-treat-dumbquotes)
 
   (gnus-define-keys (gnus-summary-wash-hide-map "W" gnus-summary-wash-map)
     "a" gnus-article-hide
-    "h" gnus-article-hide-headers
+    "h" gnus-article-toggle-headers
     "b" gnus-article-hide-boring-headers
     "s" gnus-article-hide-signature
     "c" gnus-article-hide-citation
@@ -1494,6 +1521,7 @@ increase the score of each group you read."
     "\M-\C-e" gnus-summary-expire-articles-now
     "\177" gnus-summary-delete-article
     [delete] gnus-summary-delete-article
+    [backspace] gnus-summary-delete-article
     "m" gnus-summary-move-article
     "r" gnus-summary-respool-article
     "w" gnus-summary-edit-article
@@ -1565,7 +1593,7 @@ increase the score of each group you read."
     (let ((innards
            '(("Hide"
               ["All" gnus-article-hide t]
-              ["Headers" gnus-article-hide-headers t]
+              ["Headers" gnus-article-toggle-headers t]
               ["Signature" gnus-article-hide-signature t]
               ["Citation" gnus-article-hide-citation t]
               ["PGP" gnus-article-hide-pgp t]
@@ -1652,6 +1680,7 @@ increase the score of each group you read."
              ("Cache"
               ["Enter article" gnus-cache-enter-article t]
               ["Remove article" gnus-cache-remove-article t])
+            ["Translate" gnus-article-babel t]
              ["Select article buffer" gnus-summary-select-article-buffer t]
              ["Enter digest buffer" gnus-summary-enter-digest-group t]
              ["Isearch article..." gnus-summary-isearch-article t]
@@ -1742,6 +1771,7 @@ increase the score of each group you read."
        ["Subject..." gnus-summary-limit-to-subject t]
        ["Author..." gnus-summary-limit-to-author t]
        ["Age..." gnus-summary-limit-to-age t]
+       ["Extra..." gnus-summary-limit-to-extra t]
        ["Score" gnus-summary-limit-to-score t]
        ["Unread" gnus-summary-limit-to-unread t]
        ["Non-dormant" gnus-summary-limit-exclude-dormant t]
@@ -2477,7 +2507,10 @@ marks of articles."
 (defun gnus-summary-from-or-to-or-newsgroups (header)
   (let ((to (cdr (assq 'To (mail-header-extra header))))
        (newsgroups (cdr (assq 'Newsgroups (mail-header-extra header))))
-       (mail-parse-charset gnus-newsgroup-charset))
+       (mail-parse-charset gnus-newsgroup-charset)
+       (mail-parse-ignored-charsets 
+        (save-excursion (set-buffer gnus-summary-buffer)
+                        gnus-newsgroup-ignored-charsets)))
     (cond
      ((and to
           gnus-ignored-from-addresses
@@ -3563,11 +3596,11 @@ If LINE, insert the rebuilt thread starting on line LINE."
 (defsubst gnus-article-sort-by-author (h1 h2)
   "Sort articles by root author."
   (string-lessp
-   (let ((addr (mime-read-field 'From h1)))
+   (let ((addr (car (mime-read-field 'From h1))))
      (or (std11-full-name-string addr)
         (std11-address-string addr)
         ""))
-   (let ((addr (mime-read-field 'From h2)))
+   (let ((addr (car (mime-read-field 'From h2))))
      (or (std11-full-name-string addr)
         (std11-address-string addr)
         ""))
@@ -4224,13 +4257,14 @@ If SELECT-ARTICLES, only select those articles from GROUP."
        (uncompressed '(score bookmark killed))
        type list newmarked symbol delta-marks)
     (when info
-      ;; Add all marks lists that are non-nil to the list of marks lists.
+      ;; Add all marks lists to the list of marks lists.
       (while (setq type (pop types))
-       (when (setq list (symbol-value
+       (setq list (symbol-value
                          (setq symbol
                                (intern (format "gnus-newsgroup-%s"
                                                (car type))))))
 
+       (when list
          ;; Get rid of the entries of the articles that have the
          ;; default score.
          (when (and (eq (cdr type) 'score)
@@ -4245,30 +4279,32 @@ If SELECT-ARTICLES, only select those articles from GROUP."
                    (setcdr prev (cdr arts))
                  (setq prev arts))
                (setq arts (cdr arts)))
-             (setq list (cdr all))))
-
-         (when (gnus-check-backend-function 'request-set-mark
-                                            gnus-newsgroup-name)
-           ;; score & bookmark are not proper flags (they are cons cells)
-           ;; cache is a internal gnus flag
-           (unless (memq (cdr type) '(cache score bookmark))
-             (let* ((old (cdr (assq (cdr type) (gnus-info-marks info))))
-                    (del (gnus-remove-from-range old list))
-                    (add (gnus-remove-from-range list old)))
-               (if add
-                   (push (list add 'add (list (cdr type))) delta-marks))
-               (if del
-                   (push (list del 'del (list (cdr type))) delta-marks)))))
-
-         (push (cons (cdr type)
-                     (if (memq (cdr type) uncompressed) list
-                       (gnus-compress-sequence
-                        (set symbol (sort list '<)) t)))
-               newmarked)))
-
-      (if delta-marks
-         (gnus-request-set-mark gnus-newsgroup-name delta-marks))
-
+             (setq list (cdr all)))))
+
+       (or (memq (cdr type) uncompressed)
+           (setq list (gnus-compress-sequence (set symbol (sort list '<)) t)))
+       
+       (when (gnus-check-backend-function 'request-set-mark
+                                          gnus-newsgroup-name)
+         ;; uncompressed:s are not proper flags (they are cons cells)
+         ;; cache is a internal gnus flag
+         (unless (memq (cdr type) (cons 'cache uncompressed))
+           (let* ((old (cdr (assq (cdr type) (gnus-info-marks info))))
+                  (del (gnus-remove-from-range (gnus-copy-sequence old) list))
+                  (add (gnus-remove-from-range (gnus-copy-sequence list) old)))
+             (if add
+                 (push (list add 'add (list (cdr type))) delta-marks))
+             (if del
+                 (push (list del 'del (list (cdr type))) delta-marks)))))
+         
+       (when list
+         (push (cons (cdr type) list) newmarked)))
+
+      (when delta-marks
+       (unless (gnus-check-group gnus-newsgroup-name)
+         (error "Can't open server for %s" gnus-newsgroup-name))
+       (gnus-request-set-mark gnus-newsgroup-name delta-marks))
+         
       ;; Enter these new marks into the info of the group.
       (if (nthcdr 3 info)
          (setcar (nthcdr 3 info) newmarked)
@@ -4495,7 +4531,12 @@ The resulting hash table is returned, or nil if no Xrefs were found."
             (save-excursion (set-buffer gnus-summary-buffer)
                             gnus-newsgroup-dependencies)))
        headers id end ref
-       (mail-parse-charset gnus-newsgroup-charset))
+       (mail-parse-charset gnus-newsgroup-charset)
+       (mail-parse-ignored-charsets
+        (or (and (gnus-buffer-live-p gnus-summary-buffer)
+                 (save-excursion (set-buffer gnus-summary-buffer)
+                                 gnus-newsgroup-ignored-charsets))
+            gnus-newsgroup-ignored-charsets)))
     (save-excursion
       (set-buffer nntp-server-buffer)
       ;; Translate all TAB characters into SPACE characters.
@@ -4658,6 +4699,7 @@ list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
   ;; NNTP servers do not include Xrefs when using XOVER.
   (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
   (let ((mail-parse-charset gnus-newsgroup-charset)
+       (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
        (cur nntp-server-buffer)
        (dependencies (or dependencies gnus-newsgroup-dependencies))
        number headers header)
@@ -4834,7 +4876,8 @@ executed with point over the summary line of the articles."
     `(let ((,articles (gnus-summary-work-articles ,arg)))
        (while ,articles
         (gnus-summary-goto-subject (car ,articles))
-        ,@forms))))
+        ,@forms
+        (pop ,articles)))))
 
 (put 'gnus-summary-iterate 'lisp-indent-function 1)
 (put 'gnus-summary-iterate 'edebug-form-spec '(form body))
@@ -6103,7 +6146,7 @@ If given a prefix, remove all limits."
   "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
 articles that are younger than AGE days."
-  (interactive "nTime in days: \nP")
+  (interactive "nLimit to articles older than (in days): \nP")
   (prog1
       (let ((data gnus-newsgroup-data)
            (cutoff (days-to-time age))
@@ -6121,6 +6164,30 @@ articles that are younger than AGE days."
        (gnus-summary-limit (nreverse articles)))
     (gnus-summary-position-point)))
 
+(defun gnus-summary-limit-to-extra (header regexp)
+  "Limit the summary buffer to articles that match an 'extra' header."
+  (interactive
+   (let ((header
+         (intern
+          (gnus-completing-read
+           (symbol-name (car gnus-extra-headers))      
+           "Score extra header:"       
+           (mapcar (lambda (x) 
+                     (cons (symbol-name x) x))
+                   gnus-extra-headers)
+           nil                 
+           t))))
+     (list header
+          (read-string (format "Limit to header %s (regexp): " header)))))
+  (when (not (equal "" regexp))
+    (prog1
+       (let ((articles (gnus-summary-find-matching
+                        (cons 'extra header) regexp 'all)))
+         (unless articles
+           (error "Found no matches for \"%s\"" regexp))
+         (gnus-summary-limit articles))
+      (gnus-summary-position-point))))
+
 (defalias 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
 (make-obsolete
  'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
@@ -6660,11 +6727,14 @@ to guess what the document format is."
        (delete-matching-lines "^\\(Path\\):\\|^From ")
        (widen))
       (unwind-protect
-          (if (gnus-group-read-ephemeral-group
-               name `(nndoc ,name (nndoc-address ,(get-buffer dig))
-                            (nndoc-article-type
-                             ,(if force 'digest 'guess))) t)
-              ;; Make all postings to this group go to the parent group.
+          (if (let ((gnus-newsgroup-ephemeral-charset gnus-newsgroup-charset)
+                   (gnus-newsgroup-ephemeral-ignored-charsets
+                    gnus-newsgroup-ignored-charsets))
+               (gnus-group-read-ephemeral-group
+                name `(nndoc ,name (nndoc-address ,(get-buffer dig))
+                             (nndoc-article-type
+                              ,(if force 'digest 'guess))) t))
+             ;; Make all postings to this group go to the parent group.
               (nconc (gnus-info-params (gnus-get-info name))
                      params)
             ;; Couldn't select this doc group.
@@ -6836,11 +6906,18 @@ in the comparisons."
   (let ((data (if (eq backward 'all) gnus-newsgroup-data
                (gnus-data-find-list
                 (gnus-summary-article-number) (gnus-data-list backward))))
-       (func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))
        (case-fold-search (not not-case-fold))
-       articles d)
-    (unless (fboundp (intern (concat "mail-header-" header)))
-      (error "%s is not a valid header" header))
+       articles d func)
+    (if (consp header)
+       (if (eq (car header) 'extra)
+           (setq func
+                 `(lambda (h)
+                    (or (cdr (assq ',(cdr header) (mail-header-extra h)))
+                        "")))
+         (error "%s is an invalid header" header))
+      (unless (fboundp (intern (concat "mail-header-" header)))
+       (error "%s is not a valid header" header))
+      (setq func `(lambda (h) (,(intern (concat "mail-header-" header)) h))))
     (while data
       (setq d (car data))
       (and (or (not unread)            ; We want all articles...
@@ -6989,7 +7066,7 @@ If ARG is a negative number, hide the unwanted header lines."
             (inhibit-point-motion-hooks t)
             hidden e)
        (save-restriction 
-         (message-narrow-to-head)
+         (article-narrow-to-head)
          (setq hidden (gnus-article-hidden-text-p 'headers)))
        (goto-char (point-min))
        (when (search-forward "\n\n" nil t)
@@ -7002,6 +7079,7 @@ If ARG is a negative number, hide the unwanted header lines."
        (insert-buffer-substring gnus-original-article-buffer 1 e)
        (save-restriction
          (narrow-to-region (point-min) (point))
+         (article-decode-encoded-words)
          (if (or hidden
                  (and (numberp arg) (< arg 0)))
              (let ((gnus-treat-hide-headers nil)
@@ -7088,6 +7166,9 @@ and `request-accept' functions."
                 (crosspost "Crosspost" "Crossposting")))
        (copy-buf (save-excursion
                    (nnheader-set-temp-buffer " *copy article*")))
+       (default-marks gnus-article-mark-lists)
+       (no-expire-marks (delete '(expirable . expire)
+                                (copy-sequence gnus-article-mark-lists)))
        art-group to-method new-xref article to-groups)
     (unless (assq action names)
       (error "Unknown action %s" action))
@@ -7180,7 +7261,8 @@ and `request-accept' functions."
               (entry
                (gnus-gethash pto-group gnus-newsrc-hashtb))
               (info (nth 2 entry))
-              (to-group (gnus-info-group info)))
+               (to-group (gnus-info-group info))
+              to-marks)
          ;; Update the group that has been moved to.
          (when (and info
                     (memq action '(move copy)))
@@ -7188,15 +7270,16 @@ and `request-accept' functions."
              (push to-group to-groups))
 
            (unless (memq article gnus-newsgroup-unreads)
+             (push 'read to-marks)
              (gnus-info-set-read
               info (gnus-add-to-range (gnus-info-read info)
                                       (list (cdr art-group)))))
 
            ;; Copy any marks over to the new group.
-           (let ((marks gnus-article-mark-lists)
+           (let ((marks (if (gnus-group-auto-expirable-p to-group)
+                            default-marks
+                          no-expire-marks))
                  (to-article (cdr art-group)))
-             (unless (gnus-group-auto-expirable-p to-group)
-               (setq marks (delete '(expirable . expire) marks)))
 
              ;; See whether the article is to be put in the cache.
              (when gnus-use-cache
@@ -7221,6 +7304,7 @@ and `request-accept' functions."
                (when (memq article (symbol-value
                                     (intern (format "gnus-newsgroup-%s"
                                                     (caar marks)))))
+                  (push (cdar marks) to-marks)
                  ;; If the other group is the same as this group,
                  ;; then we have to add the mark to the list.
                  (when (equal to-group gnus-newsgroup-name)
@@ -7234,6 +7318,10 @@ and `request-accept' functions."
                   to-group (cdar marks) (list to-article) info))
                (setq marks (cdr marks)))
 
+              (gnus-request-set-mark to-group (list (list (list to-article)
+                                                          'set
+                                                          to-marks)))
+
              (gnus-dribble-enter
               (concat "(gnus-group-set-info '"
                       (gnus-prin1-to-string (gnus-get-info to-group))
@@ -7401,6 +7489,8 @@ This will be the case if the article has both been mailed and posted."
        ;; There are expirable articles in this group, so we run them
        ;; through the expiry process.
        (gnus-message 6 "Expiring articles...")
+       (unless (gnus-check-group gnus-newsgroup-name)
+         (error "Can't open server for %s" gnus-newsgroup-name))
        ;; The list of articles that weren't expired is returned.
        (save-excursion
          (if expiry-wait
@@ -7480,9 +7570,10 @@ This will have permanent effect only in mail groups.
 If FORCE is non-nil, allow editing of articles even in read-only
 groups."
   (interactive "P")
-  (let ((mail-parse-charset gnus-newsgroup-charset))
-    (save-excursion
-      (set-buffer gnus-summary-buffer)
+  (save-excursion
+    (set-buffer gnus-summary-buffer)
+    (let ((mail-parse-charset gnus-newsgroup-charset)
+         (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets))
       (gnus-set-global-variables)
       (when (and (not force)
                 (gnus-group-read-only-p))
@@ -7491,7 +7582,9 @@ groups."
       (gnus-article-edit-article
        'ignore
        `(lambda (no-highlight)
-         (let ((mail-parse-charset ',gnus-newsgroup-charset))
+         (let ((mail-parse-charset ',gnus-newsgroup-charset)
+               (mail-parse-ignored-charsets
+                ',gnus-newsgroup-ignored-charsets))
            (gnus-summary-edit-article-done
             ,(or (mail-header-references gnus-current-headers) "")
             ,(gnus-group-read-only-p) ,gnus-summary-buffer no-highlight)))))))
@@ -8753,17 +8846,14 @@ save those articles instead."
                           split-name))
                    ((consp result)
                     (setq split-name (append result split-name)))))))))
-    split-name))
+    (nreverse split-name)))
 
 (defun gnus-valid-move-group-p (group)
   (and (boundp group)
        (symbol-name group)
        (symbol-value group)
-       (memq 'respool
-            (assoc (symbol-name
-                    (car (gnus-find-method-for-group
-                          (symbol-name group))))
-                   gnus-valid-select-methods))))
+       (gnus-get-function (gnus-find-method-for-group
+                          (symbol-name group)) 'request-accept-article t)))
 
 (defun gnus-read-move-group-name (prompt default articles prefix)
   "Read a group name."
@@ -8814,6 +8904,41 @@ save those articles instead."
          (error "No such group: %s" to-newsgroup)))
     to-newsgroup))
 
+(defun gnus-summary-save-parts (type dir n reverse)
+  "Save parts matching TYPE to DIR.
+If REVERSE, save parts that do not match TYPE."
+  (interactive
+   (list (read-string "Save parts of type: " "image/.*")
+        (read-file-name "Save to directory: " t nil t)
+        current-prefix-arg))
+  (gnus-summary-iterate n
+    (let ((gnus-display-mime-function nil)
+         (gnus-inhibit-treatment t))
+      (gnus-summary-select-article))
+    (save-excursion
+      (set-buffer gnus-article-buffer)
+      (let ((handles (or (mm-dissect-buffer) (mm-uu-dissect))))
+       (when handles
+         (gnus-summary-save-parts-1 type dir handles reverse)
+         (mm-destroy-parts handles))))))
+
+(defun gnus-summary-save-parts-1 (type dir handle reverse)
+  (if (stringp (car handle))
+      (mapcar (lambda (h) (gnus-summary-save-parts-1 type dir h reverse))
+             (cdr handle))
+    (when (if reverse
+             (not (string-match type (car (mm-handle-type handle))))
+           (string-match type (car (mm-handle-type handle))))
+      (let ((file (expand-file-name
+                  (file-name-nondirectory
+                   (or
+                    (mail-content-type-get
+                     (mm-handle-disposition handle) 'filename)
+                    (concat gnus-newsgroup-name "." gnus-current-article)))
+                  dir)))
+       (unless (file-exists-p file)
+         (mm-save-part-to-file handle file))))))
+
 ;; Summary extract commands
 
 (defun gnus-summary-insert-pseudos (pslist &optional not-view)
@@ -9109,6 +9234,8 @@ save those articles instead."
            (let ((del (gnus-remove-from-range (gnus-info-read info) read))
                  (add (gnus-remove-from-range read (gnus-info-read info))))
              (when (or add del)
+              (unless (gnus-check-group group)
+                (error "Can't open server for %s" group))
                (gnus-request-set-mark
                 group (delq nil (list (if add (list add 'add '(read)))
                                       (if del (list del 'del '(read)))))))))
@@ -9274,10 +9401,26 @@ save those articles instead."
 
 (defun gnus-summary-setup-default-charset ()
   "Setup newsgroup default charset."
-  (let ((name (and gnus-newsgroup-name
-                  (gnus-group-real-name gnus-newsgroup-name))))
+  (let* ((name (and gnus-newsgroup-name
+                  (gnus-group-real-name gnus-newsgroup-name)))
+        (ignored-charsets 
+         (or gnus-newsgroup-ephemeral-ignored-charsets
+             (append
+              (and gnus-newsgroup-name
+                   (or (gnus-group-find-parameter gnus-newsgroup-name
+                                                  'ignored-charsets t)
+                       (let ((alist gnus-group-ignored-charsets-alist)
+                          elem (charsets nil))
+                         (while (setq elem (pop alist))
+                           (when (and name
+                                      (string-match (car elem) name))
+                             (setq alist nil
+                                   charsets (cdr elem))))
+                         charsets))))
+             gnus-newsgroup-ignored-charsets)))
     (setq gnus-newsgroup-charset
-         (or (and gnus-newsgroup-name
+         (or gnus-newsgroup-ephemeral-charset
+             (and gnus-newsgroup-name
                   (or (gnus-group-find-parameter gnus-newsgroup-name
                                                  'charset)
                       (let ((alist gnus-group-charset-alist)
@@ -9288,7 +9431,9 @@ save those articles instead."
                             (setq alist nil
                                   charset (cadr elem))))
                         charset)))
-             gnus-default-charset))))
+             gnus-default-charset))
+    (set (make-local-variable 'gnus-newsgroup-ignored-charsets) 
+        ignored-charsets)))
 
 ;;;
 ;;; Mime Commands
@@ -9365,6 +9510,83 @@ Then replace the article with the result."
 (put 'gnus-with-article 'lisp-indent-function 1)
 (put 'gnus-with-article 'edebug-form-spec '(form body))
 
+;;;
+;;; Generic summary marking commands
+;;;
+
+(defvar gnus-summary-marking-alist
+  '((read gnus-del-mark "d")
+    (unread gnus-unread-mark "u")
+    (ticked gnus-ticked-mark "!")
+    (dormant gnus-dormant-mark "?")
+    (expirable gnus-expirable-mark "e"))
+  "An alist of names/marks/keystrokes.")
+
+(defvar gnus-summary-generic-mark-map (make-sparse-keymap))
+(defvar gnus-summary-mark-map)
+
+(defun gnus-summary-make-all-marking-commands ()
+  (define-key gnus-summary-mark-map "M" gnus-summary-generic-mark-map)
+  (dolist (elem gnus-summary-marking-alist)
+    (apply 'gnus-summary-make-marking-command elem)))
+
+(defun gnus-summary-make-marking-command (name mark keystroke)
+  (let ((map (make-sparse-keymap)))
+    (define-key gnus-summary-generic-mark-map keystroke map)
+    (dolist (lway `((next "next" next nil "n")
+                   (next-unread "next unread" next t "N")
+                   (prev "previous" prev nil "p")
+                   (prev-unread "previous unread" prev t "P")
+                   (nomove "" nil nil ,keystroke)))
+      (let ((func (gnus-summary-make-marking-command-1
+                  mark (car lway) lway name)))
+       (setq func (eval func))
+       (define-key map (nth 4 lway) func)))))
+      
+(defun gnus-summary-make-marking-command-1 (mark way lway name)      
+  `(defun ,(intern
+           (format "gnus-summary-put-mark-as-%s%s"
+                   name (if (eq way 'nomove)
+                            ""
+                          (concat "-" (symbol-name way)))))
+     (n)
+     ,(format
+       "Mark the current article as %s%s.
+If N, the prefix, then repeat N times.
+If N is negative, move in reverse order.
+The difference between N and the actual number of articles marked is
+returned."
+       name (cadr lway))
+     (interactive "p")
+     (gnus-summary-generic-mark n ,mark ',(nth 2 lway) ,(nth 3 lway))))
+    
+(defun gnus-summary-generic-mark (n mark move unread)
+  "Mark N articles with MARK."
+  (unless (eq major-mode 'gnus-summary-mode)
+    (error "This command can only be used in the summary buffer"))
+  (gnus-summary-show-thread)
+  (let ((nummove
+        (cond
+         ((eq move 'next) 1)
+         ((eq move 'prev) -1)
+         (t 0))))
+    (if (zerop nummove)
+       (setq n 1)
+      (when (< n 0)
+       (setq n (abs n)
+             nummove (* -1 nummove))))
+    (while (and (> n 0)
+               (gnus-summary-mark-article nil mark)
+               (zerop (gnus-summary-next-subject nummove unread t)))
+      (setq n (1- n)))
+    (when (/= 0 n)
+      (gnus-message 7 "No more %sarticles" (if mark "" "unread ")))
+    (gnus-summary-recenter)
+    (gnus-summary-position-point)
+    (gnus-set-mode-line 'summary)
+    n))
+
+(gnus-summary-make-all-marking-commands)
 
 (gnus-ems-redefine)