1;;; gnus.el --- a newsreader for GNU Emacs  -*- lexical-binding:t -*-
2
3;; Copyright (C) 1987-1990, 1993-1998, 2000-2021 Free Software
4;; Foundation, Inc.
5
6;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
7;;	Lars Magne Ingebrigtsen <larsi@gnus.org>
8;; Keywords: news, mail
9;; Version: 5.13
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software: you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
25
26;;; Commentary:
27
28;;; Code:
29
30(run-hooks 'gnus-load-hook)
31
32(eval-when-compile (require 'cl-lib)
33		   (require 'subr-x))
34(require 'wid-edit)
35(require 'mm-util)
36(require 'nnheader)
37(require 'seq)
38
39;; These are defined afterwards with gnus-define-group-parameter
40(defvar gnus-ham-process-destinations)
41(defvar gnus-parameter-ham-marks-alist)
42(defvar gnus-parameter-spam-marks-alist)
43(defvar gnus-spam-autodetect)
44(defvar gnus-spam-autodetect-methods)
45(defvar gnus-spam-newsgroup-contents)
46(defvar gnus-spam-process-destinations)
47(defvar gnus-spam-resend-to)
48(defvar gnus-ham-resend-to)
49(defvar gnus-spam-process-newsgroups)
50
51
52(defgroup gnus nil
53  "The coffee-brewing, all singing, all dancing, kitchen sink newsreader."
54  :group 'news
55  :group 'mail)
56
57(defgroup gnus-start nil
58  "Starting your favorite newsreader."
59  :group 'gnus)
60
61(defgroup gnus-format nil
62  "Dealing with formatting issues."
63  :group 'gnus)
64
65(defgroup gnus-charset nil
66  "Group character set issues."
67  :link '(custom-manual "(gnus)Charsets")
68  :version "21.1"
69  :group 'gnus)
70
71(defgroup gnus-cache nil
72  "Cache interface."
73  :link '(custom-manual "(gnus)Article Caching")
74  :group 'gnus)
75
76(defgroup gnus-registry nil
77  "Article Registry."
78  :group 'gnus)
79
80(defgroup gnus-start-server nil
81  "Server options at startup."
82  :group 'gnus-start)
83
84;; These belong to gnus-group.el.
85(defgroup gnus-group nil
86  "Group buffers."
87  :link '(custom-manual "(gnus)Group Buffer")
88  :group 'gnus)
89
90(defgroup gnus-group-foreign nil
91  "Foreign groups."
92  :link '(custom-manual "(gnus)Foreign Groups")
93  :group 'gnus-group)
94
95(defgroup gnus-group-new nil
96  "Automatic subscription of new groups."
97  :group 'gnus-group)
98
99(defgroup gnus-group-levels nil
100  "Group levels."
101  :link '(custom-manual "(gnus)Group Levels")
102  :group 'gnus-group)
103
104(defgroup gnus-group-select nil
105  "Selecting a Group."
106  :link '(custom-manual "(gnus)Selecting a Group")
107  :group 'gnus-group)
108
109(defgroup gnus-group-listing nil
110  "Showing slices of the group list."
111  :link '(custom-manual "(gnus)Listing Groups")
112  :group 'gnus-group)
113
114(defgroup gnus-group-visual nil
115  "Sorting the group buffer."
116  :link '(custom-manual "(gnus)Group Buffer Format")
117  :group 'gnus-group
118  :group 'gnus-visual)
119
120(defgroup gnus-group-various nil
121  "Various group options."
122  :link '(custom-manual "(gnus)Scanning New Messages")
123  :group 'gnus-group)
124
125;; These belong to gnus-sum.el.
126(defgroup gnus-summary nil
127  "Summary buffers."
128  :link '(custom-manual "(gnus)Summary Buffer")
129  :group 'gnus)
130
131(defgroup gnus-summary-exit nil
132  "Leaving summary buffers."
133  :link '(custom-manual "(gnus)Exiting the Summary Buffer")
134  :group 'gnus-summary)
135
136(defgroup gnus-summary-marks nil
137  "Marks used in summary buffers."
138  :link '(custom-manual "(gnus)Marking Articles")
139  :group 'gnus-summary)
140
141(defgroup gnus-thread nil
142  "Ordering articles according to replies."
143  :link '(custom-manual "(gnus)Threading")
144  :group 'gnus-summary)
145
146(defgroup gnus-summary-format nil
147  "Formatting of the summary buffer."
148  :link '(custom-manual "(gnus)Summary Buffer Format")
149  :group 'gnus-summary)
150
151(defgroup gnus-summary-choose nil
152  "Choosing Articles."
153  :link '(custom-manual "(gnus)Choosing Articles")
154  :group 'gnus-summary)
155
156(defgroup gnus-summary-maneuvering nil
157  "Summary movement commands."
158  :link '(custom-manual "(gnus)Summary Maneuvering")
159  :group 'gnus-summary)
160
161(defgroup gnus-picon nil
162  "Show pictures of people, domains, and newsgroups."
163  :group 'gnus-visual)
164
165(defgroup gnus-summary-mail nil
166  "Mail group commands."
167  :link '(custom-manual "(gnus)Mail Group Commands")
168  :group 'gnus-summary)
169
170(defgroup gnus-summary-sort nil
171  "Sorting the summary buffer."
172  :link '(custom-manual "(gnus)Sorting the Summary Buffer")
173  :group 'gnus-summary)
174
175(defgroup gnus-summary-visual nil
176  "Highlighting and menus in the summary buffer."
177  :link '(custom-manual "(gnus)Summary Highlighting")
178  :group 'gnus-visual
179  :group 'gnus-summary)
180
181(defgroup gnus-summary-various nil
182  "Various summary buffer options."
183  :link '(custom-manual "(gnus)Various Summary Stuff")
184  :group 'gnus-summary)
185
186(defgroup gnus-summary-pick nil
187  "Pick mode in the summary buffer."
188  :link '(custom-manual "(gnus)Pick and Read")
189  :prefix "gnus-pick-"
190  :group 'gnus-summary)
191
192(defgroup gnus-summary-tree nil
193  "Tree display of threads in the summary buffer."
194  :link '(custom-manual "(gnus)Tree Display")
195  :prefix "gnus-tree-"
196  :group 'gnus-summary)
197
198;; Belongs to gnus-uu.el
199(defgroup gnus-extract-view nil
200  "Viewing extracted files."
201  :link '(custom-manual "(gnus)Viewing Files")
202  :group 'gnus-extract)
203
204;; Belongs to gnus-score.el
205(defgroup gnus-score nil
206  "Score and kill file handling."
207  :group 'gnus)
208
209(defgroup gnus-score-kill nil
210  "Kill files."
211  :group 'gnus-score)
212
213(defgroup gnus-score-adapt nil
214  "Adaptive score files."
215  :group 'gnus-score)
216
217(defgroup gnus-score-default nil
218  "Default values for score files."
219  :group 'gnus-score)
220
221(defgroup gnus-score-expire nil
222  "Expiring score rules."
223  :group 'gnus-score)
224
225(defgroup gnus-score-decay nil
226  "Decaying score rules."
227  :group 'gnus-score)
228
229(defgroup gnus-score-files nil
230  "Score and kill file names."
231  :group 'gnus-score
232  :group 'gnus-files)
233
234(defgroup gnus-score-various nil
235  "Various scoring and killing options."
236  :group 'gnus-score)
237
238;; Other
239(defgroup gnus-visual nil
240  "Options controlling the visual fluff."
241  :group 'gnus
242  :group 'faces)
243
244(defgroup gnus-agent nil
245  "Offline support for Gnus."
246  :group 'gnus)
247
248(defgroup gnus-files nil
249  "Files used by Gnus."
250  :group 'gnus)
251
252(defgroup gnus-dribble-file nil
253  "Auto save file."
254  :link '(custom-manual "(gnus)Auto Save")
255  :group 'gnus-files)
256
257(defgroup gnus-newsrc nil
258  "Storing Gnus state."
259  :group 'gnus-files)
260
261(defgroup gnus-server nil
262  "Options related to newsservers and other servers used by Gnus."
263  :group 'gnus)
264
265(defgroup gnus-server-visual nil
266  "Highlighting and menus in the server buffer."
267  :group 'gnus-visual
268  :group 'gnus-server)
269
270(defgroup gnus-message '((message custom-group))
271  "Composing replies and followups in Gnus."
272  :group 'gnus)
273
274(defgroup gnus-meta nil
275  "Meta variables controlling major portions of Gnus.
276In general, modifying these variables does not take effect until Gnus
277is restarted, and sometimes reloaded."
278  :group 'gnus)
279
280(defgroup gnus-various nil
281  "Other Gnus options."
282  :link '(custom-manual "(gnus)Various Various")
283  :group 'gnus)
284
285(defgroup gnus-exit nil
286  "Exiting Gnus."
287  :link '(custom-manual "(gnus)Exiting Gnus")
288  :group 'gnus)
289
290(defgroup gnus-fun nil
291  "Frivolous Gnus extensions."
292  :link '(custom-manual "(gnus)Exiting Gnus")
293  :group 'gnus)
294
295(defgroup gnus-dbus nil
296  "D-Bus integration for Gnus."
297  :group 'gnus)
298
299(defconst gnus-version-number "5.13"
300  "Version number for this version of Gnus.")
301
302(defconst gnus-version (format "Gnus v%s" gnus-version-number)
303  "Version string for this version of Gnus.")
304
305(defcustom gnus-inhibit-startup-message nil
306  "If non-nil, the startup message will not be displayed.
307This variable is used before `.gnus.el' is loaded, so it should
308be set in `.emacs' instead."
309  :group 'gnus-start
310  :type 'boolean)
311
312(defun gnus-mode-line-buffer-identification (line)
313  (let ((str (car-safe line)))
314    (if (or (not (fboundp 'find-image))
315	    (not (display-graphic-p))
316	    (not (stringp str))
317	    (not (string-match "^Gnus:" str)))
318	line
319      (let ((load-path (append (mm-image-load-path) load-path)))
320	;; Add the Gnus logo.
321	(add-text-properties
322	 0 5
323	 (list 'display
324	       (find-image
325		'((:type xpm :file "gnus-pointer.xpm"
326			 :ascent center)
327		  (:type xbm :file "gnus-pointer.xbm"
328			 :ascent center))
329		t)
330	       'help-echo (format
331			   "This is %s, %s."
332			   gnus-version (gnus-emacs-version)))
333	 str)
334	(list str)))))
335
336;; We define these group faces here to avoid the display
337;; update forced when creating new faces.
338
339(defface gnus-group-news-1-empty
340  '((((class color)
341      (background dark))
342     (:foreground "PaleTurquoise"))
343    (((class color)
344      (background light))
345     (:foreground "ForestGreen"))
346    (t
347     ()))
348  "Level 1 empty newsgroup face."
349  :group 'gnus-group)
350
351(defface gnus-group-news-1
352  '((t (:inherit gnus-group-news-1-empty :bold t)))
353  "Level 1 newsgroup face."
354  :group 'gnus-group)
355
356(defface gnus-group-news-2-empty
357  '((((class color)
358      (background dark))
359     (:foreground "turquoise"))
360    (((class color)
361      (background light))
362     (:foreground "CadetBlue4"))
363    (t
364     ()))
365  "Level 2 empty newsgroup face."
366  :group 'gnus-group)
367
368(defface gnus-group-news-2
369  '((t (:inherit gnus-group-news-2-empty :bold t)))
370  "Level 2 newsgroup face."
371  :group 'gnus-group)
372
373(defface gnus-group-news-3-empty
374  '((((class color)
375      (background dark))
376     ())
377    (((class color)
378      (background light))
379     ())
380    (t
381     ()))
382  "Level 3 empty newsgroup face."
383  :group 'gnus-group)
384
385(defface gnus-group-news-3
386  '((t (:inherit gnus-group-news-3-empty :bold t)))
387  "Level 3 newsgroup face."
388  :group 'gnus-group)
389
390(defface gnus-group-news-4-empty
391  '((((class color)
392      (background dark))
393     ())
394    (((class color)
395      (background light))
396     ())
397    (t
398     ()))
399  "Level 4 empty newsgroup face."
400  :group 'gnus-group)
401
402(defface gnus-group-news-4
403  '((t (:inherit gnus-group-news-4-empty :bold t)))
404  "Level 4 newsgroup face."
405  :group 'gnus-group)
406
407(defface gnus-group-news-5-empty
408  '((((class color)
409      (background dark))
410     ())
411    (((class color)
412      (background light))
413     ())
414    (t
415     ()))
416  "Level 5 empty newsgroup face."
417  :group 'gnus-group)
418
419(defface gnus-group-news-5
420  '((t (:inherit gnus-group-news-5-empty :bold t)))
421  "Level 5 newsgroup face."
422  :group 'gnus-group)
423
424(defface gnus-group-news-6-empty
425  '((((class color)
426      (background dark))
427     ())
428    (((class color)
429      (background light))
430     ())
431    (t
432     ()))
433  "Level 6 empty newsgroup face."
434  :group 'gnus-group)
435
436(defface gnus-group-news-6
437  '((t (:inherit gnus-group-news-6-empty :bold t)))
438  "Level 6 newsgroup face."
439  :group 'gnus-group)
440
441(defface gnus-group-news-low-empty
442  '((((class color)
443      (background dark))
444     (:foreground "DarkTurquoise"))
445    (((class color)
446      (background light))
447     (:foreground "DarkGreen"))
448    (t
449     ()))
450  "Low level empty newsgroup face."
451  :group 'gnus-group)
452
453(defface gnus-group-news-low
454  '((t (:inherit gnus-group-news-low-empty :bold t)))
455  "Low level newsgroup face."
456  :group 'gnus-group)
457
458(defface gnus-group-mail-1-empty
459  '((((class color)
460      (background dark))
461     (:foreground "#e1ffe1"))
462    (((class color)
463      (background light))
464     (:foreground "DeepPink3"))
465    (t
466     (:italic t)))
467  "Level 1 empty mailgroup face."
468  :group 'gnus-group)
469
470(defface gnus-group-mail-1
471  '((t (:inherit gnus-group-mail-1-empty :bold t)))
472  "Level 1 mailgroup face."
473  :group 'gnus-group)
474
475(defface gnus-group-mail-2-empty
476  '((((class color)
477      (background dark))
478     (:foreground "DarkSeaGreen1"))
479    (((class color)
480      (background light))
481     (:foreground "HotPink3"))
482    (t
483     (:italic t)))
484  "Level 2 empty mailgroup face."
485  :group 'gnus-group)
486
487(defface gnus-group-mail-2
488  '((t (:inherit gnus-group-mail-2-empty :bold t)))
489  "Level 2 mailgroup face."
490  :group 'gnus-group)
491
492(defface gnus-group-mail-3-empty
493  '((((class color)
494      (background dark))
495     (:foreground "aquamarine1"))
496    (((class color)
497      (background light))
498     (:foreground "magenta4"))
499    (t
500     ()))
501  "Level 3 empty mailgroup face."
502  :group 'gnus-group)
503
504(defface gnus-group-mail-3
505  '((t (:inherit gnus-group-mail-3-empty :bold t)))
506  "Level 3 mailgroup face."
507  :group 'gnus-group)
508
509(defface gnus-group-mail-low-empty
510  '((((class color)
511      (background dark))
512     (:foreground "aquamarine2"))
513    (((class color)
514      (background light))
515     (:foreground "DeepPink4"))
516    (t
517     (:bold t)))
518  "Low level empty mailgroup face."
519  :group 'gnus-group)
520
521(defface gnus-group-mail-low
522  '((t (:inherit gnus-group-mail-low-empty :bold t)))
523  "Low level mailgroup face."
524  :group 'gnus-group)
525
526;; Summary mode faces.
527
528(defface gnus-summary-selected '((t (:underline t :extend t)))
529  "Face used for selected articles."
530  :group 'gnus-summary)
531
532(defface gnus-summary-cancelled
533  '((((class color))
534     (:foreground "yellow" :background "black" :extend t))
535    (t (:extend t)))
536  "Face used for canceled articles."
537  :group 'gnus-summary)
538
539(defface gnus-summary-normal-ticked
540  '((((class color)
541      (background dark))
542     (:foreground "pink" :extend t))
543    (((class color)
544      (background light))
545     (:foreground "firebrick" :extend t))
546    (t
547     (:extend t)))
548  "Face used for normal interest ticked articles."
549  :group 'gnus-summary)
550
551(defface gnus-summary-high-ticked
552  '((t (:inherit gnus-summary-normal-ticked :bold t)))
553  "Face used for high interest ticked articles."
554  :group 'gnus-summary)
555
556(defface gnus-summary-low-ticked
557  '((t (:inherit gnus-summary-normal-ticked :italic t)))
558  "Face used for low interest ticked articles."
559  :group 'gnus-summary)
560
561(defface gnus-summary-normal-ancient
562  '((((class color)
563      (background dark))
564     (:foreground "SkyBlue" :extend t))
565    (((class color)
566      (background light))
567     (:foreground "RoyalBlue" :extend t))
568    (t
569     (:extend t)))
570  "Face used for normal interest ancient articles."
571  :group 'gnus-summary)
572
573(defface gnus-summary-high-ancient
574  '((t (:inherit gnus-summary-normal-ancient :bold t)))
575  "Face used for high interest ancient articles."
576  :group 'gnus-summary)
577
578(defface gnus-summary-low-ancient
579  '((t (:inherit gnus-summary-normal-ancient :italic t)))
580  "Face used for low interest ancient articles."
581  :group 'gnus-summary)
582
583(defface gnus-summary-normal-undownloaded
584   '((((class color)
585       (background light))
586      (:foreground "cyan4" :bold nil :extend t))
587     (((class color) (background dark))
588      (:foreground "LightGray" :bold nil :extend t))
589     (t (:inverse-video t :extend t)))
590  "Face used for normal interest uncached articles."
591  :group 'gnus-summary)
592
593(defface gnus-summary-high-undownloaded
594  '((t (:inherit gnus-summary-normal-undownloaded :bold t)))
595  "Face used for high interest uncached articles."
596  :group 'gnus-summary)
597
598(defface gnus-summary-low-undownloaded
599  '((t (:inherit gnus-summary-normal-undownloaded :italic t)))
600  "Face used for low interest uncached articles."
601  :group 'gnus-summary)
602
603(defface gnus-summary-normal-unread
604  '((t
605     (:extend t)))
606  "Face used for normal interest unread articles."
607  :group 'gnus-summary)
608
609(defface gnus-summary-high-unread
610  '((t (:inherit gnus-summary-normal-unread :bold t)))
611  "Face used for high interest unread articles."
612  :group 'gnus-summary)
613
614(defface gnus-summary-low-unread
615  '((t (:inherit gnus-summary-normal-unread :italic t)))
616  "Face used for low interest unread articles."
617  :group 'gnus-summary)
618
619(defface gnus-summary-normal-read
620  '((((class color)
621      (background dark))
622     (:foreground "PaleGreen" :extend t))
623    (((class color)
624      (background light))
625     (:foreground "DarkGreen" :extend t))
626    (t
627     (:extend t)))
628  "Face used for normal interest read articles."
629  :group 'gnus-summary)
630
631(defface gnus-summary-high-read
632  '((t (:inherit gnus-summary-normal-read :bold t)))
633  "Face used for high interest read articles."
634  :group 'gnus-summary)
635
636(defface gnus-summary-low-read
637  '((t (:inherit gnus-summary-normal-read :italic t)))
638  "Face used for low interest read articles."
639  :group 'gnus-summary)
640
641;;; Base gnus-mode
642
643(define-derived-mode gnus-mode special-mode nil
644  "Base mode from which all other gnus modes derive.
645This does nothing but derive from `special-mode', and should not
646be used directly.")
647
648;;;
649;;; Gnus buffers
650;;;
651
652(defvar gnus-buffers nil
653  "List of buffers handled by Gnus.")
654
655(defun gnus-get-buffer-create (name)
656  "Do the same as `get-buffer-create', but store the created buffer."
657  (or (get-buffer name)
658      (car (push (get-buffer-create name) gnus-buffers))))
659
660(defun gnus-add-buffer ()
661  "Add the current buffer to the list of Gnus buffers."
662  (gnus-prune-buffers)
663  (cl-pushnew (current-buffer) gnus-buffers))
664
665(defmacro gnus-kill-buffer (buffer)
666  "Kill BUFFER and remove from the list of Gnus buffers."
667  `(let ((buf ,buffer))
668     (when (gnus-buffer-live-p buf)
669       (kill-buffer buf)
670       (gnus-prune-buffers))))
671
672(defun gnus-buffers ()
673  "Return a list of live Gnus buffers."
674  (setq gnus-buffers (seq-filter #'buffer-live-p gnus-buffers)))
675
676(defalias 'gnus-prune-buffers #'gnus-buffers)
677
678;;; Splash screen.
679
680(defvar gnus-group-buffer "*Group*"
681  "Name of the Gnus group buffer.")
682
683(defface gnus-splash
684  '((((class color)
685      (background dark))
686     (:foreground "#cccccc"))
687    (((class color)
688      (background light))
689     (:foreground "#888888"))
690    (t
691     ()))
692  "Face for the splash screen."
693  :group 'gnus-start)
694
695(defun gnus-splash ()
696  (save-excursion
697    (switch-to-buffer (gnus-get-buffer-create gnus-group-buffer))
698    (let ((buffer-read-only nil))
699      (erase-buffer)
700      (unless gnus-inhibit-startup-message
701	(gnus-group-startup-message)
702	(sit-for 0)))))
703
704(defun gnus-indent-rigidly (start end arg)
705  "Indent rigidly using only spaces and no tabs."
706  (save-excursion
707    (save-restriction
708      (narrow-to-region start end)
709      (let ((tab-width 8))
710	(indent-rigidly start end arg)
711	;; We translate tabs into spaces -- not everybody uses
712	;; an 8-character tab.
713	(goto-char (point-min))
714	(while (search-forward "\t" nil t)
715	  (replace-match "        " t t))))))
716
717;;(format "%02x%02x%02x" 114 66 20) "724214"
718
719(defvar gnus-logo-color-alist
720  '((flame "#cc3300" "#ff2200")
721    (pine "#c0cc93" "#f8ffb8")
722    (moss "#a1cc93" "#d2ffb8")
723    (irish "#04cc90" "#05ff97")
724    (sky "#049acc" "#05deff")
725    (tin "#6886cc" "#82b6ff")
726    (velvet "#7c68cc" "#8c82ff")
727    (grape "#b264cc" "#cf7df")
728    (labia "#cc64c2" "#fd7dff")
729    (berry "#cc6485" "#ff7db5")
730    (dino "#724214" "#1e3f03")
731    (oort "#cccccc" "#888888")
732    (storm "#666699" "#99ccff")
733    (pdino "#9999cc" "#99ccff")
734    (purp "#9999cc" "#666699")
735    (no "#ff0000" "#ffff00")
736    (neutral "#b4b4b4" "#878787")
737    (ma "#2020e0" "#8080ff")
738    (september "#bf9900" "#ffcc00"))
739  "Color alist used for the Gnus logo.")
740
741(defcustom gnus-logo-color-style 'ma
742  "Color styles used for the Gnus logo."
743  :type `(choice ,@(mapcar (lambda (elem) (list 'const (car elem)))
744			   gnus-logo-color-alist))
745  :group 'gnus-xmas)
746
747(defvar gnus-logo-colors
748  (cdr (assq gnus-logo-color-style gnus-logo-color-alist))
749  "Colors used for the Gnus logo.")
750
751(defvar image-load-path)
752(declare-function image-size "image.c" (spec &optional pixels frame))
753
754(defun gnus-group-startup-message (&optional x y)
755  "Insert startup message in current buffer."
756  ;; Insert the message.
757  (erase-buffer)
758  (unless (and
759           (fboundp 'find-image)
760           (display-graphic-p)
761           ;; Make sure the library defining `image-load-path' is
762           ;; loaded (`find-image' is autoloaded) (and discard the
763           ;; result).  Else, we may get "defvar ignored because
764           ;; image-load-path is let-bound" when calling `find-image'
765           ;; below.
766           (or (find-image '(nil (:type xpm :file "gnus.xpm"))) t)
767           (let* ((data-directory (nnheader-find-etc-directory "images/gnus"))
768                  (image-load-path (cond (data-directory
769                                          (list data-directory))
770                                         ((boundp 'image-load-path)
771                                          (symbol-value 'image-load-path))
772                                         (t load-path)))
773                  (image (gnus-splash-svg-color-symbols (find-image
774                          `((:type svg :file "gnus.svg"
775                                   :color-symbols
776                                   (("#bf9900" . ,(car gnus-logo-colors))
777                                    ("#ffcc00" . ,(cadr gnus-logo-colors))))
778                            (:type xpm :file "gnus.xpm"
779                                   :color-symbols
780                                   (("thing" . ,(car gnus-logo-colors))
781                                    ("shadow" . ,(cadr gnus-logo-colors))))
782                            (:type png :file "gnus.png")
783                            (:type pbm :file "gnus.pbm"
784                                   ;; Account for the pbm's background.
785                                   :background ,(face-foreground 'gnus-splash)
786                                   :foreground ,(face-background 'default))
787                            (:type xbm :file "gnus.xbm"
788                                   ;; Account for the xbm's background.
789                                   :background ,(face-foreground 'gnus-splash)
790                                   :foreground ,(face-background 'default)))))))
791             (when image
792               (let ((size (image-size image)))
793                 (insert-char ?\n (max 0 (round (- (window-height)
794                                                   (or y (cdr size)) 1) 2)))
795                 (insert-char ?\  (max 0 (round (- (window-width)
796                                                   (or x (car size))) 2)))
797                 (insert-image image))
798	       (goto-char (point-min))
799               t)))
800    (insert
801     "
802	  _    ___ _             _
803	  _ ___ __ ___  __    _ ___
804	  __   _     ___    __  ___
805	      _           ___     _
806	     _  _ __             _
807	     ___   __            _
808		   __           _
809		    _      _   _
810		   _      _    _
811		      _  _    _
812		  __  ___
813		 _   _ _     _
814		_   _
815	      _    _
816	     _    _
817	    _
818	  __
819
820")
821    ;; And then hack it.
822    (gnus-indent-rigidly (point-min) (point-max)
823			 (/ (max (- (window-width) (or x 46)) 0) 2))
824    (goto-char (point-min))
825    (forward-line 1)
826    (let* ((pheight (count-lines (point-min) (point-max)))
827	   (wheight (window-height))
828	   (rest (- wheight pheight)))
829      (insert (make-string (max 0 (* 2 (/ rest 3))) ?\n)))
830    ;; Fontify some.
831    (put-text-property (point-min) (point-max) 'face 'gnus-splash)
832    (goto-char (point-min))
833    (setq mode-line-buffer-identification (concat " " gnus-version))
834    (set-buffer-modified-p t)))
835
836(defun gnus-splash-svg-color-symbols (list)
837  "Do color-symbol search-and-replace in svg file."
838  (let ((type (plist-get (cdr list) :type))
839        (file (plist-get (cdr list) :file))
840        (color-symbols (plist-get (cdr list) :color-symbols)))
841    (if (string= type "svg")
842        (let ((data (with-temp-buffer (insert-file-contents file)
843                                      (buffer-string))))
844          (mapc (lambda (rule)
845                  (setq data (replace-regexp-in-string
846                              (concat "fill:" (car rule))
847                              (concat "fill:" (cdr rule)) data)))
848                color-symbols)
849          (cons (car list) (list :type type :data data)))
850       list)))
851
852;;; Do the rest.
853
854(require 'gnus-util)
855(require 'nnheader)
856
857(defcustom gnus-parameters nil
858  "Alist of group parameters.
859
860For example:
861   ((\"mail\\\\..*\"  (gnus-show-threads nil)
862		  (gnus-use-scoring nil)
863		  (gnus-summary-line-format
864			\"%U%R%z%I%(%[%d:%ub%-23,23f%]%) %s\\n\")
865		  (gcc-self . t)
866		  (display . all))
867     (\"mail\\\\.me\" (gnus-use-scoring  t))
868     (\"list\\\\..*\" (total-expire . t)
869		  (broken-reply-to . t)))"
870  :version "22.1"
871  :group 'gnus-group-various
872  :type '(repeat (cons regexp
873		       (repeat sexp))))
874
875(defcustom gnus-parameters-case-fold-search 'default
876  "If it is t, ignore case of group names specified in `gnus-parameters'.
877If it is nil, don't ignore case.  If it is `default', which is for the
878backward compatibility, use the value of `case-fold-search'."
879  :version "22.1"
880  :group 'gnus-group-various
881  :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
882		 (const :tag "Use `case-fold-search'" default)
883		 (const nil)
884		 (const t)))
885
886(defvar gnus-group-parameters-more nil)
887
888(defmacro gnus-define-group-parameter (param &rest rest)
889  "Define a group parameter PARAM.
890REST is a plist of following:
891:type               One of `bool', `list' or nil.
892:function           The name of the function.
893:function-document  The documentation of the function.
894:parameter-type     The type for customizing the parameter.
895:parameter-document The documentation for the parameter.
896:variable           The name of the variable.
897:variable-document  The documentation for the variable.
898:variable-group     The group for customizing the variable.
899:variable-type      The type for customizing the variable.
900:variable-default   The default value of the variable."
901  (let* ((type (plist-get rest :type))
902	 (parameter-type (plist-get rest :parameter-type))
903	 (parameter-document (plist-get rest :parameter-document))
904	 (function (or (plist-get rest :function)
905		       (intern (format "gnus-parameter-%s" param))))
906	 (function-document (or (plist-get rest :function-document) ""))
907	 (variable (or (plist-get rest :variable)
908		       (intern (format "gnus-parameter-%s-alist" param))))
909	 (variable-document (or (plist-get rest :variable-document) ""))
910	 (variable-group (plist-get rest :variable-group))
911	 (variable-type (or (plist-get rest :variable-type)
912			    `(quote (repeat
913				     (list (regexp :tag "Group")
914					   ,(car (cdr parameter-type)))))))
915	 (variable-default (plist-get rest :variable-default)))
916    (list
917     'progn
918     `(defcustom ,variable ,variable-default
919	,variable-document
920	:group 'gnus-group-parameter
921	:group ',variable-group
922	:type ,variable-type)
923     `(setq gnus-group-parameters-more
924	    (delq (assq ',param gnus-group-parameters-more)
925		  gnus-group-parameters-more))
926     `(add-to-list 'gnus-group-parameters-more
927		   (list ',param
928			 ,parameter-type
929			 ,parameter-document))
930     (if (eq type 'bool)
931	 `(defun ,function (name)
932	    ,function-document
933	    (let ((params (gnus-group-find-parameter name))
934		  val)
935	      (cond
936	       ((memq ',param params)
937		t)
938	       ((setq val (assq ',param params))
939		(cdr val))
940	       ((stringp ,variable)
941		(string-match ,variable name))
942	       (,variable
943		(let ((alist ,variable)
944		      elem value)
945		  (while (setq elem (pop alist))
946		    (when (and name
947			       (string-match (car elem) name))
948		      (setq alist nil
949			    value (cdr elem))))
950		  (if (consp value) (car value) value))))))
951       `(defun ,function (name)
952	  ,function-document
953	  (and name
954	       (or (gnus-group-find-parameter name ',param ,(and type t))
955		   (let ((alist ,variable)
956			 elem value)
957		     (while (setq elem (pop alist))
958		       (when (and name
959				  (string-match (car elem) name))
960			 (setq alist nil
961			       value (cdr elem))))
962		     ,(if type
963			  'value
964			'(if (consp value) (car value) value))))))))))
965
966(defcustom gnus-home-directory "~/"
967  "Directory variable that specifies the \"home\" directory.
968All other Gnus file and directory variables are initialized from this variable.
969
970Note that Gnus is mostly loaded when the `.gnus.el' file is read.
971This means that other directory variables that are initialized
972from this variable won't be set properly if you set this variable
973in `.gnus.el'.  Set this variable in `.emacs' instead."
974  :group 'gnus-files
975  :type 'directory)
976
977(defcustom gnus-directory (or (getenv "SAVEDIR")
978			      (nnheader-concat gnus-home-directory "News/"))
979  "Directory variable from which all other Gnus file variables are derived.
980
981Note that Gnus is mostly loaded when the `.gnus.el' file is read.
982This means that other directory variables that are initialized from
983this variable won't be set properly if you set this variable in `.gnus.el'.
984Set this variable in `.emacs' instead."
985  :group 'gnus-files
986  :type 'directory)
987
988(defcustom gnus-default-directory nil
989  "Default directory for all Gnus buffers."
990  :group 'gnus-files
991  :type '(choice (const :tag "current" nil)
992		 directory))
993
994;; Site dependent variables.
995
996;; Should this be obsolete?
997(defcustom gnus-default-nntp-server nil
998  "The hostname of the default NNTP server.
999The empty string, or nil, means to use the local host.
1000You may wish to set this on a site-wide basis.
1001
1002If you want to change servers, you should use `gnus-select-method'."
1003  :group 'gnus-server
1004  :type '(choice (const :tag "local host" nil)
1005                 (string :tag "host name")))
1006
1007(defcustom gnus-nntpserver-file "/etc/nntpserver"
1008  "A file with only the name of the nntp server in it."
1009  :group 'gnus-files
1010  :group 'gnus-server
1011  :type 'file)
1012
1013(defun gnus-getenv-nntpserver ()
1014  "Find default nntp server.
1015Check the NNTPSERVER environment variable and the
1016`gnus-nntpserver-file' file."
1017  (or (getenv "NNTPSERVER")
1018      (and (file-readable-p gnus-nntpserver-file)
1019	   (with-temp-buffer
1020	     (insert-file-contents gnus-nntpserver-file)
1021	     (when (re-search-forward "[^ \t\n\r]+" nil t)
1022	       (match-string 0))))))
1023
1024;; `M-x customize-variable RET gnus-select-method RET' should work without
1025;; starting or even loading Gnus.
1026;;;###autoload(custom-autoload 'gnus-select-method "gnus")
1027
1028(defcustom gnus-select-method
1029  (list 'nntp (or (gnus-getenv-nntpserver)
1030                  (when (and gnus-default-nntp-server
1031                             (not (string= gnus-default-nntp-server "")))
1032                    gnus-default-nntp-server)
1033                  "news"))
1034  "Default method for selecting a newsgroup.
1035This variable should be a list, where the first element is how the
1036news is to be fetched, the second is the address.
1037
1038For instance, if you want to get your news via \"flab.flab.edu\" using
1039NNTP, you could say:
1040
1041\(setq gnus-select-method \\='(nntp \"flab.flab.edu\"))
1042
1043If you want to use your local spool, say:
1044
1045\(setq gnus-select-method (list \\='nnspool (system-name)))
1046
1047If you use this variable, you must set `gnus-nntp-server' to nil.
1048
1049There is a lot more to know about select methods and virtual servers -
1050see the manual for details."
1051  ;; Emacs has set-after since 22.1.
1052  ;set-after '(gnus-default-nntp-server)
1053  :group 'gnus-server
1054  :group 'gnus-start
1055  :initialize 'custom-initialize-default
1056  :type 'gnus-select-method)
1057
1058(defcustom gnus-message-archive-method "archive"
1059  "Method used for archiving messages you've sent.
1060This should be a mail method.
1061
1062See also `gnus-update-message-archive-method'."
1063  :group 'gnus-server
1064  :group 'gnus-message
1065  :type '(choice (const :tag "Default archive method" "archive")
1066		 gnus-select-method))
1067
1068(defcustom gnus-update-message-archive-method nil
1069  "Non-nil means always update the saved \"archive\" method.
1070
1071The archive method is initially set according to the value of
1072`gnus-message-archive-method' and is saved in the \"~/.newsrc.eld\" file
1073so that it may be used as a real method of the server which is named
1074\"archive\" ever since.  If it once has been saved, it will never be
1075updated if the value of this variable is nil, even if you change the
1076value of `gnus-message-archive-method' afterward.  If you want the
1077saved \"archive\" method to be updated whenever you change the value of
1078`gnus-message-archive-method', set this variable to a non-nil value."
1079  :version "23.1"
1080  :group 'gnus-server
1081  :group 'gnus-message
1082  :type 'boolean)
1083
1084(defcustom gnus-message-archive-group '((format-time-string "sent.%Y-%m"))
1085  "Name of the group in which to save the messages you've written.
1086This can either be a string; a list of strings; or an alist
1087of regexps/functions/forms to be evaluated to return a string (or a list
1088of strings).  The functions are called with the name of the current
1089group (or nil) as a parameter.
1090
1091If you want to save your mail in one group and the news articles you
1092write in another group, you could say something like:
1093
1094  (setq gnus-message-archive-group
1095	\\='((if (message-news-p)
1096	      \"misc-news\"
1097	    \"misc-mail\")))
1098
1099Normally the group names returned by this variable should be
1100unprefixed -- which implicitly means \"store on the archive server\".
1101However, you may wish to store the message on some other server.  In
1102that case, just return a fully prefixed name of the group --
1103\"nnml+private:mail.misc\", for instance."
1104  :version "24.1"
1105  :group 'gnus-message
1106  :type '(choice (const :tag "none" nil)
1107		 (const :tag "Weekly" ((format-time-string "sent.%Yw%U")))
1108		 (const :tag "Monthly" ((format-time-string "sent.%Y-%m")))
1109		 (const :tag "Yearly" ((format-time-string "sent.%Y")))
1110		 function
1111		 sexp
1112		 string))
1113
1114(defcustom gnus-secondary-servers nil
1115  "List of NNTP servers that the user can choose between interactively.
1116To make Gnus query you for a server, you have to give `gnus' a
1117non-numeric prefix - `\\[universal-argument] \\[gnus]', in short."
1118  :group 'gnus-server
1119  :type '(repeat string))
1120(make-obsolete-variable 'gnus-secondary-servers 'gnus-select-method "24.1")
1121
1122(defcustom gnus-secondary-select-methods nil
1123  "A list of secondary methods that will be used for reading news.
1124This is a list where each element is a complete select method (see
1125`gnus-select-method').
1126
1127If, for instance, you want to read your mail with the nnml back end,
1128you could set this variable:
1129
1130\(setq gnus-secondary-select-methods \\='((nnml \"\")))"
1131  :group 'gnus-server
1132  :type '(repeat gnus-select-method))
1133
1134(defcustom gnus-local-domain nil
1135  "Local domain name without a host name.
1136The DOMAINNAME environment variable is used instead if it is defined.
1137If the function `system-name' returns the full Internet name, there is
1138no need to set this variable."
1139  :group 'gnus-message
1140  :type '(choice (const :tag "default" nil)
1141		 string))
1142(make-obsolete-variable 'gnus-local-domain nil "24.1")
1143
1144;; Customization variables
1145
1146(defcustom gnus-refer-article-method 'current
1147  "Preferred method for fetching an article by Message-ID.
1148The value of this variable must be a valid select method as discussed
1149in the documentation of `gnus-select-method'.
1150
1151It can also be a list of select methods, as well as the special symbol
1152`current', which means to use the current select method.  If it is a
1153list, Gnus will try all the methods in the list until it finds a match."
1154  :version "24.1"
1155  :group 'gnus-server
1156  :type '(choice (const :tag "default" nil)
1157		 (const current)
1158		 (const :tag "Google" (nnweb "refer" (nnweb-type google)))
1159		 gnus-select-method
1160		 sexp
1161		 (repeat :menu-tag "Try multiple"
1162			 :tag "Multiple"
1163			 :value (current (nnweb "refer" (nnweb-type google)))
1164			 (choice :tag "Method"
1165				 (const current)
1166				 (const :tag "Google"
1167					(nnweb "refer" (nnweb-type google)))
1168				 gnus-select-method))))
1169
1170(defcustom gnus-use-cross-reference t
1171  "Non-nil means that cross referenced articles will be marked as read.
1172If nil, ignore cross references.  If t, mark articles as read in
1173subscribed newsgroups.  If neither t nor nil, mark as read in all
1174newsgroups."
1175  :group 'gnus-server
1176  :type '(choice (const :tag "off" nil)
1177		 (const :tag "subscribed" t)
1178		 (sexp :format "all"
1179		       :value always)))
1180
1181(defcustom gnus-process-mark ?#
1182  "Process mark."
1183  :group 'gnus-group-visual
1184  :group 'gnus-summary-marks
1185  :type 'character)
1186
1187(defcustom gnus-process-mark-toggle t
1188  "If nil the process mark command only sets the process mark."
1189  :version "28.1"
1190  :group 'gnus-summary
1191  :group 'gnus-group-various
1192  :group 'gnus-group-topic
1193  :type 'boolean)
1194
1195(defcustom gnus-large-newsgroup 200
1196  "The number of articles which indicates a large newsgroup.
1197If the number of articles in a newsgroup is greater than this value,
1198confirmation is required for selecting the newsgroup.
1199If it is nil, no confirmation is required.
1200
1201Also see `gnus-large-ephemeral-newsgroup'."
1202  :group 'gnus-group-select
1203  :type '(choice (const :tag "No limit" nil)
1204		 integer))
1205
1206(defcustom gnus-use-long-file-name (not (memq system-type '(usg-unix-v)))
1207  "Non-nil means that the default file name to save articles in is the group name.
1208If it's nil, the directory form of the group name is used instead.
1209
1210If this variable is a list, and the list contains the element
1211`not-score', long file names will not be used for score files; if it
1212contains the element `not-save', long file names will not be used for
1213saving; and if it contains the element `not-kill', long file names
1214will not be used for kill files.
1215
1216Note that the default for this variable varies according to what system
1217type you're using.  On `usg-unix-v' this variable defaults to nil while
1218on all other systems it defaults to t."
1219  :group 'gnus-start
1220  :type '(radio (sexp :format "Non-nil\n"
1221		      :match (lambda (widget value)
1222			       (and value (not (listp value))))
1223		      :value t)
1224		(const nil)
1225		(checklist (const :format "%v " not-score)
1226			   (const :format "%v " not-save)
1227			   (const not-kill))))
1228
1229(defcustom gnus-kill-files-directory gnus-directory
1230  "Name of the directory where kill files will be stored (default \"~/News\")."
1231  :group 'gnus-score-files
1232  :group 'gnus-score-kill
1233  :type 'directory)
1234
1235(defcustom gnus-save-score nil
1236  "If non-nil, save group scoring info."
1237  :group 'gnus-score-various
1238  :group 'gnus-start
1239  :type 'boolean)
1240
1241(defcustom gnus-use-undo t
1242  "If non-nil, allow undoing in Gnus group mode buffers."
1243  :group 'gnus-meta
1244  :type 'boolean)
1245
1246(defcustom gnus-use-adaptive-scoring nil
1247  "If non-nil, use some adaptive scoring scheme.
1248If a list, then the values `word' and `line' are meaningful.  The
1249former will perform adaption on individual words in the subject
1250header while `line' will perform adaption on several headers."
1251  :group 'gnus-meta
1252  :group 'gnus-score-adapt
1253  :type '(set (const word) (const line)))
1254
1255(defcustom gnus-use-cache 'passive
1256  "If nil, Gnus will ignore the article cache.
1257If `passive', it will allow entering (and reading) articles
1258explicitly entered into the cache.  If anything else, use the
1259cache to the full extent of the law."
1260  :group 'gnus-meta
1261  :group 'gnus-cache
1262  :type '(choice (const :tag "off" nil)
1263		 (const :tag "passive" passive)
1264		 (const :tag "active" t)))
1265
1266(defcustom gnus-use-trees nil
1267  "If non-nil, display a thread tree buffer."
1268  :group 'gnus-meta
1269  :type 'boolean)
1270
1271(defcustom gnus-keep-backlog 20
1272  "If non-nil, Gnus will keep read articles for later re-retrieval.
1273If it is a number N, then Gnus will only keep the last N articles
1274read.  If it is neither nil nor a number, Gnus will keep all read
1275articles.  This is not a good idea."
1276  :group 'gnus-meta
1277  :type '(choice (const :tag "off" nil)
1278		 integer
1279		 (sexp :format "all"
1280		       :value t)))
1281
1282(defcustom gnus-suppress-duplicates nil
1283  "If non-nil, Gnus will mark duplicate copies of the same article as read."
1284  :group 'gnus-meta
1285  :type 'boolean)
1286
1287(defcustom gnus-use-scoring t
1288  "If non-nil, enable scoring."
1289  :group 'gnus-meta
1290  :type 'boolean)
1291
1292(defcustom gnus-summary-prepare-exit-hook
1293  '(gnus-summary-expire-articles)
1294  "A hook called when preparing to exit from the summary buffer.
1295It calls `gnus-summary-expire-articles' by default."
1296  :group 'gnus-summary-exit
1297  :type 'hook)
1298
1299(defcustom gnus-novice-user t
1300  "Non-nil means that you are a Usenet novice.
1301If non-nil, verbose messages may be displayed and confirmations may be
1302required."
1303  :group 'gnus-meta
1304  :type 'boolean)
1305
1306(defcustom gnus-expert-user nil
1307  "Non-nil means that you will never be asked for confirmation about anything.
1308That doesn't mean *anything* anything; particularly destructive
1309commands will still require prompting."
1310  :group 'gnus-meta
1311  :type 'boolean)
1312
1313(defcustom gnus-interactive-catchup t
1314  "If non-nil, require your confirmation when catching up a group."
1315  :group 'gnus-group-select
1316  :type 'boolean)
1317
1318(defcustom gnus-interactive-exit t
1319  "If non-nil, require your confirmation when exiting Gnus.
1320If `quiet', update any active summary buffers automatically
1321first before exiting."
1322  :group 'gnus-exit
1323  :type '(choice boolean
1324		 (const quiet)))
1325
1326(defcustom gnus-extract-address-components 'gnus-extract-address-components
1327  "Function for extracting address components from a From header.
1328Two pre-defined function exist: `gnus-extract-address-components',
1329which is the default, quite fast, and too simplistic solution, and
1330`mail-extract-address-components', which works much better, but is
1331slower."
1332  :group 'gnus-summary-format
1333  :type '(radio (function-item gnus-extract-address-components)
1334		(function-item mail-extract-address-components)
1335		(function :tag "Other")))
1336
1337(defcustom gnus-shell-command-separator ";"
1338  "String used to separate shell commands."
1339  :group 'gnus-files
1340  :type 'string)
1341
1342(defcustom gnus-valid-select-methods
1343  '(("nntp" post address prompt-address physical-address cloud)
1344    ("nnspool" post address)
1345    ("nnvirtual" post-mail virtual prompt-address)
1346    ("nnmbox" mail respool address)
1347    ("nnml" post-mail respool address)
1348    ("nnmh" mail respool address)
1349    ("nndir" post-mail prompt-address physical-address)
1350    ("nneething" none address prompt-address physical-address)
1351    ("nndoc" none address prompt-address)
1352    ("nnbabyl" mail address respool)
1353    ("nndraft" post-mail)
1354    ("nnfolder" mail respool address)
1355    ("nngateway" post-mail address prompt-address physical-address)
1356    ("nnweb" none)
1357    ("nnrss" none global)
1358    ("nnagent" post-mail)
1359    ("nnimap" post-mail address prompt-address physical-address respool
1360     server-marks cloud)
1361    ("nnmaildir" mail respool address server-marks)
1362    ("nnnil" none))
1363  "An alist of valid select methods.
1364The first element of each list lists should be a string with the name
1365of the select method.  The other elements may be the category of
1366this method (i. e., `post', `mail', `none' or whatever) or other
1367properties that this method has (like being respoolable).
1368If you implement a new select method, all you should have to change is
1369this variable.  I think."
1370  :group 'gnus-server
1371  :type '(repeat (group (string :tag "Name")
1372			(radio-button-choice (const :format "%v " post)
1373					     (const :format "%v " mail)
1374					     (const :format "%v " none)
1375					     (const post-mail))
1376			(checklist :inline t :greedy t
1377				   (const :format "%v " address)
1378				   (const cloud)
1379				   (const global)
1380				   (const :format "%v " prompt-address)
1381				   (const :format "%v " physical-address)
1382				   (const virtual)
1383				   (const :format "%v " respool)
1384				   (const server-marks))))
1385  :version "24.1")
1386
1387(defun gnus-redefine-select-method-widget ()
1388  "Recomputes the select-method widget based on the value of
1389`gnus-valid-select-methods'."
1390  (define-widget 'gnus-select-method 'list
1391    "Widget for entering a select method."
1392    :value '(nntp "")
1393    :tag "Select Method"
1394    :args `((choice :tag "Method"
1395		    ,@(mapcar (lambda (entry)
1396				(list 'const :format "%v\n"
1397				      (intern (car entry))))
1398			      gnus-valid-select-methods)
1399		    (symbol :tag "other"))
1400	    (string :tag "Address")
1401	    (repeat :tag "Options"
1402		    :inline t
1403		    (list :format "%v"
1404			  variable
1405			  (sexp :tag "Value"))))))
1406
1407(gnus-redefine-select-method-widget)
1408
1409(defcustom gnus-updated-mode-lines '(group article summary tree)
1410  "List of buffers that should update their mode lines.
1411The list may contain the symbols `group', `article', `tree' and
1412`summary'.  If the corresponding symbol is present, Gnus will keep
1413that mode line updated with information that may be pertinent.
1414If this variable is nil, screen refresh may be quicker."
1415  :group 'gnus-various
1416  :type '(set (const group)
1417	      (const article)
1418	      (const summary)
1419	      (const tree)))
1420
1421(defcustom gnus-mode-non-string-length 30
1422  "Max length of mode-line non-string contents.
1423If this is nil, Gnus will take space as is needed, leaving the rest
1424of the mode line intact."
1425  :version "24.1"
1426  :group 'gnus-various
1427  :type '(choice (const nil)
1428		 integer))
1429
1430;; There should be special validation for this.
1431(define-widget 'gnus-email-address 'string
1432  "An email address.")
1433
1434(gnus-define-group-parameter
1435 to-address
1436 :function-document
1437 "Return GROUP's to-address."
1438 :variable-document
1439 "Alist of group regexps and correspondent to-addresses."
1440 :variable-group gnus-group-parameter
1441 :parameter-type '(gnus-email-address :tag "To Address")
1442 :parameter-document "\
1443This will be used when doing followups and posts.
1444
1445This is primarily useful in mail groups that represent closed
1446mailing lists--mailing lists where it's expected that everybody that
1447writes to the mailing list is subscribed to it.  Since using this
1448parameter ensures that the mail only goes to the mailing list itself,
1449it means that members won't receive two copies of your followups.
1450
1451Using `to-address' will actually work whether the group is foreign or
1452not.  Let's say there's a group on the server that is called
1453`fa.4ad-l'.  This is a real newsgroup, but the server has gotten the
1454articles from a mail-to-news gateway.  Posting directly to this group
1455is therefore impossible--you have to send mail to the mailing list
1456address instead.
1457
1458The gnus-group-split mail splitting mechanism will behave as if this
1459address was listed in gnus-group-split Addresses (see below).")
1460
1461(gnus-define-group-parameter
1462 to-list
1463 :function-document
1464 "Return GROUP's to-list."
1465 :variable-document
1466 "Alist of group regexps and correspondent to-lists."
1467 :variable-group gnus-group-parameter
1468 :parameter-type '(gnus-email-address :tag "To List")
1469 :parameter-document "\
1470This address will be used when doing a \\`a' in the group.
1471
1472It is totally ignored when doing a followup--except that if it is
1473present in a news group, you'll get mail group semantics when doing
1474\\`f'.
1475
1476The gnus-group-split mail splitting mechanism will behave as if this
1477address was listed in gnus-group-split Addresses (see below).")
1478
1479(gnus-define-group-parameter
1480 subscribed
1481 :type bool
1482 :function-document
1483 "Return GROUP's subscription status."
1484 :variable-document
1485 "Groups which are automatically considered subscribed."
1486 :variable-group gnus-group-parameter
1487 :parameter-type '(const :tag "Subscribed" t)
1488 :parameter-document "\
1489Gnus assumed that you are subscribed to the To/List address.
1490
1491When constructing a list of subscribed groups using
1492`gnus-find-subscribed-addresses', Gnus includes the To address given
1493above, or the list address (if the To address has not been set).")
1494
1495(gnus-define-group-parameter
1496 auto-expire
1497 :type bool
1498 :function gnus-group-auto-expirable-p
1499 :function-document
1500 "Check whether GROUP is auto-expirable or not."
1501 :variable gnus-auto-expirable-newsgroups
1502 :variable-default nil
1503 :variable-document
1504 "Groups in which to automatically mark read articles as expirable.
1505If non-nil, this should be a regexp that should match all groups in
1506which to perform auto-expiry.  This only makes sense for mail groups."
1507 :variable-group nnmail-expire
1508 :variable-type '(choice (const nil)
1509			 regexp)
1510 :parameter-type '(const :tag "Automatic Expire" t)
1511 :parameter-document
1512 "All articles that are read will be marked as expirable.")
1513
1514(gnus-define-group-parameter
1515 total-expire
1516 :type bool
1517 :function gnus-group-total-expirable-p
1518 :function-document
1519 "Check whether GROUP is total-expirable or not."
1520 :variable gnus-total-expirable-newsgroups
1521 :variable-default nil
1522 :variable-document
1523 "Groups in which to perform expiry of all read articles.
1524Use with extreme caution.  All groups that match this regexp will be
1525expiring - which means that all read articles will be deleted after
1526\(say) one week.  (This only goes for mail groups and the like, of
1527course.)"
1528 :variable-group nnmail-expire
1529 :variable-type '(choice (const nil)
1530			 regexp)
1531 :parameter-type '(const :tag "Total Expire" t)
1532 :parameter-document
1533 "All read articles will be put through the expiry process
1534
1535This happens even if they are not marked as expirable.
1536Use with caution.")
1537
1538(gnus-define-group-parameter
1539 charset
1540 :function-document
1541 "Return the default charset of GROUP."
1542 :variable gnus-group-charset-alist
1543 :variable-default
1544 '(("\\(^\\|:\\)hk\\>\\|\\(^\\|:\\)tw\\>\\|\\<big5\\>" cn-big5)
1545   ("\\(^\\|:\\)cn\\>\\|\\<chinese\\>" cn-gb-2312)
1546   ("\\(^\\|:\\)fj\\>\\|\\(^\\|:\\)japan\\>" iso-2022-jp-2)
1547   ("\\(^\\|:\\)tnn\\>\\|\\(^\\|:\\)pin\\>\\|\\(^\\|:\\)sci.lang.japan" iso-2022-7bit)
1548   ("\\(^\\|:\\)relcom\\>" koi8-r)
1549   ("\\(^\\|:\\)fido7\\>" koi8-r)
1550   ("\\(^\\|:\\)\\(cz\\|hun\\|pl\\|sk\\|hr\\)\\>" iso-8859-2)
1551   ("\\(^\\|:\\)israel\\>" iso-8859-1)
1552   ("\\(^\\|:\\)han\\>" euc-kr)
1553   ("\\(^\\|:\\)alt.chinese.text.big5\\>" chinese-big5)
1554   ("\\(^\\|:\\)soc.culture.vietnamese\\>" vietnamese-viqr)
1555   ("\\(^\\|:\\)\\(comp\\|rec\\|alt\\|sci\\|soc\\|news\\|gnu\\|bofh\\)\\>" iso-8859-1))
1556 :variable-document
1557 "Alist of regexps (to match group names) and charsets to be used when reading."
1558 :variable-group gnus-charset
1559 :variable-type '(repeat (list (regexp :tag "Group")
1560			       (symbol :tag "Charset")))
1561 :parameter-type '(symbol :tag "Charset")
1562 :parameter-document "\
1563The default charset to use in the group.")
1564
1565(gnus-define-group-parameter
1566 post-method
1567 :type list
1568 :function-document
1569 "Return a posting method for GROUP."
1570 :variable gnus-post-method-alist
1571 :variable-document
1572 "Alist of regexps (to match group names) and method to be used when
1573posting an article."
1574 :variable-group gnus-group-foreign
1575 :parameter-type
1576 '(choice :tag "Posting Method"
1577	  (const :tag "Use native server" native)
1578	  (const :tag "Use current server" current)
1579	  (list :convert-widget
1580		(lambda (widget)
1581		  (list 'sexp :tag "Methods"
1582			:value gnus-select-method))))
1583 :parameter-document
1584 "Posting method for this group.")
1585
1586(gnus-define-group-parameter
1587 large-newsgroup-initial
1588 :type integer
1589 :function-document
1590 "Return GROUP's initial input of the number of articles."
1591 :variable-document
1592 "Alist of group regexps and its initial input of the number of articles."
1593 :variable-group gnus-group-parameter
1594 :parameter-type '(choice :tag "Initial Input for Large Newsgroup"
1595			  (const :tag "All" 'all)
1596			  (integer))
1597 :parameter-document "\
1598
1599This number will be prompted as the initial value of the number of
1600articles to list when the group is a large newsgroup (see
1601`gnus-large-newsgroup').  If it is nil, the default value is the
1602total number of articles in the group.")
1603
1604;; The Gnus registry's ignored groups
1605(gnus-define-group-parameter
1606 registry-ignore
1607 :type list
1608 :function-document
1609 "Whether this group should be ignored by the registry."
1610 :variable gnus-registry-ignored-groups
1611 :variable-default (mapcar
1612                    (lambda (g) (list g t))
1613                    '("delayed$" "drafts$" "queue$" "INBOX$"
1614                      "^nnmairix:" "^nnselect:" "archive"))
1615 :variable-document
1616 "Groups in which the registry should be turned off."
1617 :variable-group gnus-registry
1618 :variable-type '(repeat
1619		  (list
1620		   (regexp :tag "Group Name Regular Expression")
1621		   (boolean :tag "Ignored")))
1622
1623 :parameter-type '(boolean :tag "Group Ignored by the Registry")
1624 :parameter-document
1625 "Whether the Gnus Registry should ignore this group.")
1626
1627;; group parameters for spam processing added by Ted Zlatanov <tzz@lifelogs.com>
1628(defcustom gnus-install-group-spam-parameters t
1629  "Disable the group parameters for spam detection.
1630Enable if `G c' in XEmacs is giving you trouble, and make sure to
1631submit a bug report."
1632  :version "22.1"
1633  :type 'boolean
1634  :group 'gnus-start)
1635
1636(when gnus-install-group-spam-parameters
1637  (defvar gnus-group-spam-classification-spam t
1638    "Spam group classification (requires spam.el).
1639This group contains spam messages.  On summary entry, unread messages
1640will be marked as spam.  On summary exit, the specified spam
1641processors will be invoked on spam-marked messages, then those
1642messages will be expired, so the spam processor will only see a
1643spam-marked message once.")
1644
1645  (defvar gnus-group-spam-classification-ham 'ask
1646    "The ham value for the spam group parameter (requires spam.el).
1647On summary exit, the specified ham processors will be invoked on
1648ham-marked messages.  Exercise caution, since the ham processor will
1649see the same message more than once because there is no ham message
1650registry.")
1651
1652  (gnus-define-group-parameter
1653   spam-contents
1654   :type list
1655   :function-document
1656   "The spam type (spam, ham, or neither) of the group."
1657   :variable gnus-spam-newsgroup-contents
1658   :variable-default nil
1659   :variable-document
1660   "Group classification (spam, ham, or neither).  Only
1661meaningful when spam.el is loaded.  If non-nil, this should be a
1662list of group name regexps associated with a classification for
1663each one.  In spam groups, new articles are marked as spam on
1664summary entry.  There is other behavior associated with ham and
1665no classification when spam.el is loaded - see the manual."
1666   :variable-group spam
1667   :variable-type '(repeat
1668		    (list :tag "Group contents spam/ham classification"
1669			  (regexp :tag "Group")
1670			  (choice
1671			   (variable-item gnus-group-spam-classification-spam)
1672			   (variable-item gnus-group-spam-classification-ham)
1673			   (const :tag "Unclassified" nil))))
1674
1675   :parameter-type '(list :tag "Group contents spam/ham classification"
1676			  (choice :tag "Group contents classification for spam sorting"
1677				  (variable-item gnus-group-spam-classification-spam)
1678				  (variable-item gnus-group-spam-classification-ham)
1679				  (const :tag "Unclassified" nil)))
1680   :parameter-document
1681   "The spam classification (spam, ham, or neither) of this group.
1682When a spam group is entered, all unread articles are marked as
1683spam.  There is other behavior associated with ham and no
1684classification when spam.el is loaded - see the manual.")
1685
1686  (gnus-define-group-parameter
1687   spam-resend-to
1688   :type list
1689   :function-document
1690   "The address to get spam resent (through spam-report-resend)."
1691   :variable gnus-spam-resend-to
1692   :variable-default nil
1693   :variable-document
1694   "The address to get spam resent (through spam-report-resend)."
1695   :variable-group spam
1696   :variable-type '(repeat
1697		    (list :tag "Group address for resending spam"
1698			  (regexp :tag "Group")
1699			  (string :tag "E-mail address for resending spam (requires the spam-use-resend exit processor)")))
1700   :parameter-type 'string :tag "E-mail address for resending spam (requires the spam-use-resend exit processor)"
1701   :parameter-document
1702   "The address to get spam resent (through spam-report-resend).")
1703
1704  (gnus-define-group-parameter
1705   ham-resend-to
1706   :type list
1707   :function-document
1708   "The address to get ham resent (through spam-report-resend)."
1709   :variable gnus-ham-resend-to
1710   :variable-default nil
1711   :variable-document
1712   "The address to get ham resent (through spam-report-resend)."
1713   :variable-group spam
1714   :variable-type '(repeat
1715		    (list :tag "Group address for resending ham"
1716			  (regexp :tag "Group")
1717			  (string :tag "E-mail address for resending ham (requires the spam-use-resend exit processor)")))
1718   :parameter-type 'string :tag "E-mail address for resending ham (requires the spam-use-resend exit processor)"
1719   :parameter-document
1720   "The address to get ham resent (through spam-report-resend).")
1721
1722  (defvar gnus-group-spam-exit-processor-ifile "ifile"
1723    "OBSOLETE: The ifile summary exit spam processor.")
1724
1725  (defvar gnus-group-spam-exit-processor-stat "stat"
1726    "OBSOLETE: The spam-stat summary exit spam processor.")
1727
1728  (defvar gnus-group-spam-exit-processor-bogofilter "bogofilter"
1729    "OBSOLETE: The Bogofilter summary exit spam processor.")
1730
1731  (defvar gnus-group-spam-exit-processor-blacklist "blacklist"
1732    "OBSOLETE: The Blacklist summary exit spam processor.")
1733
1734  (defvar gnus-group-spam-exit-processor-report-gmane "report-gmane"
1735    "OBSOLETE: The Gmane reporting summary exit spam processor.
1736Only applicable to NNTP groups with articles from Gmane.  See spam-report.el")
1737
1738  (defvar gnus-group-spam-exit-processor-spamoracle "spamoracle-spam"
1739    "OBSOLETE: The spamoracle summary exit spam processor.")
1740
1741  (defvar gnus-group-ham-exit-processor-ifile "ifile-ham"
1742    "OBSOLETE: The ifile summary exit ham processor.
1743Only applicable to non-spam (unclassified and ham) groups.")
1744
1745  (defvar gnus-group-ham-exit-processor-bogofilter "bogofilter-ham"
1746    "OBSOLETE: The Bogofilter summary exit ham processor.
1747Only applicable to non-spam (unclassified and ham) groups.")
1748
1749  (defvar gnus-group-ham-exit-processor-stat "stat-ham"
1750    "OBSOLETE: The spam-stat summary exit ham processor.
1751Only applicable to non-spam (unclassified and ham) groups.")
1752
1753  (defvar gnus-group-ham-exit-processor-whitelist "whitelist"
1754    "OBSOLETE: The whitelist summary exit ham processor.
1755Only applicable to non-spam (unclassified and ham) groups.")
1756
1757  (defvar gnus-group-ham-exit-processor-BBDB "bbdb"
1758    "OBSOLETE: The BBDB summary exit ham processor.
1759Only applicable to non-spam (unclassified and ham) groups.")
1760
1761  (defvar gnus-group-ham-exit-processor-copy "copy"
1762    "OBSOLETE: The ham copy exit ham processor.
1763Only applicable to non-spam (unclassified and ham) groups.")
1764
1765  (defvar gnus-group-ham-exit-processor-spamoracle "spamoracle-ham"
1766    "OBSOLETE: The spamoracle summary exit ham processor.
1767Only applicable to non-spam (unclassified and ham) groups.")
1768
1769  (gnus-define-group-parameter
1770   spam-process
1771   :type list
1772   :parameter-type
1773   '(choice
1774     :tag "Spam Summary Exit Processor"
1775     :value nil
1776     (list :tag "Spam Summary Exit Processor Choices"
1777	   (set
1778	    (const :tag "Spam: Bogofilter"    (spam spam-use-bogofilter))
1779	    (const :tag "Spam: Blacklist"     (spam spam-use-blacklist))
1780	    (const :tag "Spam: Bsfilter"      (spam spam-use-bsfilter))
1781	    (const :tag "Spam: Gmane Report"  (spam spam-use-gmane))
1782	    (const :tag "Spam: Resend Message"(spam spam-use-resend))
1783	    (const :tag "Spam: ifile"	      (spam spam-use-ifile))
1784	    (const :tag "Spam: Spam Oracle"   (spam spam-use-spamoracle))
1785	    (const :tag "Spam: Spam-stat"     (spam spam-use-stat))
1786	    (const :tag "Spam: SpamAssassin"  (spam spam-use-spamassassin))
1787	    (const :tag "Spam: CRM114"        (spam spam-use-crm114))
1788	    (const :tag "Ham: BBDB"	      (ham spam-use-BBDB))
1789	    (const :tag "Ham: Bogofilter"     (ham spam-use-bogofilter))
1790	    (const :tag "Ham: Bsfilter"       (ham spam-use-bsfilter))
1791	    (const :tag "Ham: Copy"	      (ham spam-use-ham-copy))
1792	    (const :tag "Ham: Resend Message" (ham spam-use-resend))
1793	    (const :tag "Ham: ifile"	      (ham spam-use-ifile))
1794	    (const :tag "Ham: Spam Oracle"    (ham spam-use-spamoracle))
1795	    (const :tag "Ham: Spam-stat"      (ham spam-use-stat))
1796	    (const :tag "Ham: SpamAssassin"   (ham spam-use-spamassassin))
1797	    (const :tag "Ham: CRM114"         (ham spam-use-crm114))
1798	    (const :tag "Ham: Whitelist"      (ham spam-use-whitelist))
1799	    (variable-item gnus-group-spam-exit-processor-ifile)
1800	    (variable-item gnus-group-spam-exit-processor-stat)
1801	    (variable-item gnus-group-spam-exit-processor-bogofilter)
1802	    (variable-item gnus-group-spam-exit-processor-blacklist)
1803	    (variable-item gnus-group-spam-exit-processor-spamoracle)
1804	    (variable-item gnus-group-spam-exit-processor-report-gmane)
1805	    (variable-item gnus-group-ham-exit-processor-bogofilter)
1806	    (variable-item gnus-group-ham-exit-processor-ifile)
1807	    (variable-item gnus-group-ham-exit-processor-stat)
1808	    (variable-item gnus-group-ham-exit-processor-whitelist)
1809	    (variable-item gnus-group-ham-exit-processor-BBDB)
1810	    (variable-item gnus-group-ham-exit-processor-spamoracle)
1811	    (variable-item gnus-group-ham-exit-processor-copy))))
1812   :function-document
1813   "Which spam or ham processors will be applied when the summary is exited."
1814   :variable gnus-spam-process-newsgroups
1815   :variable-default nil
1816   :variable-document
1817   "Groups in which to automatically process spam or ham articles with
1818a backend on summary exit.  If non-nil, this should be a list of group
1819name regexps that should match all groups in which to do automatic
1820spam processing, associated with the appropriate processor."
1821   :variable-group spam
1822   :variable-type
1823   '(repeat :tag "Spam/Ham Processors"
1824	    (list :tag "Spam Summary Exit Processor Choices"
1825		  (regexp :tag "Group Regexp")
1826		  (set
1827		   :tag "Spam/Ham Summary Exit Processor"
1828		   (const :tag "Spam: Bogofilter"    (spam spam-use-bogofilter))
1829		   (const :tag "Spam: Blacklist"     (spam spam-use-blacklist))
1830		   (const :tag "Spam: Bsfilter"	     (spam spam-use-bsfilter))
1831		   (const :tag "Spam: Gmane Report"  (spam spam-use-gmane))
1832		   (const :tag "Spam: Resend Message"(spam spam-use-resend))
1833		   (const :tag "Spam: ifile"	     (spam spam-use-ifile))
1834		   (const :tag "Spam: Spam-stat"     (spam spam-use-stat))
1835		   (const :tag "Spam: Spam Oracle"   (spam spam-use-spamoracle))
1836		   (const :tag "Spam: SpamAssassin"  (spam spam-use-spamassassin))
1837		   (const :tag "Spam: CRM114"        (spam spam-use-crm114))
1838		   (const :tag "Ham: BBDB"	     (ham spam-use-BBDB))
1839		   (const :tag "Ham: Bogofilter"     (ham spam-use-bogofilter))
1840		   (const :tag "Ham: Bsfilter"	     (ham spam-use-bsfilter))
1841		   (const :tag "Ham: Copy"	     (ham spam-use-ham-copy))
1842		   (const :tag "Ham: Resend Message" (ham spam-use-resend))
1843		   (const :tag "Ham: ifile"	     (ham spam-use-ifile))
1844		   (const :tag "Ham: Spam-stat"	     (ham spam-use-stat))
1845		   (const :tag "Ham: Spam Oracle"    (ham spam-use-spamoracle))
1846		   (const :tag "Ham: SpamAssassin"   (ham spam-use-spamassassin))
1847		   (const :tag "Ham: CRM114"         (ham spam-use-crm114))
1848		   (const :tag "Ham: Whitelist"	     (ham spam-use-whitelist))
1849		   (variable-item gnus-group-spam-exit-processor-ifile)
1850		   (variable-item gnus-group-spam-exit-processor-stat)
1851		   (variable-item gnus-group-spam-exit-processor-bogofilter)
1852		   (variable-item gnus-group-spam-exit-processor-blacklist)
1853		   (variable-item gnus-group-spam-exit-processor-spamoracle)
1854		   (variable-item gnus-group-spam-exit-processor-report-gmane)
1855		   (variable-item gnus-group-ham-exit-processor-bogofilter)
1856		   (variable-item gnus-group-ham-exit-processor-ifile)
1857		   (variable-item gnus-group-ham-exit-processor-stat)
1858		   (variable-item gnus-group-ham-exit-processor-whitelist)
1859		   (variable-item gnus-group-ham-exit-processor-BBDB)
1860		   (variable-item gnus-group-ham-exit-processor-spamoracle)
1861		   (variable-item gnus-group-ham-exit-processor-copy))))
1862
1863   :parameter-document
1864   "Which spam or ham processors will be applied when the summary is exited.")
1865
1866  (gnus-define-group-parameter
1867   spam-autodetect
1868   :type list
1869   :parameter-type
1870   '(boolean :tag "Spam autodetection")
1871   :function-document
1872   "Should spam be autodetected (with spam-split) in this group?"
1873   :variable gnus-spam-autodetect
1874   :variable-default nil
1875   :variable-document
1876   "Groups in which spam should be autodetected when they are entered.
1877   Only unseen articles will be examined, unless
1878   spam-autodetect-recheck-messages is set."
1879   :variable-group spam
1880   :variable-type
1881   '(repeat
1882     :tag "Autodetection setting"
1883     (list
1884      (regexp :tag "Group Regexp")
1885      boolean))
1886   :parameter-document
1887   "Spam autodetection.
1888Only unseen articles will be examined, unless
1889spam-autodetect-recheck-messages is set.")
1890
1891  (gnus-define-group-parameter
1892   spam-autodetect-methods
1893   :type list
1894   :parameter-type
1895   '(choice :tag "Spam autodetection-specific methods"
1896     (const none)
1897     (const default)
1898     (set :tag "Use specific methods"
1899	  (variable-item spam-use-blacklist)
1900	  (variable-item spam-use-gmane-xref)
1901	  (variable-item spam-use-regex-headers)
1902	  (variable-item spam-use-regex-body)
1903	  (variable-item spam-use-whitelist)
1904	  (variable-item spam-use-BBDB)
1905	  (variable-item spam-use-ifile)
1906	  (variable-item spam-use-spamoracle)
1907	  (variable-item spam-use-crm114)
1908	  (variable-item spam-use-spamassassin)
1909	  (variable-item spam-use-spamassassin-headers)
1910	  (variable-item spam-use-bsfilter)
1911	  (variable-item spam-use-bsfilter-headers)
1912	  (variable-item spam-use-stat)
1913	  (variable-item spam-use-blackholes)
1914	  (variable-item spam-use-hashcash)
1915	  (variable-item spam-use-bogofilter-headers)
1916	  (variable-item spam-use-bogofilter)))
1917   :function-document
1918   "Methods to be used for autodetection in each group"
1919   :variable gnus-spam-autodetect-methods
1920   :variable-default nil
1921   :variable-document
1922   "Methods for autodetecting spam per group.
1923Requires the spam-autodetect parameter.  Only unseen articles
1924will be examined, unless spam-autodetect-recheck-messages is
1925set."
1926   :variable-group spam
1927   :variable-type
1928   '(repeat
1929     :tag "Autodetection methods"
1930     (list
1931      (regexp :tag "Group Regexp")
1932      (choice
1933       (const none)
1934       (const default)
1935       (set :tag "Use specific methods"
1936	(variable-item spam-use-blacklist)
1937	(variable-item spam-use-gmane-xref)
1938	(variable-item spam-use-regex-headers)
1939	(variable-item spam-use-regex-body)
1940	(variable-item spam-use-whitelist)
1941	(variable-item spam-use-BBDB)
1942	(variable-item spam-use-ifile)
1943	(variable-item spam-use-spamoracle)
1944	(variable-item spam-use-crm114)
1945	(variable-item spam-use-stat)
1946	(variable-item spam-use-blackholes)
1947	(variable-item spam-use-hashcash)
1948	(variable-item spam-use-spamassassin)
1949	(variable-item spam-use-spamassassin-headers)
1950	(variable-item spam-use-bsfilter)
1951	(variable-item spam-use-bsfilter-headers)
1952	(variable-item spam-use-bogofilter-headers)
1953	(variable-item spam-use-bogofilter)))))
1954     :parameter-document
1955   "Spam autodetection methods.
1956Requires the spam-autodetect parameter.  Only unseen articles
1957will be examined, unless spam-autodetect-recheck-messages is
1958set.")
1959
1960  (gnus-define-group-parameter
1961   spam-process-destination
1962   :type list
1963   :parameter-type
1964   '(choice :tag "Destination for spam-processed articles at summary exit"
1965	    (string :tag "Move to a group")
1966	    (repeat :tag "Move to multiple groups"
1967		    (string :tag "Destination group"))
1968	    (const :tag "Expire" nil))
1969   :function-document
1970   "Where spam-processed articles will go at summary exit."
1971   :variable gnus-spam-process-destinations
1972   :variable-default nil
1973   :variable-document
1974   "Groups in which to explicitly send spam-processed articles to
1975another group, or expire them (the default).  If non-nil, this should
1976be a list of group name regexps that should match all groups in which
1977to do spam-processed article moving, associated with the destination
1978group or nil for explicit expiration.  This only makes sense for
1979mail groups."
1980   :variable-group spam
1981   :variable-type
1982   '(repeat
1983     :tag "Spam-processed articles destination"
1984     (list
1985      (regexp :tag "Group Regexp")
1986      (choice
1987       :tag "Destination for spam-processed articles at summary exit"
1988       (string :tag "Move to a group")
1989       (repeat :tag "Move to multiple groups"
1990	       (string :tag "Destination group"))
1991       (const :tag "Expire" nil))))
1992   :parameter-document
1993   "Where spam-processed articles will go at summary exit.")
1994
1995  (gnus-define-group-parameter
1996   ham-process-destination
1997   :type list
1998   :parameter-type
1999   '(choice
2000     :tag "Destination for ham articles at summary exit from a spam group"
2001     (string :tag "Move to a group")
2002     (repeat :tag "Move to multiple groups"
2003	     (string :tag "Destination group"))
2004     (const :tag "Respool" respool)
2005     (const :tag "Do nothing" nil))
2006   :function-document
2007   "Where ham articles will go at summary exit from a spam group."
2008   :variable gnus-ham-process-destinations
2009   :variable-default nil
2010   :variable-document
2011   "Groups in which to explicitly send ham articles to
2012another group, or do nothing (the default).  If non-nil, this should
2013be a list of group name regexps that should match all groups in which
2014to do ham article moving, associated with the destination
2015group or nil for explicit ignoring.  This only makes sense for
2016mail groups, and only works in spam groups."
2017   :variable-group spam
2018   :variable-type
2019   '(repeat
2020     :tag "Ham articles destination"
2021     (list
2022      (regexp :tag "Group Regexp")
2023      (choice
2024       :tag "Destination for ham articles at summary exit from spam group"
2025       (string :tag "Move to a group")
2026       (repeat :tag "Move to multiple groups"
2027		(string :tag "Destination group"))
2028       (const :tag "Respool" respool)
2029       (const :tag "Expire" nil))))
2030   :parameter-document
2031   "Where ham articles will go at summary exit from a spam group.")
2032
2033  (gnus-define-group-parameter
2034   ham-marks
2035   :type 'list
2036   :parameter-type '(list :tag "Ham mark choices"
2037			  (set
2038			   (variable-item gnus-del-mark)
2039			   (variable-item gnus-read-mark)
2040			   (variable-item gnus-ticked-mark)
2041			   (variable-item gnus-killed-mark)
2042			   (variable-item gnus-kill-file-mark)
2043			   (variable-item gnus-low-score-mark)))
2044
2045   :parameter-document
2046   "Marks considered ham (positively not spam).  Such articles will be
2047processed as ham (non-spam) on group exit.  When nil, the global
2048spam-ham-marks variable takes precedence."
2049   :variable-default '((".*" ((gnus-del-mark
2050			       gnus-read-mark
2051			       gnus-killed-mark
2052			       gnus-kill-file-mark
2053			       gnus-low-score-mark))))
2054   :variable-group spam
2055   :variable-document
2056   "Groups in which to explicitly set the ham marks to some value.")
2057
2058  (gnus-define-group-parameter
2059   spam-marks
2060   :type 'list
2061   :parameter-type '(list :tag "Spam mark choices"
2062			  (set
2063			   (variable-item gnus-spam-mark)
2064			   (variable-item gnus-killed-mark)
2065			   (variable-item gnus-kill-file-mark)
2066			   (variable-item gnus-low-score-mark)))
2067
2068   :parameter-document
2069   "Marks considered spam.
2070Such articles will be processed as spam on group exit.  When nil, the global
2071spam-spam-marks variable takes precedence."
2072   :variable-default '((".*" ((gnus-spam-mark))))
2073   :variable-group spam
2074   :variable-document
2075   "Groups in which to explicitly set the spam marks to some value."))
2076
2077(defcustom gnus-group-uncollapsed-levels 1
2078  "Number of group name elements to leave alone when making a short group name."
2079  :group 'gnus-group-visual
2080  :type 'integer)
2081
2082(defcustom gnus-group-use-permanent-levels nil
2083  "If non-nil, once you set a level, Gnus will use this level."
2084  :group 'gnus-group-levels
2085  :type 'boolean)
2086
2087;; Hooks.
2088
2089(defcustom gnus-load-hook nil
2090  "A hook run while Gnus is loaded."
2091  :group 'gnus-start
2092  :type 'hook)
2093
2094(defcustom gnus-apply-kill-hook '(gnus-apply-kill-file)
2095  "A hook called to apply kill files to a group.
2096This hook is intended to apply a kill file to the selected newsgroup.
2097The function `gnus-apply-kill-file' is called by default.
2098
2099Since a general kill file is too heavy to use only for a few
2100newsgroups, I recommend you to use a lighter hook function.  For
2101example, if you'd like to apply a kill file to articles which contains
2102a string `rmgroup' in subject in newsgroup `control', you can use the
2103following hook:
2104
2105 (setq gnus-apply-kill-hook
2106      (list
2107	(lambda ()
2108	  (cond ((string-match \"control\" gnus-newsgroup-name)
2109		 (gnus-kill \"Subject\" \"rmgroup\")
2110		 (gnus-expunge \"X\"))))))"
2111  :group 'gnus-score-kill
2112  :options '(gnus-apply-kill-file)
2113  :type 'hook)
2114
2115(defcustom gnus-group-change-level-function nil
2116  "Function run when a group level is changed.
2117It is called with three parameters -- GROUP, LEVEL and OLDLEVEL."
2118  :group 'gnus-group-levels
2119  :type '(choice (const nil)
2120		 function))
2121
2122;;; Face thingies.
2123
2124(defcustom gnus-visual
2125  '(summary-highlight group-highlight article-highlight
2126		      mouse-face
2127		      summary-menu group-menu article-menu
2128		      tree-highlight menu highlight
2129		      browse-menu server-menu
2130		      page-marker tree-menu binary-menu pick-menu)
2131  "Enable visual features.
2132If `visual' is disabled, there will be no menus and few faces.  Most of
2133the visual customization options below will be ignored.  Gnus will use
2134less space and be faster as a result.
2135
2136This variable can also be a list of visual elements to switch on.  For
2137instance, to switch off all visual things except menus, you can say:
2138
2139   (setq gnus-visual \\='(menu))
2140
2141Valid elements include `summary-highlight', `group-highlight',
2142`article-highlight', `mouse-face', `summary-menu', `group-menu',
2143`article-menu', `tree-highlight', `menu', `highlight',
2144`browse-menu', `server-menu', `page-marker', `tree-menu',
2145`binary-menu', and `pick-menu'."
2146  :group 'gnus-meta
2147  :group 'gnus-visual
2148  :type '(set (const summary-highlight)
2149	      (const group-highlight)
2150	      (const article-highlight)
2151	      (const mouse-face)
2152	      (const summary-menu)
2153	      (const group-menu)
2154	      (const article-menu)
2155	      (const tree-highlight)
2156	      (const menu)
2157	      (const highlight)
2158	      (const browse-menu)
2159	      (const server-menu)
2160	      (const page-marker)
2161	      (const tree-menu)
2162	      (const binary-menu)
2163	      (const pick-menu)))
2164
2165;; Byte-compiler warning.
2166(defvar gnus-visual)
2167;; Find out whether the gnus-visual TYPE is wanted.
2168(defun gnus-visual-p (&optional type class)
2169  (and gnus-visual			; Has to be non-nil, at least.
2170       (if (not type)			; We don't care about type.
2171	   gnus-visual
2172	 (if (listp gnus-visual)	; It's a list, so we check it.
2173	     (or (memq type gnus-visual)
2174		 (memq class gnus-visual))
2175	   t))))
2176
2177(defcustom gnus-mouse-face
2178  (condition-case ()
2179      (if (gnus-visual-p 'mouse-face 'highlight)
2180	  (if (boundp 'gnus-mouse-face)
2181	      (or gnus-mouse-face 'highlight)
2182	    'highlight)
2183	'default)
2184    (error 'highlight))
2185  "Face used for group or summary buffer mouse highlighting.
2186The line beneath the mouse pointer will be highlighted with this
2187face."
2188  :group 'gnus-visual
2189  :type 'face)
2190
2191(defcustom gnus-article-save-directory gnus-directory
2192  "Name of the directory articles will be saved in (default \"~/News\")."
2193  :group 'gnus-article-saving
2194  :type 'directory)
2195
2196(defvar gnus-plugged t
2197  "Whether Gnus is plugged or not.")
2198
2199(defcustom gnus-agent-cache t
2200  "Controls use of the agent cache while plugged.
2201When set, Gnus will prefer using the locally stored content rather
2202than re-fetching it from the server.  You also need to enable
2203`gnus-agent' for this to have any affect."
2204  :version "22.1"
2205  :group 'gnus-agent
2206  :type 'boolean)
2207
2208(defcustom gnus-default-charset 'undecided
2209  "Default charset assumed to be used when viewing non-ASCII characters.
2210This variable is overridden on a group-to-group basis by the
2211`gnus-group-charset-alist' variable and is only used on groups not
2212covered by that variable."
2213  :type 'symbol
2214  :group 'gnus-charset)
2215
2216;; Fixme: Doc reference to agent.
2217(defcustom gnus-agent t
2218  "Whether we want to use the Gnus agent or not.
2219
2220You may customize `gnus-agent' to disable its use.  However, some
2221back ends have started to use the agent as a client-side cache.
2222Disabling the agent may result in noticeable loss of performance."
2223  :version "22.1"
2224  :group 'gnus-agent
2225  :type 'boolean)
2226
2227(defcustom gnus-other-frame-function #'gnus
2228  "Function called by the command `gnus-other-frame' when starting Gnus."
2229  :group 'gnus-start
2230  :type '(choice (function-item gnus)
2231		 (function-item gnus-no-server)
2232		 (function-item gnus-child)
2233		 (function-item gnus-child-no-server)))
2234
2235(declare-function gnus-group-get-new-news "gnus-group")
2236
2237(defcustom gnus-other-frame-resume-function #'gnus-group-get-new-news
2238  "Function called by the command `gnus-other-frame' when resuming Gnus."
2239  :version "24.4"
2240  :group 'gnus-start
2241  :type '(choice (function-item gnus)
2242		 (function-item gnus-group-get-new-news)
2243		 (function-item gnus-no-server)
2244		 (function-item gnus-child)
2245		 (function-item gnus-child-no-server)))
2246
2247(defcustom gnus-other-frame-parameters nil
2248  "Frame parameters used by `gnus-other-frame' to create a Gnus frame."
2249  :group 'gnus-start
2250  :type '(repeat (cons :format "%v"
2251		       (symbol :tag "Parameter")
2252		       (sexp :tag "Value"))))
2253
2254(defcustom gnus-user-agent '(emacs gnus type)
2255  "Which information should be exposed in the User-Agent header.
2256
2257Can be a list of symbols or a string.  Valid symbols are `gnus'
2258\(show Gnus version) and `emacs' \(show Emacs version).  In
2259addition to the Emacs version, you can add `codename' \(show
2260\(S)XEmacs codename) or either `config' \(show system
2261configuration) or `type' \(show system type).  If you set it to
2262a string, be sure to use a valid format, see RFC 2616."
2263
2264  :version "22.1"
2265  :group 'gnus-message
2266  :type '(choice (list (set :inline t
2267			    (const gnus  :tag "Gnus version")
2268			    (const emacs :tag "Emacs version")
2269			    (choice :tag "system"
2270				    (const type   :tag "system type")
2271				    (const config :tag "system configuration"))
2272			    (const codename :tag "Emacs codename")))
2273		 (string)))
2274
2275;; Convert old (< 2005-01-10) symbol type values:
2276(when (symbolp gnus-user-agent)
2277  (setq gnus-user-agent
2278	(cond ((eq gnus-user-agent 'emacs-gnus-config)
2279	       '(emacs gnus config))
2280	      ((eq gnus-user-agent 'emacs-gnus-type)
2281	       '(emacs gnus type))
2282	      ((eq gnus-user-agent 'emacs-gnus)
2283	       '(emacs gnus))
2284	      ((eq gnus-user-agent 'gnus)
2285	       '(gnus))
2286	      (t gnus-user-agent)))
2287  (gnus-message 1 "Converted `gnus-user-agent' to `%s'." gnus-user-agent)
2288  (sit-for 1)
2289  (if (get 'gnus-user-agent 'saved-value)
2290      (customize-save-variable 'gnus-user-agent gnus-user-agent)
2291    (gnus-message 1 "Edit your init file to make this change permanent.")
2292    (sit-for 2)))
2293
2294(defcustom gnus-agent-eagerly-store-articles t
2295  "If non-nil, cache articles eagerly.
2296
2297When using the Gnus Agent and reading an agentized newsgroup,
2298automatically cache the article in the agent cache."
2299  :type 'boolean
2300  :version "28.1")
2301
2302
2303;;; Internal variables
2304
2305(defvar gnus-agent-gcc-header "X-Gnus-Agent-Gcc")
2306(defvar gnus-agent-meta-information-header "X-Gnus-Agent-Meta-Information")
2307(defvar gnus-agent-method-p-cache nil
2308  ; Reset each time gnus-agent-covered-methods is changed else
2309  ; gnus-agent-method-p may mis-report a methods status.
2310  )
2311(defvar gnus-agent-target-move-group-header "X-Gnus-Agent-Move-To")
2312(defvar gnus-draft-meta-information-header "X-Draft-From")
2313(defvar gnus-group-get-parameter-function #'gnus-group-get-parameter)
2314(defvar gnus-original-article-buffer " *Original Article*")
2315(defvar gnus-newsgroup-name nil)
2316(defvar gnus-ephemeral-servers nil)
2317(defvar gnus-server-method-cache nil)
2318(defvar gnus-extended-servers nil)
2319
2320;; The carpal mode has been removed, but define the variable for
2321;; backwards compatibility.
2322(defvar gnus-carpal nil)
2323(make-obsolete-variable 'gnus-carpal nil "24.1")
2324
2325(defvar gnus-agent-fetching nil
2326  "Whether Gnus agent is in fetching mode.")
2327
2328(defvar gnus-agent-covered-methods nil
2329  "A list of servers, NOT methods, showing which servers are covered by the agent.")
2330
2331(defvar gnus-command-method nil
2332  "Dynamically bound variable that says what the current back end is.")
2333
2334(defvar gnus-current-select-method nil
2335  "The current method for selecting a newsgroup.")
2336
2337(defvar gnus-tree-buffer "*Tree*"
2338  "Buffer where Gnus thread trees are displayed.")
2339
2340;; Variable holding the user answers to all method prompts.
2341(defvar gnus-method-history nil)
2342
2343;; Variable holding the user answers to all mail method prompts.
2344(defvar gnus-mail-method-history nil)
2345
2346;; Variable holding the user answers to all group prompts.
2347(defvar gnus-group-history nil)
2348
2349(defvar gnus-server-alist nil
2350  "Servers created by Gnus, or via the server buffer.
2351Servers defined in the user's config files do not appear here.
2352This variable is persisted in the user's .newsrc.eld file.")
2353
2354(defcustom gnus-cache-directory
2355  (nnheader-concat gnus-directory "cache/")
2356  "The directory where cached articles will be stored."
2357  :group 'gnus-cache
2358  :type 'directory)
2359
2360(defvar gnus-predefined-server-alist
2361  `(("cache"
2362     nnspool "cache"
2363     (nnspool-spool-directory ,gnus-cache-directory)
2364     (nnspool-nov-directory ,gnus-cache-directory)
2365     (nnspool-active-file
2366      ,(nnheader-concat gnus-cache-directory "active"))))
2367  "List of predefined (convenience) servers.")
2368
2369(defconst gnus-article-mark-lists
2370  '((marked . tick) (replied . reply)
2371    (expirable . expire) (killed . killed)
2372    (bookmarks . bookmark) (dormant . dormant)
2373    (scored . score) (saved . save)
2374    (cached . cache) (downloadable . download)
2375    (unsendable . unsend) (forwarded . forward)
2376    (seen . seen) (unexist . unexist)))
2377
2378(defconst gnus-article-special-mark-lists
2379  '((seen range)
2380    (unexist range)
2381    (killed range)
2382    (bookmark tuple)
2383    (uid tuple)
2384    (active tuple)
2385    (score tuple)))
2386
2387;; Propagate flags to server, with the following exceptions:
2388;; `seen' is private to each gnus installation
2389;; `cache' is an internal gnus flag for each gnus installation
2390;; `download' is an agent flag private to each gnus installation
2391;; `unsend' are for nndraft groups only
2392;; `score' is not a proper mark
2393;; `bookmark': don't propagate it, or fix the bug in update-mark.
2394(defconst gnus-article-unpropagated-mark-lists
2395  '(seen cache download unsend score bookmark unexist)
2396  "Marks that shouldn't be propagated to back ends.
2397Typical marks are those that make no sense in a standalone back end,
2398such as a mark that says whether an article is stored in the cache
2399\(which doesn't make sense in a standalone back end).")
2400
2401(defvar gnus-headers-retrieved-by nil)
2402(defvar gnus-article-reply nil)
2403(defvar gnus-override-method nil)
2404(defvar gnus-opened-servers nil)
2405
2406(defvar gnus-current-kill-article nil)
2407
2408(defvar gnus-have-read-active-file nil)
2409
2410(defconst gnus-maintainer
2411  "submit@debbugs.gnu.org (The Gnus Bugfixing Girls + Boys)"
2412  "The mail address of the Gnus maintainers.")
2413
2414(defconst gnus-bug-package
2415  "emacs,gnus"
2416  "The package to use in the bug submission.")
2417
2418(defvar gnus-info-nodes
2419  '((gnus-group-mode "(gnus)Group Buffer")
2420    (gnus-summary-mode "(gnus)Summary Buffer")
2421    (gnus-article-mode "(gnus)Article Buffer")
2422    (gnus-server-mode "(gnus)Server Buffer")
2423    (gnus-browse-mode "(gnus)Browse Foreign Server")
2424    (gnus-tree-mode "(gnus)Tree Display"))
2425  "Alist of major modes and related Info nodes.")
2426
2427(defvar gnus-summary-buffer "*Summary*")
2428(defvar gnus-article-buffer "*Article*")
2429(defvar gnus-server-buffer "*Server*")
2430
2431(defvar gnus-child nil
2432  "Whether this Gnus is a child or not.")
2433
2434(defvar gnus-batch-mode nil
2435  "Whether this Gnus is running in batch mode or not.")
2436
2437(defvar gnus-variable-list
2438  '(gnus-newsrc-options gnus-newsrc-options-n
2439			gnus-newsrc-last-checked-date
2440			gnus-newsrc-alist gnus-server-alist
2441			gnus-killed-list gnus-zombie-list
2442			gnus-topic-topology gnus-topic-alist
2443			gnus-cloud-sequence
2444			gnus-cloud-covered-servers
2445			gnus-cloud-file-timestamps)
2446  "Gnus variables saved in the quick startup file.")
2447
2448(defvar gnus-newsrc-alist nil
2449  "Assoc list of read articles.
2450`gnus-newsrc-hashtb' should be kept so that both hold the same information.")
2451
2452(defvar gnus-registry-alist nil
2453  "Assoc list of registry data.
2454gnus-registry.el will populate this if it's loaded.")
2455
2456(defvar gnus-newsrc-hashtb nil
2457  "Hash table of `gnus-newsrc-alist'.")
2458
2459(defvar gnus-group-list nil
2460  "Ordered list of group names as strings.
2461This variable only exists to provide easy access to the ordering
2462of `gnus-newsrc-alist'.")
2463
2464(defvar gnus-killed-list nil
2465  "List of killed newsgroups.")
2466
2467(defvar gnus-killed-hashtb nil
2468  "Hash table equivalent of `gnus-killed-list'.
2469This is a hash table purely for the fast membership test: values
2470are always t.")
2471
2472(defvar gnus-zombie-list nil
2473  "List of almost dead newsgroups.")
2474
2475(defvar gnus-description-hashtb nil
2476  "Hash table mapping group names to their descriptions.")
2477
2478(defvar gnus-list-of-killed-groups nil
2479  "List of newsgroups that have recently been killed by the user.")
2480
2481(defvar gnus-active-hashtb nil
2482  "Hash table mapping group names to their active entry.")
2483
2484(defvar gnus-moderated-hashtb nil
2485  "Hash table of moderated groups.
2486This is a hash table purely for the fast membership test: values
2487are always t.")
2488
2489;; Save window configuration.
2490(defvar gnus-prev-winconf nil)
2491
2492(defvar gnus-reffed-article-number nil)
2493
2494(defvar gnus-dead-summary nil)
2495
2496(defvar gnus-invalid-group-regexp "[: `'\"/]\\|^$"
2497  "Regexp matching invalid groups.")
2498
2499(defvar gnus-other-frame-object nil
2500  "A frame object which will be created by `gnus-other-frame'.")
2501
2502;;; End of variables.
2503
2504;; Define some autoload functions Gnus might use.
2505(eval-and-compile
2506
2507  ;; This little mapcar goes through the list below and marks the
2508  ;; symbols in question as autoloaded functions.
2509  (mapc
2510   (lambda (package)
2511     (let ((interactive (nth 1 (memq ':interactive package))))
2512       (mapcar
2513	(lambda (function)
2514	  (let (type)
2515	    (when (consp function)
2516	      (setq type (cadr function))
2517	      (setq function (car function)))
2518	    (unless (fboundp function)
2519	      (autoload function (car package) nil interactive type))))
2520	(if (eq (nth 1 package) ':interactive)
2521	    (nthcdr 3 package)
2522	  (cdr package)))))
2523   '(("info" :interactive t Info-goto-node)
2524     ("qp" quoted-printable-decode-region quoted-printable-decode-string)
2525     ("ps-print" ps-print-preprint)
2526     ("message" :interactive (message-mode)
2527      message-send-and-exit message-yank-original)
2528     ("babel" babel-as-string)
2529     ("nnmail" nnmail-split-fancy nnmail-article-group)
2530     ("nnvirtual" nnvirtual-catchup-group nnvirtual-convert-headers)
2531     ("gnus-xmas" gnus-xmas-splash)
2532     ("score-mode" :interactive t gnus-score-mode gnus-score-edit-all-score)
2533     ("gnus-mh" gnus-summary-save-article-folder
2534      gnus-Folder-save-name gnus-folder-save-name)
2535     ("gnus-mh" :interactive (gnus-summary-mode) gnus-summary-save-in-folder)
2536     ("gnus-demon" gnus-demon-add-scanmail
2537      gnus-demon-add-rescan gnus-demon-add-scan-timestamps
2538      gnus-demon-add-disconnection gnus-demon-add-handler
2539      gnus-demon-remove-handler)
2540     ("gnus-demon" :interactive t
2541      gnus-demon-init gnus-demon-cancel)
2542     ("gnus-fun" gnus-convert-gray-x-face-to-xpm gnus-display-x-face-in-from
2543      gnus-convert-image-to-gray-x-face gnus-convert-face-to-png
2544      gnus-face-from-file)
2545     ("gnus-salt" gnus-highlight-selected-tree gnus-possibly-generate-tree
2546      gnus-tree-open gnus-tree-close)
2547     ("gnus-srvr" gnus-enter-server-buffer gnus-server-set-info
2548      gnus-server-server-name)
2549     ("gnus-srvr" gnus-browse-foreign-server)
2550     ("gnus-cite" :interactive (gnus-article-mode gnus-summary-mode)
2551      gnus-article-highlight-citation gnus-article-hide-citation-maybe
2552      gnus-article-hide-citation gnus-article-fill-cited-article
2553      gnus-article-hide-citation-in-followups
2554      gnus-article-fill-cited-long-lines)
2555     ("gnus-kill" gnus-kill gnus-apply-kill-file-internal
2556      gnus-kill-file-edit-file gnus-kill-file-raise-followups-to-author
2557      gnus-execute gnus-expunge gnus-batch-kill gnus-batch-score)
2558     ("gnus-registry" gnus-try-warping-via-registry
2559      gnus-registry-handle-action)
2560     ("gnus-cache" gnus-cache-possibly-enter-article gnus-cache-save-buffers
2561      gnus-cache-possibly-remove-articles gnus-cache-request-article
2562      gnus-cache-retrieve-headers gnus-cache-possibly-alter-active
2563      gnus-cache-enter-remove-article gnus-cached-article-p
2564      gnus-cache-open gnus-cache-close gnus-cache-update-article
2565      gnus-cache-articles-in-group)
2566     ("gnus-cache" :interactive (gnus-summary-mode)
2567      gnus-summary-insert-cached-articles gnus-cache-enter-article
2568      gnus-cache-remove-article gnus-summary-insert-cached-articles)
2569     ("gnus-cache" :interactive t gnus-jog-cache)
2570     ("gnus-score" :interactive t
2571      gnus-score-flush-cache gnus-score-close)
2572     ("gnus-score" :interactive (gnus-summary-mode)
2573      gnus-summary-increase-score gnus-summary-set-score
2574      gnus-summary-raise-thread gnus-summary-raise-same-subject
2575      gnus-summary-raise-score gnus-summary-raise-same-subject-and-select
2576      gnus-summary-lower-thread gnus-summary-lower-same-subject
2577      gnus-summary-lower-score gnus-summary-lower-same-subject-and-select
2578      gnus-summary-current-score gnus-score-delta-default
2579      gnus-possibly-score-headers gnus-score-followup-article
2580      gnus-score-followup-thread)
2581     ("gnus-score"
2582      (gnus-summary-score-map keymap) gnus-score-save gnus-score-headers
2583      gnus-current-score-file-nondirectory gnus-score-adaptive
2584      gnus-score-find-trace gnus-score-file-name)
2585     ("gnus-cus" :interactive (gnus-group-mode) gnus-group-customize)
2586     ("gnus-cus" :interactive (gnus-summary-mode) gnus-score-customize)
2587     ("gnus-topic" :interactive (gnus-group-mode) gnus-topic-mode)
2588     ("gnus-topic" gnus-topic-remove-group gnus-topic-set-parameters
2589      gnus-subscribe-topics)
2590     ("gnus-salt" :interactive (gnus-summary-mode)
2591      gnus-pick-mode gnus-binary-mode)
2592     ("gnus-uu" (gnus-uu-extract-map keymap) (gnus-uu-mark-map keymap))
2593     ("gnus-uu" :interactive (gnus-article-mode gnus-summary-mode)
2594      gnus-uu-digest-mail-forward gnus-uu-digest-post-forward
2595      gnus-uu-mark-series gnus-uu-mark-region gnus-uu-mark-buffer
2596      gnus-uu-mark-by-regexp gnus-uu-mark-all
2597      gnus-uu-mark-sparse gnus-uu-mark-thread gnus-uu-decode-uu
2598      gnus-uu-decode-uu-and-save gnus-uu-decode-unshar
2599      gnus-uu-decode-unshar-and-save gnus-uu-decode-save
2600      gnus-uu-decode-binhex gnus-uu-decode-uu-view
2601      gnus-uu-decode-uu-and-save-view gnus-uu-decode-unshar-view
2602      gnus-uu-decode-unshar-and-save-view gnus-uu-decode-save-view
2603      gnus-uu-decode-binhex-view gnus-uu-unmark-thread
2604      gnus-uu-mark-over gnus-uu-post-news gnus-uu-invert-processable
2605      gnus-uu-decode-postscript-and-save-view
2606      gnus-uu-decode-postscript-view gnus-uu-decode-postscript-and-save
2607      gnus-uu-decode-yenc gnus-uu-unmark-by-regexp gnus-uu-unmark-region
2608      gnus-uu-decode-postscript)
2609     ("gnus-uu" gnus-uu-delete-work-dir gnus-uu-unmark-thread)
2610     ("gnus-msg" (gnus-summary-send-map keymap)
2611      gnus-article-mail gnus-copy-article-buffer gnus-extended-version)
2612     ("gnus-msg" :interactive (gnus-group-mode)
2613      gnus-group-post-news gnus-group-mail gnus-group-news)
2614     ("gnus-msg" :interactive (gnus-summary-mode)
2615      gnus-summary-post-news gnus-summary-news-other-window
2616      gnus-summary-followup gnus-summary-followup-with-original
2617      gnus-summary-cancel-article gnus-summary-supersede-article
2618      gnus-summary-reply gnus-summary-reply-with-original
2619      gnus-summary-mail-forward gnus-summary-mail-other-window
2620      gnus-summary-resend-message gnus-summary-resend-bounced-mail
2621      gnus-summary-wide-reply gnus-summary-followup-to-mail
2622      gnus-summary-followup-to-mail-with-original gnus-bug
2623      gnus-summary-wide-reply-with-original
2624      gnus-summary-post-forward gnus-summary-wide-reply-with-original
2625      gnus-summary-post-forward)
2626     ("gnus-msg" gnus-post-news)
2627     ("gnus-picon" :interactive (gnus-article-mode gnus-summary-mode)
2628      gnus-treat-from-picon)
2629     ("smiley" :interactive t smiley-region)
2630     ("gnus-win" gnus-configure-windows gnus-add-configuration)
2631     ("gnus-sum" gnus-summary-insert-line gnus-summary-read-group
2632      gnus-list-of-unread-articles gnus-list-of-read-articles
2633      gnus-offer-save-summaries gnus-make-thread-indent-array
2634      gnus-summary-exit gnus-update-read-articles gnus-summary-last-subject
2635      gnus-summary-skip-intangible gnus-summary-article-number
2636      gnus-data-header gnus-data-find)
2637     ("gnus-group" gnus-group-insert-group-line gnus-group-quit
2638      gnus-group-list-groups gnus-group-first-unread-group
2639      gnus-group-set-mode-line gnus-group-set-info gnus-group-save-newsrc
2640      gnus-group-setup-buffer gnus-group-get-new-news
2641      gnus-group-make-help-group gnus-group-update-group
2642      gnus-group-iterate gnus-group-group-name)
2643     ("gnus-bcklg" gnus-backlog-request-article gnus-backlog-enter-article
2644      gnus-backlog-remove-article)
2645     ("gnus-art" gnus-article-read-summary-keys gnus-article-save
2646      gnus-article-prepare gnus-article-set-window-start
2647      gnus-article-next-page gnus-article-prev-page
2648      gnus-request-article-this-buffer gnus-article-mode
2649      gnus-article-setup-buffer gnus-narrow-to-page
2650      gnus-article-delete-invisible-text gnus-treat-article)
2651     ("gnus-art" :interactive (gnus-summary-mode gnus-article-mode)
2652      gnus-article-hide-headers gnus-article-hide-boring-headers
2653      gnus-article-treat-overstrike
2654      gnus-article-remove-cr gnus-article-remove-trailing-blank-lines
2655      gnus-article-emojize-symbols
2656      gnus-article-display-x-face gnus-article-de-quoted-unreadable
2657      gnus-article-de-base64-unreadable
2658      gnus-article-decode-HZ
2659      gnus-article-wash-html
2660      gnus-article-unsplit-urls
2661      gnus-article-hide-pem gnus-article-hide-signature
2662      gnus-article-strip-leading-blank-lines gnus-article-date-local
2663      gnus-article-date-original gnus-article-date-lapsed
2664      gnus-article-edit-mode gnus-article-edit-article
2665      gnus-article-edit-done gnus-article-decode-encoded-words
2666      gnus-start-date-timer gnus-stop-date-timer
2667      gnus-mime-view-all-parts gnus-article-pipe-part
2668      gnus-article-inline-part gnus-article-encrypt-body
2669      gnus-article-browse-html-article gnus-article-view-part-externally
2670      gnus-article-view-part-as-charset gnus-article-copy-part
2671      gnus-article-jump-to-part gnus-article-view-part-as-type
2672      gnus-article-delete-part gnus-article-replace-part
2673      gnus-article-save-part-and-strip gnus-article-save-part
2674      gnus-article-remove-leading-whitespace gnus-article-strip-trailing-space
2675      gnus-article-strip-leading-space gnus-article-strip-all-blank-lines
2676      gnus-article-strip-blank-lines gnus-article-strip-multiple-blank-lines
2677      gnus-article-date-user gnus-article-date-iso8601
2678      gnus-article-date-english gnus-article-date-ut
2679      gnus-article-decode-charset gnus-article-decode-mime-words
2680      gnus-article-toggle-fonts gnus-article-show-images
2681      gnus-article-remove-images gnus-article-display-face
2682      gnus-article-treat-fold-newsgroups gnus-article-treat-unfold-headers
2683      gnus-article-treat-fold-headers gnus-article-highlight-signature
2684      gnus-article-highlight-headers gnus-article-highlight
2685      gnus-article-strip-banner gnus-article-hide-list-identifiers
2686      gnus-article-hide gnus-article-outlook-rearrange-citation
2687      gnus-article-treat-non-ascii gnus-article-treat-smartquotes
2688      gnus-article-verify-x-pgp-sig gnus-article-strip-headers-in-body
2689      gnus-treat-smiley gnus-article-treat-ansi-sequences
2690      gnus-article-capitalize-sentences gnus-article-toggle-truncate-lines
2691      gnus-article-fill-long-lines gnus-article-emphasize
2692      gnus-article-add-buttons-to-head gnus-article-add-button
2693      gnus-article-babel gnus-sticky-article gnus-article-view-part
2694      gnus-article-add-buttons)
2695     ("gnus-int" gnus-request-type)
2696     ("gnus-start" gnus-newsrc-parse-options gnus-1 gnus-no-server-1
2697      gnus-dribble-enter gnus-read-init-file gnus-dribble-touch
2698      gnus-check-reasonable-setup)
2699     ("gnus-dup" gnus-dup-suppress-articles gnus-dup-unsuppress-article
2700      gnus-dup-enter-articles)
2701     ("gnus-eform" gnus-edit-form)
2702     ("gnus-logic" gnus-score-advanced)
2703     ("gnus-undo" gnus-undo-mode gnus-undo-register)
2704     ("gnus-async" gnus-async-request-fetched-article gnus-async-prefetch-next
2705      gnus-async-prefetch-article gnus-async-prefetch-remove-group
2706      gnus-async-halt-prefetch)
2707     ("gnus-agent" gnus-open-agent gnus-agent-get-function
2708      gnus-agent-save-active gnus-agent-method-p
2709      gnus-agent-get-undownloaded-list gnus-agent-fetch-session
2710      gnus-summary-set-agent-mark gnus-agent-save-group-info
2711      gnus-agent-request-article gnus-agent-retrieve-headers
2712      gnus-agent-store-article gnus-agent-group-covered-p)
2713     ("gnus-agent" :interactive t
2714      gnus-unplugged gnus-agentize gnus-agent-batch)
2715     ("gnus-vm" :interactive (gnus-summary-mode) gnus-summary-save-in-vm
2716      gnus-summary-save-article-vm)
2717     ("compface" uncompface)
2718     ("gnus-draft" :interactive (gnus-summary-mode) gnus-draft-mode)
2719     ("gnus-draft" :interactive t gnus-group-send-queue)
2720     ("gnus-mlspl" gnus-group-split gnus-group-split-fancy)
2721     ("gnus-mlspl" :interactive (gnus-group-mode) gnus-group-split-setup
2722      gnus-group-split-update)
2723     ("gnus-delay" gnus-delay-initialize))))
2724
2725;;; gnus-sum.el thingies
2726
2727
2728(defcustom gnus-summary-line-format "%U%R%z%I%(%[%4L: %-23,23f%]%) %s\n"
2729  "The format specification of the lines in the summary buffer.
2730
2731It works along the same lines as a normal formatting string,
2732with some simple extensions.
2733
2734%N          Article number, left padded with spaces (string)
2735%S          Subject (string)
2736%s          Subject if it is at the root of a thread, and \"\"
2737            otherwise (string)
2738%n          Name of the poster (string)
2739%a          Extracted name of the poster (string)
2740%A          Extracted address of the poster (string)
2741%F          Contents of the From: header (string)
2742%f          Contents of the From: or To: headers (string)
2743%x          Contents of the Xref: header (string)
2744%D          Contents of the Date: header article (string)
2745%d          Date of the article (string) in DD-MMM format
2746%o          Date of the article (string) in YYYYMMDD`T'HHMMSS
2747            format
2748%M          Message-id of the article (string)
2749%r          References of the article (string)
2750%c          Number of characters in the article (integer)
2751%k          Pretty-printed version of the above (string)
2752            For example, \"1.2k\" or \"0.4M\".
2753%L          Number of lines in the article (integer)
2754%Z          RSV of the article; nil if not in an nnselect group (integer)
2755%G          Originating group name for the article; nil if not
2756            in an nnselect group (string)
2757%g          Short from  of the originating group name for the article;
2758            nil if not in an nnselect group (string)
2759%I          Indentation based on thread level (a string of
2760            spaces)
2761%B          A complex trn-style thread tree (string)
2762            The variables `gnus-sum-thread-*' can be used for
2763            customization.
2764%T          A string with two possible values: 80 spaces if the
2765            article is on thread level two or larger and 0 spaces
2766            on level one
2767%R          \"A\" if this article has been replied to, \" \"
2768            otherwise (character)
2769%U          \"Read\" status of this article.
2770            See Info node `(gnus)Marking Articles'
2771%[          Opening bracket (character, \"[\" or \"<\")
2772%]          Closing bracket (character, \"]\" or \">\")
2773%>          Spaces of length thread-level (string)
2774%<          Spaces of length (- 20 thread-level) (string)
2775%i          Article score (number)
2776%z          Article zcore (character)
2777%t          Number of articles under the current thread (number).
2778%e          Whether the thread is empty or not (character).
2779%V          Total thread score (number).
2780%P          The line number (number).
2781%O          Download mark (character).
2782%*          If present, indicates desired cursor position
2783            (instead of after first colon).
2784%u          User defined specifier.  The next character in the
2785            format string should be a letter.  Gnus will call the
2786            function gnus-user-format-function-X, where X is the
2787            letter following %u.  The function will be passed the
2788            current header as argument.  The function should
2789            return a string, which will be inserted into the
2790            summary just like information from any other summary
2791            specifier.
2792&user-date; Age sensitive date format.  Various date format is
2793            defined in `gnus-user-date-format-alist'.
2794
2795
2796The %U (status), %R (replied) and %z (zcore) specs have to be handled
2797with care.  For reasons of efficiency, Gnus will compute what column
2798these characters will end up in, and \"hard-code\" that.  This means that
2799it is invalid to have these specs after a variable-length spec.  Well,
2800you might not be arrested, but your summary buffer will look strange,
2801which is bad enough.
2802
2803The smart choice is to have these specs as far to the left as
2804possible.
2805
2806This restriction may disappear in later versions of Gnus.
2807
2808General format specifiers can also be used.
2809See Info node `(gnus)Formatting Variables'."
2810  :link '(custom-manual "(gnus)Formatting Variables")
2811  :type 'string
2812  :group 'gnus-summary-format)
2813
2814;;;
2815;;; Skeleton keymaps
2816;;;
2817
2818(defun gnus-suppress-keymap (keymap)
2819  (suppress-keymap keymap)
2820  (let ((keys '([delete] "\177" "\M-u"))) ;[mouse-2]
2821    (while keys
2822      (define-key keymap (pop keys) 'undefined))))
2823
2824(defvar gnus-article-mode-map
2825  (let ((keymap (make-sparse-keymap)))
2826    (gnus-suppress-keymap keymap)
2827    keymap))
2828(defvar gnus-summary-mode-map
2829  (let ((keymap (make-keymap)))
2830    (gnus-suppress-keymap keymap)
2831    keymap))
2832(defvar gnus-group-mode-map
2833  (let ((keymap (make-keymap)))
2834    (gnus-suppress-keymap keymap)
2835    keymap))
2836
2837
2838
2839;; Fix by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
2840;; If you want the cursor to go somewhere else, set these two
2841;; functions in some startup hook to whatever you want.
2842(defalias 'gnus-summary-position-point 'gnus-goto-colon)
2843(defalias 'gnus-group-position-point 'gnus-goto-colon)
2844
2845;;; Various macros and substs.
2846
2847(defun gnus-header-from (header)
2848  (mail-header-from header))
2849
2850(defmacro gnus-group-unread (group)
2851  "Get the currently computed number of unread articles in GROUP."
2852  `(car (gethash ,group gnus-newsrc-hashtb)))
2853
2854(defmacro gnus-group-entry (group)
2855  "Get the newsrc entry for GROUP."
2856  `(gethash ,group gnus-newsrc-hashtb))
2857
2858(defmacro gnus-active (group)
2859  "Get active info on GROUP."
2860  `(gethash ,group gnus-active-hashtb))
2861
2862(defmacro gnus-set-active (group active)
2863  "Set GROUP's active info."
2864  `(puthash ,group ,active gnus-active-hashtb))
2865
2866;; Info access macros.
2867
2868(cl-defstruct (gnus-info
2869               (:constructor gnus-info-make
2870                (group rank read &optional marks method params))
2871               (:constructor nil)
2872	       ;; FIXME: gnus-newsrc-alist contains a list of those,
2873               ;; so changing them to a real struct will take more work!
2874               (:type list))
2875  group rank read marks method params)
2876
2877(defsubst gnus-info-level (info)
2878  (declare (gv-setter gnus-info--set-level))
2879  (let ((rank (gnus-info-rank info)))
2880    (if (consp rank)
2881	(car rank)
2882      rank)))
2883(defsubst gnus-info-score (info)
2884  (declare (gv-setter gnus-info--set-score))
2885  (let ((rank (gnus-info-rank info)))
2886    (or (and (consp rank) (cdr rank)) 0)))
2887
2888(defsubst gnus-info-set-marks (info marks &optional extend)
2889  (if extend (gnus-info--grow-entry info 3))
2890  (setf (gnus-info-marks info) marks))
2891(defsubst gnus-info-set-method (info method &optional extend)
2892  (if extend (gnus-info--grow-entry info 4))
2893  (setf (gnus-info-method info) method))
2894(defsubst gnus-info-set-params (info params &optional extend)
2895  (if extend (gnus-info--grow-entry info 5))
2896  (setf (gnus-info-params info) params))
2897
2898(defun gnus-info--grow-entry (info number)
2899  ;; Extend the info until we have enough elements.
2900  (while (<= (length info) number)
2901    (nconc info (list nil))))
2902
2903(defsubst gnus-info--set-level (info level)
2904  (let ((rank (gnus-info-rank info)))
2905    (if (consp rank)
2906        (setcar rank level)
2907      (setf (gnus-info-rank info) level))))
2908(defsubst gnus-info--set-score (info score)
2909  (let ((rank (gnus-info-rank info)))
2910     (if (consp rank)
2911	 (setcdr rank score)
2912       (setf (gnus-info-rank info) (cons rank score)))))
2913
2914(defsubst gnus-get-info (group)
2915  (nth 1 (gethash group gnus-newsrc-hashtb)))
2916
2917(defun gnus-set-info (group info)
2918  (setcdr (gethash group gnus-newsrc-hashtb)
2919	  (list info)))
2920
2921
2922;;;
2923;;; Shutdown
2924;;;
2925
2926(defvar gnus-shutdown-alist nil)
2927
2928(defun gnus-add-shutdown (function &rest symbols)
2929  "Run FUNCTION whenever one of SYMBOLS is shut down."
2930  (push (cons function symbols) gnus-shutdown-alist))
2931
2932(defun gnus-shutdown (symbol)
2933  "Shut down everything that waits for SYMBOL."
2934  (dolist (entry gnus-shutdown-alist)
2935    (when (memq symbol (cdr entry))
2936      (funcall (car entry)))))
2937
2938
2939;;;
2940;;; Gnus Utility Functions
2941;;;
2942
2943(defun gnus-find-subscribed-addresses ()
2944  "Return a regexp matching the addresses of all subscribed mail groups.
2945It consists of the `to-address' or `to-list' parameter of all groups
2946with a `subscribed' parameter."
2947  (let (group address addresses)
2948    (dolist (entry (cdr gnus-newsrc-alist))
2949      (setq group (car entry))
2950      (when (gnus-parameter-subscribed group)
2951	(setq address (mail-strip-quoted-names
2952		       (or (gnus-group-fast-parameter group 'to-address)
2953			   (gnus-group-fast-parameter group 'to-list))))
2954	(when address
2955	  (cl-pushnew address addresses :test #'equal))))
2956    (when addresses
2957      (list (mapconcat #'regexp-quote addresses "\\|")))))
2958
2959(defmacro gnus-string-or (&rest strings)
2960  "Return the first element of STRINGS that is a non-blank string.
2961STRINGS will be evaluated in normal `or' order."
2962  `(gnus-string-or-1 (list ,@strings)))
2963
2964(defun gnus-string-or-1 (strings)
2965  (let (string)
2966    (while strings
2967      (setq string (pop strings))
2968      (if (string-match "^[ \t]*$" string)
2969	  (setq string nil)
2970	(setq strings nil)))
2971    string))
2972
2973(defun gnus-version (&optional arg)
2974  "Version number of this version of Gnus.
2975If ARG, insert string at point."
2976  (interactive "P")
2977  (if arg
2978      (insert (message gnus-version))
2979    (message gnus-version)))
2980
2981(defun gnus-continuum-version (&optional version)
2982  "Return VERSION as a floating point number."
2983  (unless version
2984    (setq version gnus-version))
2985  (when (or (string-match "^\\([^ ]+\\)? ?Gnus v?\\([0-9.]+\\)$" version)
2986	    (string-match "^\\(.?\\)gnus-\\([0-9.]+\\)$" version))
2987    (let ((alpha (and (match-beginning 1) (match-string 1 version)))
2988	  (number (match-string 2 version))
2989	  major minor least)
2990      (unless (string-match
2991	       "\\([0-9]\\)\\.\\([0-9]+\\)\\.?\\([0-9]+\\)?" number)
2992	(error "Invalid version string: %s" version))
2993      (setq major (string-to-number (match-string 1 number))
2994	    minor (string-to-number (match-string 2 number))
2995	    least (if (match-beginning 3)
2996		      (string-to-number (match-string 3 number))
2997		    0))
2998      (string-to-number
2999       (if (zerop major)
3000	     (format "%1.2f00%02d%02d"
3001		     (if (member alpha '("(ding)" "d"))
3002			 4.99
3003		       (+ 5 (* 0.02
3004			       (abs
3005				(- (aref (downcase alpha) 0) ?t)))
3006			  -0.01))
3007		     minor least)
3008	 (format "%d.%02d%02d" major minor least))))))
3009
3010(defvar gnus-info-buffer)
3011
3012(defun gnus-info-find-node (&optional nodename)
3013  "Find Info documentation of Gnus."
3014  (interactive)
3015  ;; Enlarge info window if needed.
3016  (let (gnus-info-buffer)
3017    (Info-goto-node (or nodename (cadr (assq major-mode gnus-info-nodes))))
3018    (setq gnus-info-buffer (current-buffer))
3019    (gnus-configure-windows 'info)))
3020
3021;;;
3022;;; gnus-interactive
3023;;;
3024
3025(defvar gnus-current-prefix-symbol nil
3026  "Current prefix symbol.")
3027
3028(defvar gnus-current-prefix-symbols nil
3029  "List of current prefix symbols.")
3030
3031(defun gnus-interactive (string)
3032  "Return a list that can be fed to `interactive'.
3033See `interactive' for full documentation.
3034
3035Adds the following specs:
3036
3037y -- The current symbolic prefix.
3038Y -- A list of the current symbolic prefix(es).
3039A -- Article number.
3040H -- Article header.
3041g -- Group name."
3042  (let ((i 0)
3043	out c prompt)
3044    (while (< i (length string))
3045      (string-match ".\\([^\n]*\\)\n?" string i)
3046      (setq c (aref string i))
3047      (when (match-end 1)
3048	(setq prompt (match-string 1 string)))
3049      (setq i (match-end 0))
3050      ;; We basically emulate just about everything that
3051      ;; `interactive' does, but add the specs listed above.
3052      (push
3053       (cond
3054	((= c ?a)
3055	 (completing-read prompt obarray 'fboundp t))
3056	((= c ?b)
3057	 (read-buffer prompt (current-buffer) t))
3058	((= c ?B)
3059	 (read-buffer prompt (other-buffer (current-buffer))))
3060	((= c ?c)
3061	 (read-char))
3062	((= c ?C)
3063	 (completing-read prompt obarray 'commandp t))
3064	((= c ?d)
3065	 (point))
3066	((= c ?D)
3067	 (read-directory-name prompt nil default-directory 'lambda))
3068	((= c ?f)
3069	 (read-file-name prompt nil nil 'lambda))
3070	((= c ?F)
3071	 (read-file-name prompt))
3072	((= c ?k)
3073	 (read-key-sequence prompt))
3074	((= c ?K)
3075	 (error "Not implemented spec"))
3076	((= c ?e)
3077	 (error "Not implemented spec"))
3078	((= c ?m)
3079	 (mark))
3080	((= c ?N)
3081	 (error "Not implemented spec"))
3082	((= c ?n)
3083	 (string-to-number (read-from-minibuffer prompt)))
3084	((= c ?p)
3085	 (prefix-numeric-value current-prefix-arg))
3086	((= c ?P)
3087	 current-prefix-arg)
3088	((= c ?r)
3089	 'gnus-prefix-nil)
3090	((= c ?s)
3091	 (read-string prompt))
3092	((= c ?S)
3093	 (intern (read-string prompt)))
3094	((= c ?v)
3095	 (read-variable prompt))
3096	((= c ?x)
3097	 (read-minibuffer prompt))
3098	((= c ?x)
3099	 (eval-minibuffer prompt))
3100	;; And here the new specs come.
3101	((= c ?y)
3102	 gnus-current-prefix-symbol)
3103	((= c ?Y)
3104	 gnus-current-prefix-symbols)
3105	((= c ?g)
3106	 (gnus-group-group-name))
3107	((= c ?A)
3108	 (gnus-summary-skip-intangible)
3109	 (or (get-text-property (point) 'gnus-number)
3110	     (gnus-summary-last-subject)))
3111	((= c ?H)
3112	 (gnus-data-header (gnus-data-find (gnus-summary-article-number))))
3113	(t
3114	 (error "Non-implemented spec")))
3115       out)
3116      (cond
3117       ((= c ?r)
3118	(push (if (< (point) (mark)) (point) (mark)) out)
3119	(push (if (> (point) (mark)) (point) (mark)) out))))
3120    (setq out (delq 'gnus-prefix-nil out))
3121    (nreverse out)))
3122
3123(defun gnus-symbolic-argument ()
3124  "Read a symbolic argument and a command, and then execute command."
3125  (interactive)
3126  (let* ((in-command (this-command-keys))
3127	 (command in-command)
3128	 gnus-current-prefix-symbols
3129	 gnus-current-prefix-symbol
3130	 syms)
3131    (while (equal in-command command)
3132      (message "%s-" (key-description (this-command-keys)))
3133      (push (intern (char-to-string (read-char))) syms)
3134      (setq command (read-key-sequence nil t)))
3135    (setq gnus-current-prefix-symbols (nreverse syms)
3136	  gnus-current-prefix-symbol (car gnus-current-prefix-symbols))
3137    (call-interactively (key-binding command t))))
3138
3139;;; More various functions.
3140
3141(defsubst gnus-check-backend-function (func group)
3142  "Check whether GROUP supports function FUNC.
3143GROUP can either be a string (a group name) or a select method."
3144  (ignore-errors
3145    (when-let ((method (if (stringp group)
3146		           (car (gnus-find-method-for-group group))
3147		         group)))
3148      (unless (featurep method)
3149	(require method))
3150      (fboundp (intern (format "%s-%s" method func))))))
3151
3152(defun gnus-group-read-only-p (&optional group)
3153  "Check whether GROUP supports editing or not.
3154If GROUP is nil, `gnus-newsgroup-name' will be checked instead.  Note
3155that that variable is buffer-local to the summary buffers."
3156  (let ((group (or group gnus-newsgroup-name)))
3157    (not (gnus-check-backend-function 'request-replace-article group))))
3158
3159(defun gnus-virtual-group-p (group)
3160  "Say whether GROUP is virtual or not."
3161  (memq 'virtual (assoc (symbol-name (car (gnus-find-method-for-group group)))
3162			gnus-valid-select-methods)))
3163
3164(defun gnus-news-group-p (group &optional article)
3165  "Return non-nil if GROUP (and ARTICLE) come from a news server."
3166  (cond ((gnus-member-of-valid 'post group) ;Ordinary news group
3167	 t)				    ;is news of course.
3168	((not (gnus-member-of-valid 'post-mail group)) ;Non-combined.
3169	 nil)				;must be mail then.
3170	((mail-header-p article)		;Has header info.
3171	 (eq (gnus-request-type group (mail-header-id article)) 'news))
3172	((null article)			       ;Hasn't header info
3173	 (eq (gnus-request-type group) 'news)) ;(unknown ==> mail)
3174	((< article 0)			       ;Virtual message
3175	 nil)				;we don't know, guess mail.
3176	(t				;Has positive number
3177	 (eq (gnus-request-type group article) 'news)))) ;use it.
3178
3179;; Check whether to use long file names.
3180(defun gnus-use-long-file-name (symbol)
3181  ;; The variable has to be set...
3182  (and gnus-use-long-file-name
3183       ;; If it isn't a list, then we return t.
3184       (or (not (listp gnus-use-long-file-name))
3185	   ;; If it is a list, and the list contains `symbol', we
3186	   ;; return nil.
3187	   (not (memq symbol gnus-use-long-file-name)))))
3188
3189;; Generate a unique new group name.
3190(defun gnus-generate-new-group-name (leaf)
3191  (let ((name leaf)
3192	(num 0))
3193    (while (gnus-group-entry name)
3194      (setq name (concat leaf "<" (int-to-string (setq num (1+ num))) ">")))
3195    name))
3196
3197(defun gnus-ephemeral-group-p (group)
3198  "Say whether GROUP is ephemeral or not."
3199  (gnus-group-get-parameter group 'quit-config t))
3200
3201(defun gnus-group-quit-config (group)
3202  "Return the quit-config of GROUP."
3203  (gnus-group-get-parameter group 'quit-config t))
3204
3205(defun gnus-kill-ephemeral-group (group)
3206  "Remove ephemeral GROUP from relevant structures."
3207  (remhash group gnus-newsrc-hashtb)
3208  (setq gnus-newsrc-alist
3209	(delq (assoc group gnus-newsrc-alist)
3210              gnus-newsrc-alist)))
3211
3212(defun gnus-simplify-mode-line ()
3213  "Make mode lines a bit simpler."
3214  (setq mode-line-modified "--")
3215  (when (listp mode-line-format)
3216    (setq-local mode-line-format (copy-sequence mode-line-format))
3217    (when (equal (nth 3 mode-line-format) "   ")
3218      (setcar (nthcdr 3 mode-line-format) " "))))
3219
3220;;; Servers and groups.
3221
3222(defsubst gnus-server-add-address (method)
3223  (let ((method-name (symbol-name (car method))))
3224    (if (and (memq 'address (assoc method-name gnus-valid-select-methods))
3225	     (not (assq (intern (concat method-name "-address")) method))
3226	     (memq 'physical-address (assq (car method)
3227					   gnus-valid-select-methods)))
3228	(append method (list (list (intern (concat method-name "-address"))
3229				   (nth 1 method))))
3230      method)))
3231
3232(defsubst gnus-method-to-server (method &optional nocache no-enter-cache)
3233  (catch 'server-name
3234    (setq method (or method gnus-select-method))
3235
3236    ;; Perhaps it is already in the cache.
3237    (unless nocache
3238      (mapc (lambda (name-method)
3239	      (if (equal (cdr name-method) method)
3240		  (throw 'server-name (car name-method))))
3241	    gnus-server-method-cache))
3242
3243    (dolist (server-alist
3244             (list gnus-server-alist
3245	           gnus-predefined-server-alist))
3246      (mapc (lambda (name-method)
3247	      (when (gnus-methods-equal-p (cdr name-method) method)
3248		(unless (member name-method gnus-server-method-cache)
3249		  (push name-method gnus-server-method-cache))
3250		(throw 'server-name (car name-method))))
3251	    server-alist))
3252
3253    (let* ((name (if (member (cadr method) '(nil ""))
3254		     (format "%s" (car method))
3255		   (format "%s:%s" (car method) (cadr method))))
3256	   (name-method (cons name method)))
3257      (unless (or no-enter-cache
3258		  (member name-method gnus-server-method-cache)
3259		  (assoc (car name-method) gnus-server-method-cache))
3260	(push name-method gnus-server-method-cache))
3261      name)))
3262
3263(defsubst gnus-server-to-method (server)
3264  "Map virtual server names to select methods."
3265  (or (and server (listp server) server)
3266      (cdr (assoc server gnus-server-method-cache))
3267      (let ((result
3268	     (or
3269	      ;; Perhaps this is the native server?
3270	      (and (equal server "native") gnus-select-method)
3271	      ;; It should be in the server alist.
3272	      (cdr (assoc server gnus-server-alist))
3273	      ;; It could be in the predefined server alist.
3274	      (cdr (assoc server gnus-predefined-server-alist))
3275	      ;; If not, we look through all the opened server
3276	      ;; to see whether we can find it there.
3277	      (let ((opened gnus-opened-servers))
3278		(while (and opened
3279			    (not (equal server (format "%s:%s" (caaar opened)
3280						       (cadaar opened)))))
3281		  (pop opened))
3282		(caar opened))
3283	      ;; It could be a named method, search all servers
3284	      (let ((servers gnus-secondary-select-methods))
3285		(while (and servers
3286			    (not (equal server (format "%s:%s" (caar servers)
3287						       (cadar servers)))))
3288		  (pop servers))
3289		(car servers))
3290	      ;; This could be some sort of foreign server that I
3291	      ;; simply haven't opened (yet).  Do a brute-force scan
3292	      ;; of the entire gnus-newsrc-alist for the server name
3293	      ;; of every method.  As a side-effect, loads the
3294	      ;; gnus-server-method-cache so this only happens once,
3295	      ;; if at all.
3296	      (let ((alist (cdr gnus-newsrc-alist))
3297		    method match)
3298		(while alist
3299		  (setq method (gnus-info-method (pop alist)))
3300		  (when (and (not (stringp method))
3301			     (equal server
3302				    (gnus-method-to-server method nil t)))
3303		    (setq match method
3304			  alist nil)))
3305		match))))
3306	(when (and result
3307		   (not (assoc server gnus-server-method-cache)))
3308	  (push (cons server result) gnus-server-method-cache))
3309	result)))
3310
3311(defsubst gnus-server-get-method (group method)
3312  ;; Input either a server name, and extended server name, or a
3313  ;; select method, and return a select method.
3314  (cond ((stringp method)
3315	 (gnus-server-to-method method))
3316	((equal method gnus-select-method)
3317	 gnus-select-method)
3318	((and group (stringp (car method)))
3319	 (gnus-server-extend-method group method))
3320	((and method
3321	      (not group)
3322	      (equal (cadr method) ""))
3323	 method)
3324	(t
3325	 (gnus-server-add-address method))))
3326
3327(defmacro gnus-method-equal (ss1 ss2)
3328  "Say whether two servers are equal."
3329  `(let ((s1 ,ss1)
3330	 (s2 ,ss2))
3331     (or (equal s1 s2)
3332	 (and (= (length s1) (length s2))
3333	      (progn
3334		(while (and s1 (member (car s1) s2))
3335		  (setq s1 (cdr s1)))
3336		(null s1))))))
3337
3338(defun gnus-methods-equal-p (m1 m2)
3339  (let ((m1 (or m1 gnus-select-method))
3340	(m2 (or m2 gnus-select-method)))
3341    (or (equal m1 m2)
3342	(and (eq (car m1) (car m2))
3343	     (or (not (memq 'address (assoc (symbol-name (car m1))
3344					    gnus-valid-select-methods)))
3345		 (equal (nth 1 m1) (nth 1 m2)))))))
3346
3347(defsubst gnus-sloppily-equal-method-parameters (m1 m2)
3348  ;; Check parameters for sloppy equality.
3349  (let ((p1 (copy-sequence (cddr m1)))
3350	(p2 (copy-sequence (cddr m2)))
3351	e1 e2)
3352    (cl-block nil
3353      (while (setq e1 (pop p1))
3354	(unless (setq e2 (assq (car e1) p2))
3355	  ;; The parameter doesn't exist in p2.
3356	  (cl-return nil))
3357	(setq p2 (delq e2 p2))
3358	(unless (equal e1 e2)
3359	  (if (not (and (stringp (cadr e1))
3360			(stringp (cadr e2))))
3361	      (cl-return nil)
3362	    ;; Special-case string parameter comparison so that we
3363	    ;; can uniquify them.
3364	    (let ((s1 (cadr e1))
3365		  (s2 (cadr e2)))
3366	      (when (string-match "/\\'" s1)
3367		(setq s1 (directory-file-name s1)))
3368	      (when (string-match "/\\'" s2)
3369		(setq s2 (directory-file-name s2)))
3370	      (unless (equal s1 s2)
3371		(cl-return nil))))))
3372      ;; If p2 now is empty, they were equal.
3373      (null p2))))
3374
3375(defun gnus-method-ephemeral-p (method)
3376  (let ((equal nil))
3377    (dolist (ephemeral gnus-ephemeral-servers)
3378      (when (gnus-sloppily-equal-method-parameters method ephemeral)
3379	(setq equal t)))
3380    equal))
3381
3382(defun gnus-methods-sloppily-equal (m1 m2)
3383  ;; Same method.
3384  (or
3385   (eq m1 m2)
3386   ;; Type and name are equal.
3387   (and
3388    (eq (car m1) (car m2))
3389    (equal (cadr m1) (cadr m2))
3390    (gnus-sloppily-equal-method-parameters m1 m2))))
3391
3392(defun gnus-server-equal (m1 m2)
3393  "Say whether two methods are equal."
3394  (let ((m1 (cond ((null m1) gnus-select-method)
3395		  ((stringp m1) (gnus-server-to-method m1))
3396		  (t m1)))
3397	(m2 (cond ((null m2) gnus-select-method)
3398		  ((stringp m2) (gnus-server-to-method m2))
3399		  (t m2))))
3400    (gnus-method-equal m1 m2)))
3401
3402(defun gnus-servers-using-backend (backend)
3403  "Return a list of known servers using BACKEND."
3404  (let ((opened gnus-opened-servers)
3405	out)
3406    (while opened
3407      (when (eq backend (caaar opened))
3408	(push (caar opened) out))
3409      (pop opened))
3410    out))
3411
3412(defun gnus-archive-server-wanted-p ()
3413  "Say whether the user wants to use the archive server."
3414  (cond
3415   ((or (not gnus-message-archive-method)
3416	(not gnus-message-archive-group))
3417    nil)
3418   ((and gnus-message-archive-method gnus-message-archive-group)
3419    t)
3420   (t
3421    (let ((active (cadr (assq 'nnfolder-active-file
3422			      gnus-message-archive-method))))
3423      (and active
3424	   (file-exists-p active))))))
3425
3426(defsubst gnus-method-to-server-name (method)
3427  (concat
3428   (format "%s" (car method))
3429   (when (and
3430	  (or (assoc (format "%s" (car method))
3431		     (gnus-methods-using 'address))
3432	      (gnus-server-equal method gnus-message-archive-method))
3433	  (nth 1 method)
3434	  (not (string= (nth 1 method) "")))
3435     (concat "+" (nth 1 method)))))
3436
3437(defsubst gnus-method-to-full-server-name (method)
3438  (format "%s+%s" (car method) (nth 1 method)))
3439
3440(defun gnus-group-prefixed-name (group method &optional full)
3441  "Return the whole name from GROUP and METHOD.
3442Call with full set to get the fully qualified group name (even if the
3443server is native)."
3444  (when (stringp method)
3445    (setq method (gnus-server-to-method method)))
3446  (if (or (not method)
3447	  (and (not full) (gnus-server-equal method "native"))
3448	  ;;;!!! This might not be right.  We'll see...
3449	  ;(string-match ":" group)
3450	  )
3451      group
3452    (concat (gnus-method-to-server-name method) ":" group)))
3453
3454(defun gnus-group-full-name (group method)
3455  "Return the full name from GROUP and METHOD, even if the method is native."
3456  (gnus-group-prefixed-name group method t))
3457
3458(defun gnus-group-guess-full-name-from-command-method (group)
3459  "Guess the full name from GROUP, even if the method is native."
3460  (if (gnus-group-prefixed-p group)
3461      group
3462    (gnus-group-full-name group gnus-command-method)))
3463
3464(defun gnus-group-real-prefix (group)
3465  "Return the prefix of the current group name."
3466  (if (stringp group)
3467      (if (string-match "^[^:]+:" group)
3468	  (substring group 0 (match-end 0))
3469	"")
3470    nil))
3471
3472(defun gnus-group-short-name (group)
3473  "Return the short group name."
3474  (let ((prefix (gnus-group-real-prefix group)))
3475    (if (< 0 (length prefix))
3476	(substring group (length prefix) nil)
3477      group)))
3478
3479(defun gnus-group-prefixed-p (group)
3480  "Return the prefix of the current group name."
3481  (< 0 (length (gnus-group-real-prefix group))))
3482
3483(defun gnus-summary-buffer-name (group)
3484  "Return the summary buffer name of GROUP."
3485  (concat "*Summary " group "*"))
3486
3487(defun gnus-group-method (group)
3488  "Return the server or method used for selecting GROUP.
3489You should probably use `gnus-find-method-for-group' instead."
3490  (let ((prefix (gnus-group-real-prefix group)))
3491    (if (equal prefix "")
3492	gnus-select-method
3493      (let ((servers gnus-opened-servers)
3494	    (server "")
3495	    backend possible found)
3496	(if (string-match "^[^\\+]+\\+" prefix)
3497	    (setq backend (intern (substring prefix 0 (1- (match-end 0))))
3498		  server (substring prefix (match-end 0) (1- (length prefix))))
3499	  (setq backend (intern (substring prefix 0 (1- (length prefix))))))
3500	(while servers
3501	  (when (eq (caaar servers) backend)
3502	    (setq possible (caar servers))
3503	    (when (equal (cadaar servers) server)
3504	      (setq found (caar servers))))
3505	  (pop servers))
3506	(or (car (rassoc found gnus-server-alist))
3507	    found
3508	    (car (rassoc possible gnus-server-alist))
3509	    possible
3510	    (list backend server))))))
3511
3512(defsubst gnus-native-method-p (method)
3513  "Return whether METHOD is the native select method."
3514  (gnus-method-equal method gnus-select-method))
3515
3516(defsubst gnus-secondary-method-p (method)
3517  "Return whether METHOD is a secondary select method."
3518  (let ((methods gnus-secondary-select-methods)
3519	(gmethod (inline (gnus-server-get-method nil method))))
3520    (while (and methods
3521		(not (gnus-method-equal
3522		      (inline (gnus-server-get-method nil (car methods)))
3523		      gmethod)))
3524      (setq methods (cdr methods)))
3525    methods))
3526
3527(defun gnus-method-simplify (method)
3528  "Return the shortest uniquely identifying string or method for METHOD."
3529  (cond ((stringp method)
3530	 method)
3531	((gnus-native-method-p method)
3532	 nil)
3533	((gnus-secondary-method-p method)
3534	 (format "%s:%s" (nth 0 method) (nth 1 method)))
3535	(t
3536	 method)))
3537
3538(defun gnus-groups-from-server (server)
3539  "Return a list of all groups that are fetched from SERVER."
3540  (let ((alist (cdr gnus-newsrc-alist))
3541	info groups)
3542    (while (setq info (pop alist))
3543      (when (gnus-server-equal (gnus-info-method info) server)
3544	(push (gnus-info-group info) groups)))
3545    (sort groups #'string<)))
3546
3547(defun gnus-group-foreign-p (group)
3548  "Say whether a group is foreign or not."
3549  (and (not (gnus-group-native-p group))
3550       (not (gnus-group-secondary-p group))))
3551
3552(defun gnus-group-native-p (group)
3553  "Say whether the group is native or not."
3554  (not (string-search ":" group)))
3555
3556(defun gnus-group-secondary-p (group)
3557  "Say whether the group is secondary or not."
3558  (gnus-secondary-method-p (gnus-find-method-for-group group)))
3559
3560(defun gnus-parameters-get-parameter (group)
3561  "Return the group parameters for GROUP from `gnus-parameters'."
3562  (let ((case-fold-search (if (eq gnus-parameters-case-fold-search 'default)
3563			      case-fold-search
3564			    gnus-parameters-case-fold-search))
3565	params-list)
3566    (dolist (elem gnus-parameters)
3567      (when (string-match (car elem) group)
3568	(setq params-list
3569	      (nconc (gnus-expand-group-parameters
3570		      (car elem) (cdr elem) group)
3571		     params-list))))
3572    params-list))
3573
3574(defun gnus-expand-group-parameter (match value group)
3575  "Use MATCH to expand VALUE in GROUP."
3576  (let ((start (string-match match group)))
3577    (if start
3578        (let ((matched-string (substring group start (match-end 0))))
3579          ;; Build match groups
3580          (string-match match matched-string)
3581          (replace-match value nil nil matched-string))
3582      group)))
3583
3584(defun gnus-expand-group-parameters (match parameters group)
3585  "Go through PARAMETERS and expand them according to the match data."
3586  (let (new)
3587    (dolist (elem parameters)
3588      (cond
3589       ((and (stringp (cdr elem))
3590             (string-match "\\\\[0-9&]" (cdr elem)))
3591        (push (cons (car elem)
3592                    (gnus-expand-group-parameter match (cdr elem) group))
3593              new))
3594       ;; For `sieve' group parameters, perform substitutions for every
3595       ;; string within the match rule.  This allows for parameters such
3596       ;; as:
3597       ;;  ("list\\.\\(.*\\)"
3598       ;;   (sieve header :is "list-id" "<\\1.domain.org>"))
3599       ((eq 'sieve (car elem))
3600        (push (mapcar (lambda (sieve-elem)
3601                        (if (and (stringp sieve-elem)
3602                                 (string-match "\\\\[0-9&]" sieve-elem))
3603                            (gnus-expand-group-parameter match sieve-elem
3604                                                         group)
3605                          sieve-elem))
3606                      (cdr elem))
3607              new))
3608       (t
3609	(push elem new))))
3610    new))
3611
3612(defun gnus-group-fast-parameter (group symbol &optional allow-list)
3613  "For GROUP, return the value of SYMBOL.
3614
3615You should call this in the `gnus-group-buffer' buffer.
3616The function `gnus-group-find-parameter' will do that for you."
3617  ;; The speed trick:  No cons'ing and quit early.
3618  (let* ((params (funcall gnus-group-get-parameter-function group))
3619	 ;; Start easy, check the "real" group parameters.
3620	 (simple-results
3621	  (gnus-group-parameter-value params symbol allow-list t)))
3622    (if simple-results
3623	;; Found results; return them.
3624	(car simple-results)
3625      ;; We didn't find it there, try `gnus-parameters'.
3626      (let ((result nil)
3627	    (head nil)
3628	    (tail gnus-parameters))
3629	;; A good old-fashioned non-cl loop.
3630	(while tail
3631	  (setq head (car tail)
3632		tail (cdr tail))
3633	  ;; The car is regexp matching for matching the group name.
3634	  (when (string-match (car head) group)
3635	    ;; The cdr is the parameters.
3636	    (let ((this-result
3637		   (gnus-group-parameter-value (cdr head) symbol allow-list t)))
3638	      (when this-result
3639		(setq result (car this-result))
3640		;; Expand if necessary.
3641		(cond
3642                 ((and (stringp result) (string-match "\\\\[0-9&]" result))
3643                  (setq result (gnus-expand-group-parameter
3644                                (car head) result group)))
3645                 ;; For `sieve' group parameters, perform substitutions
3646                 ;; for every string within the match rule (see above).
3647                 ((eq symbol 'sieve)
3648                  (setq result
3649                        (mapcar (lambda (elem)
3650                                  (if (stringp elem)
3651                                      (gnus-expand-group-parameter (car head)
3652                                                                   elem group)
3653                                    elem))
3654                                result))))))))
3655	;; Done.
3656	result))))
3657
3658(defun gnus-group-find-parameter (group &optional symbol allow-list)
3659  "Return the group parameters for GROUP.
3660If SYMBOL, return the value of that symbol in the group parameters.
3661
3662If you call this function inside a loop, consider using the faster
3663`gnus-group-fast-parameter' instead."
3664  (with-current-buffer (or (gnus-buffer-live-p gnus-group-buffer)
3665                           (current-buffer))
3666    (if symbol
3667	(gnus-group-fast-parameter group symbol allow-list)
3668      (nconc
3669       (copy-sequence
3670	(funcall gnus-group-get-parameter-function group))
3671       (gnus-parameters-get-parameter group)))))
3672
3673(defun gnus-group-get-parameter (group &optional symbol allow-list)
3674  "Return the group parameters for GROUP.
3675If SYMBOL, return the value of that symbol in the group
3676parameters.  If ALLOW-LIST, also allow list as a result.  Most
3677functions should use `gnus-group-find-parameter', which also
3678examines the topic parameters.  GROUP can also be an info structure."
3679  (let ((params (gnus-info-params (if (listp group) group
3680				    (gnus-get-info group)))))
3681    (if symbol
3682	(gnus-group-parameter-value params symbol allow-list)
3683      params)))
3684
3685(defun gnus-group-parameter-value (params symbol &optional
3686					  allow-list present-p)
3687  "Return the value of SYMBOL in group PARAMS.
3688If ALLOW-LIST, also allow list as a result."
3689  ;; We only wish to return group parameters (dotted lists) and
3690  ;; not local variables, which may have the same names.
3691  ;; But first we handle single elements...
3692  (or (car (memq symbol params))
3693      ;; Handle alist.
3694      (let (elem)
3695	(catch 'found
3696	  (while (setq elem (pop params))
3697	    (when (and (consp elem)
3698		       (eq (car elem) symbol)
3699		       (or allow-list
3700			   (atom (cdr elem))))
3701	      (throw 'found (if present-p (list (cdr elem))
3702			      (cdr elem)))))))))
3703
3704(defun gnus-group-add-parameter (group param)
3705  "Add parameter PARAM to GROUP."
3706  (let ((info (gnus-get-info group)))
3707    (when info
3708      (gnus-group-remove-parameter group (if (consp param) (car param) param))
3709      ;; Cons the new param to the old one and update.
3710      (gnus-group-set-info (cons param (gnus-info-params info))
3711			   group 'params))))
3712
3713(defun gnus-group-set-parameter (group name value)
3714  "Set parameter NAME to VALUE in GROUP.
3715GROUP can also be an INFO structure."
3716  (let ((info (if (listp group)
3717		  group
3718		(gnus-get-info group))))
3719    (when info
3720      (gnus-group-remove-parameter group name)
3721      (let ((old-params (gnus-info-params info))
3722	    (new-params (list (cons name value))))
3723	(while old-params
3724	  (when (or (not (listp (car old-params)))
3725		    (not (eq (caar old-params) name)))
3726	    (setq new-params (append new-params (list (car old-params)))))
3727	  (setq old-params (cdr old-params)))
3728	(if (listp group)
3729	    (gnus-info-set-params info new-params t)
3730	  (gnus-group-set-info new-params (gnus-info-group info) 'params))))))
3731
3732(defun gnus-group-remove-parameter (group name)
3733  "Remove parameter NAME from GROUP.
3734GROUP can also be an INFO structure."
3735  (let ((info (if (listp group)
3736		  group
3737		(gnus-get-info group))))
3738    (when info
3739      (let ((params (gnus-info-params info)))
3740	(when params
3741	  (setq params (delq name params))
3742	  (while (assq name params)
3743	    (gnus-alist-pull name params))
3744	  (setf (gnus-info-params info) params))))))
3745
3746(defun gnus-group-add-score (group &optional score)
3747  "Add SCORE to the GROUP score.
3748If SCORE is nil, add 1 to the score of GROUP."
3749  (let ((info (gnus-get-info group)))
3750    (when info
3751      (setf (gnus-info-score info) (+ (gnus-info-score info) (or score 1))))))
3752
3753(defun gnus-short-group-name (group &optional levels)
3754  "Collapse GROUP name LEVELS.
3755Select methods are stripped and any remote host name is stripped down to
3756just the host name."
3757  (let* ((foreign "")
3758	 (depth 0)
3759	 (skip 1)
3760	 (levels (or levels
3761		     gnus-group-uncollapsed-levels
3762		     (progn
3763		       (while (string-match "\\." group skip)
3764			 (setq skip (match-end 0)
3765			       depth (+ depth 1)))
3766		       depth))))
3767    ;; Separate foreign select method from group name and collapse.
3768    ;; If method contains a server, collapse to non-domain server name,
3769    ;; otherwise collapse to select method.
3770    (let* ((colon (string-search ":" group))
3771	   (server (and colon (substring group 0 colon)))
3772	   (plus (and server (string-search "+" server))))
3773      (when server
3774	(if plus
3775	    (setq foreign (substring server (+ 1 plus)
3776				     (string-search "." server))
3777		  group (substring group (+ 1 colon)))
3778	  (setq foreign server
3779		group (substring group (+ 1 colon))))
3780	(setq foreign (concat foreign ":")))
3781      ;; Remove braces from name (common in IMAP groups).
3782      (setq group (replace-regexp-in-string "[][]+" "" group))
3783      ;; Collapse group name leaving LEVELS uncollapsed elements
3784      (let* ((slist (split-string group "/"))
3785	     (slen (length slist))
3786	     (dlist (split-string group "\\."))
3787	     (dlen (length dlist))
3788	     glist
3789	     glen
3790	     gsep
3791	     res)
3792	(if (> slen dlen)
3793	    (setq glist slist
3794		  glen slen
3795		  gsep "/")
3796	  (setq glist dlist
3797		glen dlen
3798		gsep "."))
3799	(setq levels (- glen levels))
3800	(dolist (g glist)
3801	  (push (if (>= (cl-decf levels) 0)
3802		    (if (zerop (length g))
3803			""
3804		      (substring g 0 1))
3805		  g)
3806		res))
3807	(concat foreign (mapconcat #'identity (nreverse res) gsep))))))
3808
3809(defun gnus-narrow-to-body ()
3810  "Narrow to the body of an article."
3811  (narrow-to-region
3812   (progn
3813     (goto-char (point-min))
3814     (or (search-forward "\n\n" nil t)
3815	 (point-max)))
3816   (point-max)))
3817
3818
3819;;;
3820;;; Kill file handling.
3821;;;
3822
3823(defun gnus-apply-kill-file ()
3824  "Apply a kill file to the current newsgroup.
3825Returns the number of articles marked as read."
3826  (if (or (file-exists-p (gnus-newsgroup-kill-file nil))
3827	  (file-exists-p (gnus-newsgroup-kill-file gnus-newsgroup-name)))
3828      (gnus-apply-kill-file-internal)
3829    0))
3830
3831(defun gnus-kill-save-kill-buffer ()
3832  (let ((file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
3833    (when (get-file-buffer file)
3834      (with-current-buffer (get-file-buffer file)
3835	(when (buffer-modified-p)
3836	  (save-buffer))
3837	(kill-buffer (current-buffer))))))
3838
3839(defcustom gnus-kill-file-name "KILL"
3840  "Suffix of the kill files."
3841  :group 'gnus-score-kill
3842  :group 'gnus-score-files
3843  :type 'string)
3844
3845(defun gnus-newsgroup-kill-file (newsgroup)
3846  "Return the name of a kill file name for NEWSGROUP.
3847If NEWSGROUP is nil, return the global kill file name instead."
3848  (cond
3849   ;; The global KILL file is placed at top of the directory.
3850   ((or (null newsgroup)
3851	(string-equal newsgroup ""))
3852    (expand-file-name gnus-kill-file-name
3853		      gnus-kill-files-directory))
3854   ;; Append ".KILL" to newsgroup name.
3855   ((gnus-use-long-file-name 'not-kill)
3856    (expand-file-name (concat (gnus-newsgroup-savable-name newsgroup)
3857			      "." gnus-kill-file-name)
3858		      gnus-kill-files-directory))
3859   ;; Place "KILL" under the hierarchical directory.
3860   (t
3861    (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
3862			      "/" gnus-kill-file-name)
3863		      gnus-kill-files-directory))))
3864
3865;;; Server things.
3866
3867(defun gnus-member-of-valid (symbol group)
3868  "Find out if GROUP has SYMBOL as part of its \"valid\" spec."
3869  (memq symbol (assoc
3870		(symbol-name (car (gnus-find-method-for-group group)))
3871		gnus-valid-select-methods)))
3872
3873(defun gnus-method-option-p (method option)
3874  "Return non-nil if select METHOD has OPTION as a parameter."
3875  (when (stringp method)
3876    (setq method (gnus-server-to-method method)))
3877  (memq option (assoc (format "%s" (car method))
3878		      gnus-valid-select-methods)))
3879
3880(defun gnus-similar-server-opened (method)
3881  "Return non-nil if we have a similar server opened.
3882This is defined as a server with the same name, but different
3883parameters."
3884  (let ((opened gnus-opened-servers)
3885	open)
3886    (while (and method opened)
3887      (setq open (car (pop opened)))
3888      ;; Type and name are the same...
3889      (when (and (equal (car method) (car open))
3890		 (equal (cadr method) (cadr open))
3891		 ;; ... but the rest of the parameters differ.
3892		 (not (gnus-methods-sloppily-equal method open)))
3893	(setq method nil)))
3894    (not method)))
3895
3896(defun gnus-server-extend-method (group method)
3897  ;; This function "extends" a virtual server.  If the server is
3898  ;; "hello", and the select method is ("hello" (my-var "something"))
3899  ;; in the group "alt.alt", this will result in a new virtual server
3900  ;; called "hello+alt.alt".
3901  (if (or (not (inline (gnus-similar-server-opened method)))
3902	  (not (cddr method)))
3903      method
3904    (let ((address-slot
3905	   (intern (format "%s-address" (car method)))))
3906      (setq method
3907	    (if (assq address-slot (cddr method))
3908		`(,(car method) ,(concat (cadr method) "+" group)
3909		  ,@(cddr method))
3910	      `(,(car method) ,(concat (cadr method) "+" group)
3911		(,address-slot ,(cadr method))
3912		,@(cddr method))))
3913      (push method gnus-extended-servers)
3914      method)))
3915
3916(defun gnus-server-status (method)
3917  "Return the status of METHOD."
3918  (nth 1 (assoc method gnus-opened-servers)))
3919
3920(defun gnus-group-name-to-method (group)
3921  "Guess a select method based on GROUP."
3922  (if (string-match ":" group)
3923      (let ((server (substring group 0 (match-beginning 0))))
3924	(if (string-match "\\+" server)
3925	    (list (intern (substring server 0 (match-beginning 0)))
3926		  (substring server (match-end 0)))
3927	  (list (intern server) "")))
3928    gnus-select-method))
3929
3930(defun gnus-server-string (server)
3931  "Return a readable string that describes SERVER."
3932  (let* ((server (gnus-server-to-method server))
3933	 (address (nth 1 server)))
3934    (if (and address
3935	     (not (zerop (length address))))
3936	(format "%s using %s" address (car server))
3937      (format "%s" (car server)))))
3938
3939(defun gnus-same-method-different-name (method)
3940  (let ((slot (intern (concat (symbol-name (car method)) "-address"))))
3941    (unless (assq slot (cddr method))
3942      (setq method
3943	    (append method (list (list slot (nth 1 method)))))))
3944  (let ((methods gnus-extended-servers)
3945	open found)
3946    (while (and (not found)
3947		(setq open (pop methods)))
3948      (when (and (eq (car method) (car open))
3949		 (gnus-sloppily-equal-method-parameters method open))
3950	(setq found open)))
3951    found))
3952
3953(defun gnus-find-method-for-group (group &optional info)
3954  "Find the select method that GROUP uses."
3955  (or gnus-override-method
3956      (and (not group)
3957	   gnus-select-method)
3958      (and (not (gnus-group-entry group))
3959	   ;; Killed or otherwise unknown group.
3960	   (or
3961	    ;; If we know a virtual server by that name, return its method.
3962	    (gnus-server-to-method (gnus-group-server group))
3963	    ;; Guess a new method as last resort.
3964	    (gnus-group-name-to-method group)))
3965      (let ((info (or info (gnus-get-info group)))
3966	    method)
3967	(if (or (not info)
3968		(not (setq method (gnus-info-method info)))
3969		(equal method "native"))
3970	    gnus-select-method
3971	  (setq method
3972		(cond ((stringp method)
3973		       (inline (gnus-server-to-method method)))
3974		      ((stringp (cadr method))
3975		       (or
3976			(inline
3977			 (gnus-same-method-different-name method))
3978			(inline (gnus-server-extend-method group method))))
3979		      (t
3980		       method)))
3981	  (cond ((equal (cadr method) "")
3982		 method)
3983		((null (cadr method))
3984		 (list (car method) ""))
3985		(t
3986		 (gnus-server-add-address method)))))))
3987
3988(defun gnus-methods-using (feature)
3989  "Find all methods that have FEATURE."
3990  (let ((valids gnus-valid-select-methods)
3991	outs)
3992    (while valids
3993      (when (memq feature (car valids))
3994	(push (car valids) outs))
3995      (setq valids (cdr valids)))
3996    outs))
3997
3998(autoload 'message-y-or-n-p "message" nil nil 'macro)
3999
4000(defun gnus-read-group (prompt &optional default)
4001  "Prompt the user for a group name.
4002Disallow invalid group names."
4003  (let ((prefix "")
4004	group)
4005    (while (not group)
4006      (when (string-match
4007	     gnus-invalid-group-regexp
4008	     (setq group (read-string (concat prefix prompt)
4009				      (cons (or default "") 0)
4010				      'gnus-group-history)))
4011	(let ((match (match-string 0 group)))
4012	  ;; Might be okay (e.g. for nnimap), so ask the user:
4013	  (unless (and (not (string-match "^$\\|:" match))
4014		       (message-y-or-n-p
4015			"Proceed and create group anyway? " t
4016"The group name \"" group "\" contains a forbidden character: \"" match "\".
4017
4018Usually, it's dangerous to create a group with this name, because it's not
4019supported by all back ends and servers.  On IMAP servers it should work,
4020though.  If you are really sure, you can proceed anyway and create the group.
4021
4022You may customize the variable `gnus-invalid-group-regexp', which currently is
4023set to \"" gnus-invalid-group-regexp
4024"\", if you want to get rid of this query permanently."))
4025	    (setq prefix (format "Invalid group name: \"%s\".  " group)
4026		  group nil)))))
4027    group))
4028
4029(defun gnus-read-method (prompt)
4030  "Prompt the user for a method.
4031Allow completion over sensible values."
4032  (let* ((open-servers
4033	  (mapcar (lambda (i) (cons (format "%s:%s" (caar i) (cadar i)) i))
4034		  gnus-opened-servers))
4035	 (valid-methods
4036	  (let (methods)
4037	    (dolist (method gnus-valid-select-methods)
4038	      (if (or (memq 'prompt-address method)
4039		      (not (assoc (format "%s:" (car method)) open-servers)))
4040		  (push method methods)))
4041	    methods))
4042	 (servers
4043	  (append valid-methods
4044		  open-servers
4045		  gnus-predefined-server-alist
4046		  gnus-server-alist))
4047	 (method
4048	  (gnus-completing-read
4049	   prompt (mapcar #'car servers)
4050	   t nil 'gnus-method-history)))
4051    (cond
4052     ((equal method "")
4053      (setq method gnus-select-method))
4054     ((assoc method gnus-valid-select-methods)
4055      (let ((address (if (memq 'prompt-address
4056			       (assoc method gnus-valid-select-methods))
4057			 (read-string "Address: ")
4058		       "")))
4059	(or (cadr (assoc (format "%s:%s" method address) open-servers))
4060	    (list (intern method) address))))
4061     ((assoc method servers)
4062      method)
4063     (t
4064      (list (intern method) "")))))
4065
4066;;; Agent functions
4067
4068(defun gnus-agent-method-p (method-or-server)
4069  "Say whether METHOD is covered by the agent."
4070  (or (eq (car gnus-agent-method-p-cache) method-or-server)
4071      (let* ((method (if (stringp method-or-server)
4072			 (gnus-server-to-method method-or-server)
4073		       method-or-server))
4074	     (server (gnus-method-to-server method t)))
4075	(setq gnus-agent-method-p-cache
4076	      (cons method-or-server
4077		    (member server gnus-agent-covered-methods)))))
4078  (cdr gnus-agent-method-p-cache))
4079
4080(defun gnus-online (method)
4081  (not
4082   (if gnus-plugged
4083       (eq (cadr (assoc method gnus-opened-servers)) 'offline)
4084     (gnus-agent-method-p method))))
4085
4086;;; User-level commands.
4087
4088;;;###autoload
4089(defun gnus-child-no-server (&optional arg)
4090  "Read network news as a child, without connecting to the local server."
4091  (interactive "P")
4092  (gnus-no-server arg t))
4093
4094;;;###autoload
4095(defun gnus-slave-no-server (&optional arg)
4096  "Read network news as a child, without connecting to the local server."
4097  (interactive "P")
4098  (gnus-no-server arg t))
4099(make-obsolete 'gnus-slave-no-server 'gnus-child-no-server "28.1")
4100
4101;;;###autoload
4102(defun gnus-no-server (&optional arg child)
4103  "Read network news.
4104If ARG is a positive number, Gnus will use that as the startup level.
4105If ARG is nil, Gnus will be started at level 2.  If ARG is non-nil
4106and not a positive number, Gnus will prompt the user for the name of
4107an NNTP server to use.
4108As opposed to `gnus', this command will not connect to the local
4109server."
4110  (interactive "P")
4111  (gnus-no-server-1 arg child))
4112
4113;;;###autoload
4114(defun gnus-child (&optional arg)
4115  "Read news as a child."
4116  (interactive "P")
4117  (gnus arg nil 'child))
4118
4119;;;###autoload
4120(defun gnus-slave (&optional arg)
4121  "Read news as a child."
4122  (interactive "P")
4123  (gnus arg nil 'child))
4124(make-obsolete 'gnus-slave 'gnus-child "28.1")
4125
4126(defun gnus-delete-gnus-frame ()
4127  "Delete gnus frame unless it is the only one.
4128Used for `gnus-exit-gnus-hook' in `gnus-other-frame'."
4129  (when (and (frame-live-p gnus-other-frame-object)
4130             (cdr (frame-list)))
4131    (delete-frame gnus-other-frame-object))
4132  (setq gnus-other-frame-object nil))
4133
4134;;;###autoload
4135(defun gnus-other-frame (&optional arg display)
4136  "Pop up a frame to read news.
4137This will call one of the Gnus commands which is specified by the user
4138option `gnus-other-frame-function' (default `gnus') with the argument
4139ARG if Gnus is not running, otherwise pop up a Gnus frame and run the
4140command specified by `gnus-other-frame-resume-function'.
4141The optional second argument DISPLAY should be a standard display string
4142such as \"unix:0\" to specify where to pop up a frame.  If DISPLAY is
4143omitted or the function `make-frame-on-display' is not available, the
4144current display is used."
4145  (interactive "P")
4146  (if (fboundp 'make-frame-on-display)
4147      (unless display
4148	(setq display (gnus-frame-or-window-display-name (selected-frame))))
4149    (setq display nil))
4150  (let ((alive (gnus-alive-p)))
4151    (unless (and alive
4152		 (catch 'found
4153		   (walk-windows
4154		    (lambda (window)
4155		      (when (and (or (not display)
4156				     (equal display
4157					    (gnus-frame-or-window-display-name
4158					     window)))
4159				 (with-current-buffer (window-buffer window)
4160				   (string-match "\\`gnus-"
4161						 (symbol-name major-mode))))
4162			(select-frame-set-input-focus
4163			 (setq gnus-other-frame-object (window-frame window)))
4164			(select-window window)
4165			(throw 'found t)))
4166		    'ignore t)))
4167      (select-frame-set-input-focus
4168       (setq gnus-other-frame-object
4169	     (if display
4170		 (make-frame-on-display display gnus-other-frame-parameters)
4171	       (make-frame gnus-other-frame-parameters))))
4172      (if alive
4173	  (progn (switch-to-buffer gnus-group-buffer)
4174		 (funcall gnus-other-frame-resume-function arg))
4175	(funcall gnus-other-frame-function arg)
4176	(add-hook 'gnus-exit-gnus-hook #'gnus-delete-gnus-frame)
4177  ;; One might argue that `gnus-delete-gnus-frame' should not be called
4178  ;; from `gnus-suspend-gnus-hook', but, on the other hand, one might
4179  ;; argue that it should.  No matter what you think, for the sake of
4180  ;; those who want it to be called from it, please keep (defun
4181  ;; gnus-delete-gnus-frame) even if you remove the next `add-hook'.
4182  (add-hook 'gnus-suspend-gnus-hook #'gnus-delete-gnus-frame)))))
4183
4184;;;###autoload
4185(defun gnus (&optional arg dont-connect child)
4186  "Read network news.
4187If ARG is non-nil and a positive number, Gnus will use that as the
4188startup level.  If ARG is non-nil and not a positive number, Gnus will
4189prompt the user for the name of an NNTP server to use."
4190  (interactive "P")
4191  ;; When using the development version of Gnus, load the gnus-load
4192  ;; file.
4193  (unless (string-match "^Gnus" gnus-version)
4194    (load "gnus-load" nil t))
4195  (unless (or (byte-code-function-p (symbol-function 'gnus))
4196	      (subr-native-elisp-p (symbol-function 'gnus)))
4197    (message "You should compile Gnus")
4198    (sit-for 2))
4199  (let ((gnus-action-message-log (list nil)))
4200    (gnus-1 arg dont-connect child)
4201    (gnus-final-warning)))
4202
4203(declare-function debbugs-gnu "ext:debbugs-gnu"
4204		  (severities &optional packages archivedp suppress tags))
4205
4206(defun gnus-list-debbugs ()
4207  "List all open Gnus bug reports."
4208  (interactive)
4209  (require 'debbugs-gnu)
4210  (debbugs-gnu nil "gnus"))
4211
4212(provide 'gnus)
4213
4214;;; gnus.el ends here
4215