2001-01-24 17:00:00 ShengHuo ZHU <zsh@cs.rochester.edu>
[gnus] / lisp / gnus-srvr.el
1 ;;; gnus-srvr.el --- virtual server support for Gnus
2 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;; Keywords: news
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 (eval-when-compile (require 'cl))
30
31 (require 'gnus)
32 (require 'gnus-spec)
33 (require 'gnus-group)
34 (require 'gnus-int)
35 (require 'gnus-range)
36
37 (defvar gnus-server-mode-hook nil
38   "Hook run in `gnus-server-mode' buffers.")
39
40 (defconst gnus-server-line-format "     {%(%h:%w%)} %s%a\n"
41   "Format of server lines.
42 It works along the same lines as a normal formatting string,
43 with some simple extensions.
44
45 The following specs are understood:
46
47 %h backend
48 %n name
49 %w address
50 %s status
51 %a agent covered")
52
53 (defvar gnus-server-mode-line-format "Gnus: %%b"
54   "The format specification for the server mode line.")
55
56 (defvar gnus-server-exit-hook nil
57   "*Hook run when exiting the server buffer.")
58
59 (defvar gnus-server-browse-in-group-buffer t
60   "Whether browse server in group buffer.")
61
62 ;;; Internal variables.
63
64 (defvar gnus-inserted-opened-servers nil)
65
66 (defvar gnus-server-line-format-alist
67   `((?h gnus-tmp-how ?s)
68     (?n gnus-tmp-name ?s)
69     (?w gnus-tmp-where ?s)
70     (?s gnus-tmp-status ?s)
71     (?a gnus-tmp-agent ?s)))
72
73 (defvar gnus-server-mode-line-format-alist
74   `((?S gnus-tmp-news-server ?s)
75     (?M gnus-tmp-news-method ?s)
76     (?u gnus-tmp-user-defined ?s)))
77
78 (defvar gnus-server-line-format-spec nil)
79 (defvar gnus-server-mode-line-format-spec nil)
80 (defvar gnus-server-killed-servers nil)
81
82 (defvar gnus-server-mode-map)
83
84 (defvar gnus-server-menu-hook nil
85   "*Hook run after the creation of the server mode menu.")
86
87 (defun gnus-server-make-menu-bar ()
88   (gnus-turn-off-edit-menu 'server)
89   (unless (boundp 'gnus-server-server-menu)
90     (easy-menu-define
91      gnus-server-server-menu gnus-server-mode-map ""
92      '("Server"
93        ["Add" gnus-server-add-server t]
94        ["Browse" gnus-server-read-server t]
95        ["Scan" gnus-server-scan-server t]
96        ["List" gnus-server-list-servers t]
97        ["Kill" gnus-server-kill-server t]
98        ["Yank" gnus-server-yank-server t]
99        ["Copy" gnus-server-copy-server t]
100        ["Edit" gnus-server-edit-server t]
101        ["Regenerate" gnus-server-regenerate-server t]
102        ["Exit" gnus-server-exit t]))
103
104     (easy-menu-define
105      gnus-server-connections-menu gnus-server-mode-map ""
106      '("Connections"
107        ["Open" gnus-server-open-server t]
108        ["Close" gnus-server-close-server t]
109        ["Deny" gnus-server-deny-server t]
110        "---"
111        ["Open All" gnus-server-open-all-servers t]
112        ["Close All" gnus-server-close-all-servers t]
113        ["Reset All" gnus-server-remove-denials t]))
114
115     (gnus-run-hooks 'gnus-server-menu-hook)))
116
117 (defvar gnus-server-mode-map nil)
118 (put 'gnus-server-mode 'mode-class 'special)
119
120 (unless gnus-server-mode-map
121   (setq gnus-server-mode-map (make-sparse-keymap))
122   (suppress-keymap gnus-server-mode-map)
123
124   (gnus-define-keys gnus-server-mode-map
125     " " gnus-server-read-server-in-server-buffer
126     "\r" gnus-server-read-server
127     gnus-mouse-2 gnus-server-pick-server
128     "q" gnus-server-exit
129     "l" gnus-server-list-servers
130     "k" gnus-server-kill-server
131     "y" gnus-server-yank-server
132     "c" gnus-server-copy-server
133     "a" gnus-server-add-server
134     "e" gnus-server-edit-server
135     "s" gnus-server-scan-server
136
137     "O" gnus-server-open-server
138     "\M-o" gnus-server-open-all-servers
139     "C" gnus-server-close-server
140     "\M-c" gnus-server-close-all-servers
141     "D" gnus-server-deny-server
142     "R" gnus-server-remove-denials
143
144     "n" next-line
145     "p" previous-line
146
147     "g" gnus-server-regenerate-server
148
149     "\C-c\C-i" gnus-info-find-node
150     "\C-c\C-b" gnus-bug))
151
152 (defun gnus-server-mode ()
153   "Major mode for listing and editing servers.
154
155 All normal editing commands are switched off.
156 \\<gnus-server-mode-map>
157 For more in-depth information on this mode, read the manual
158 (`\\[gnus-info-find-node]').
159
160 The following commands are available:
161
162 \\{gnus-server-mode-map}"
163   (interactive)
164   (when (gnus-visual-p 'server-menu 'menu)
165     (gnus-server-make-menu-bar))
166   (kill-all-local-variables)
167   (gnus-simplify-mode-line)
168   (setq major-mode 'gnus-server-mode)
169   (setq mode-name "Server")
170   (gnus-set-default-directory)
171   (setq mode-line-process nil)
172   (use-local-map gnus-server-mode-map)
173   (buffer-disable-undo)
174   (setq truncate-lines t)
175   (setq buffer-read-only t)
176   (gnus-run-hooks 'gnus-server-mode-hook))
177
178 (defun gnus-server-insert-server-line (gnus-tmp-name method)
179   (let* ((gnus-tmp-how (car method))
180          (gnus-tmp-where (nth 1 method))
181          (elem (assoc method gnus-opened-servers))
182          (gnus-tmp-status (cond ((eq (nth 1 elem) 'denied)
183                                  "(denied)")
184                                 ((or (gnus-server-opened method)
185                                      (eq (nth 1 elem) 'ok))
186                                  "(opened)")
187                                 (t
188                                  "(closed)")))
189          (gnus-tmp-agent (if (and gnus-agent
190                                   (member method
191                                           gnus-agent-covered-methods))
192                              "(agent)"
193                            "")))
194     (beginning-of-line)
195     (gnus-add-text-properties
196      (point)
197      (prog1 (1+ (point))
198        ;; Insert the text.
199        (eval gnus-server-line-format-spec))
200      (list 'gnus-server (intern gnus-tmp-name)))))
201
202 (defun gnus-enter-server-buffer ()
203   "Set up the server buffer."
204   (gnus-server-setup-buffer)
205   (gnus-configure-windows 'server)
206   (gnus-server-prepare))
207
208 (defun gnus-server-setup-buffer ()
209   "Initialize the server buffer."
210   (unless (get-buffer gnus-server-buffer)
211     (save-excursion
212       (set-buffer (gnus-get-buffer-create gnus-server-buffer))
213       (gnus-server-mode)
214       (when gnus-carpal
215         (gnus-carpal-setup-buffer 'server)))))
216
217 (defun gnus-server-prepare ()
218   (gnus-set-format 'server-mode)
219   (gnus-set-format 'server t)
220   (let ((alist gnus-server-alist)
221         (buffer-read-only nil)
222         (opened gnus-opened-servers)
223         done server op-ser)
224     (erase-buffer)
225     (setq gnus-inserted-opened-servers nil)
226     ;; First we do the real list of servers.
227     (while alist
228       (unless (member (cdar alist) done)
229         (push (cdar alist) done)
230         (cdr (setq server (pop alist)))
231         (when (and server (car server) (cdr server))
232           (gnus-server-insert-server-line (car server) (cdr server))))
233       (when (member (cdar alist) done)
234         (pop alist)))
235     ;; Then we insert the list of servers that have been opened in
236     ;; this session.
237     (while opened
238       (when (and (not (member (caar opened) done))
239                  ;; Just ignore ephemeral servers.
240                  (not (member (caar opened) gnus-ephemeral-servers)))
241         (push (caar opened) done)
242         (gnus-server-insert-server-line
243          (setq op-ser (format "%s:%s" (caaar opened) (nth 1 (caar opened))))
244          (caar opened))
245         (push (list op-ser (caar opened)) gnus-inserted-opened-servers))
246       (setq opened (cdr opened))))
247   (goto-char (point-min))
248   (gnus-server-position-point))
249
250 (defun gnus-server-server-name ()
251   (let ((server (get-text-property (gnus-point-at-bol) 'gnus-server)))
252     (and server (symbol-name server))))
253
254 (defalias 'gnus-server-position-point 'gnus-goto-colon)
255
256 (defconst gnus-server-edit-buffer "*Gnus edit server*")
257
258 (defun gnus-server-update-server (server)
259   (save-excursion
260     (set-buffer gnus-server-buffer)
261     (let* ((buffer-read-only nil)
262            (entry (assoc server gnus-server-alist))
263            (oentry (assoc (gnus-server-to-method server)
264                           gnus-opened-servers)))
265       (when entry
266         (gnus-dribble-enter
267          (concat "(gnus-server-set-info \"" server "\" '"
268                  (prin1-to-string (cdr entry)) ")\n")))
269       (when (or entry oentry)
270         ;; Buffer may be narrowed.
271         (save-restriction
272           (widen)
273           (when (gnus-server-goto-server server)
274             (gnus-delete-line))
275           (if entry
276               (gnus-server-insert-server-line (car entry) (cdr entry))
277             (gnus-server-insert-server-line
278              (format "%s:%s" (caar oentry) (nth 1 (car oentry)))
279              (car oentry)))
280           (gnus-server-position-point))))))
281
282 (defun gnus-server-set-info (server info)
283   ;; Enter a select method into the virtual server alist.
284   (when (and server info)
285     (gnus-dribble-enter
286      (concat "(gnus-server-set-info \"" server "\" '"
287              (prin1-to-string info) ")"))
288     (let* ((server (nth 1 info))
289            (entry (assoc server gnus-server-alist)))
290       (if entry (setcdr entry info)
291         (setq gnus-server-alist
292               (nconc gnus-server-alist (list (cons server info))))))))
293
294 ;;; Interactive server functions.
295
296 (defun gnus-server-kill-server (server)
297   "Kill the server on the current line."
298   (interactive (list (gnus-server-server-name)))
299   (unless (gnus-server-goto-server server)
300     (if server (error "No such server: %s" server)
301       (error "No server on the current line")))
302   (unless (assoc server gnus-server-alist)
303     (error "Read-only server %s" server))
304   (gnus-dribble-touch)
305   (let ((buffer-read-only nil))
306     (gnus-delete-line))
307   (push (assoc server gnus-server-alist) gnus-server-killed-servers)
308   (setq gnus-server-alist (delq (car gnus-server-killed-servers)
309                                 gnus-server-alist))
310   (let ((groups (gnus-groups-from-server server)))
311     (when (and groups
312                (gnus-yes-or-no-p
313                 (format "Kill all %s groups from this server? "
314                         (length groups))))
315       (dolist (group groups)
316         (setq gnus-newsrc-alist
317               (delq (assoc group gnus-newsrc-alist)
318                     gnus-newsrc-alist))
319         (when gnus-group-change-level-function
320           (funcall gnus-group-change-level-function
321                    group gnus-level-killed 3)))))
322   (gnus-server-position-point))
323
324 (defun gnus-server-yank-server ()
325   "Yank the previously killed server."
326   (interactive)
327   (unless gnus-server-killed-servers
328     (error "No killed servers to be yanked"))
329   (let ((alist gnus-server-alist)
330         (server (gnus-server-server-name))
331         (killed (car gnus-server-killed-servers)))
332     (if (not server)
333         (setq gnus-server-alist (nconc gnus-server-alist (list killed)))
334       (if (string= server (caar gnus-server-alist))
335           (push killed gnus-server-alist)
336         (while (and (cdr alist)
337                     (not (string= server (caadr alist))))
338           (setq alist (cdr alist)))
339         (if alist
340             (setcdr alist (cons killed (cdr alist)))
341           (setq gnus-server-alist (list killed)))))
342     (gnus-server-update-server (car killed))
343     (setq gnus-server-killed-servers (cdr gnus-server-killed-servers))
344     (gnus-server-position-point)))
345
346 (defun gnus-server-exit ()
347   "Return to the group buffer."
348   (interactive)
349   (gnus-run-hooks 'gnus-server-exit-hook)
350   (kill-buffer (current-buffer))
351   (gnus-configure-windows 'group t))
352
353 (defun gnus-server-list-servers ()
354   "List all available servers."
355   (interactive)
356   (let ((cur (gnus-server-server-name)))
357     (gnus-server-prepare)
358     (if cur (gnus-server-goto-server cur)
359       (goto-char (point-max))
360       (forward-line -1))
361     (gnus-server-position-point)))
362
363 (defun gnus-server-set-status (method status)
364   "Make METHOD have STATUS."
365   (let ((entry (assoc method gnus-opened-servers)))
366     (if entry
367         (setcar (cdr entry) status)
368       (push (list method status) gnus-opened-servers))))
369
370 (defun gnus-opened-servers-remove (method)
371   "Remove METHOD from the list of opened servers."
372   (setq gnus-opened-servers (delq (assoc method gnus-opened-servers)
373                                   gnus-opened-servers)))
374
375 (defun gnus-server-open-server (server)
376   "Force an open of SERVER."
377   (interactive (list (gnus-server-server-name)))
378   (let ((method (gnus-server-to-method server)))
379     (unless method
380       (error "No such server: %s" server))
381     (gnus-server-set-status method 'ok)
382     (prog1
383         (or (gnus-open-server method)
384             (progn (message "Couldn't open %s" server) nil))
385       (gnus-server-update-server server)
386       (gnus-server-position-point))))
387
388 (defun gnus-server-open-all-servers ()
389   "Open all servers."
390   (interactive)
391   (let ((servers gnus-inserted-opened-servers))
392     (while servers
393       (gnus-server-open-server (car (pop servers))))))
394
395 (defun gnus-server-close-server (server)
396   "Close SERVER."
397   (interactive (list (gnus-server-server-name)))
398   (let ((method (gnus-server-to-method server)))
399     (unless method
400       (error "No such server: %s" server))
401     (gnus-server-set-status method 'closed)
402     (prog1
403         (gnus-close-server method)
404       (gnus-server-update-server server)
405       (gnus-server-position-point))))
406
407 (defun gnus-server-close-all-servers ()
408   "Close all servers."
409   (interactive)
410   (let ((servers gnus-inserted-opened-servers))
411     (while servers
412       (gnus-server-close-server (car (pop servers))))))
413
414 (defun gnus-server-deny-server (server)
415   "Make sure SERVER will never be attempted opened."
416   (interactive (list (gnus-server-server-name)))
417   (let ((method (gnus-server-to-method server)))
418     (unless method
419       (error "No such server: %s" server))
420     (gnus-server-set-status method 'denied))
421   (gnus-server-update-server server)
422   (gnus-server-position-point)
423   t)
424
425 (defun gnus-server-remove-denials ()
426   "Make all denied servers into closed servers."
427   (interactive)
428   (let ((servers gnus-opened-servers))
429     (while servers
430       (when (eq (nth 1 (car servers)) 'denied)
431         (setcar (nthcdr 1 (car servers)) 'closed))
432       (setq servers (cdr servers))))
433   (gnus-server-list-servers))
434
435 (defun gnus-server-copy-server (from to)
436   (interactive
437    (list
438     (or (gnus-server-server-name)
439         (error "No server on the current line"))
440     (read-string "Copy to: ")))
441   (unless from
442     (error "No server on current line"))
443   (unless (and to (not (string= to "")))
444     (error "No name to copy to"))
445   (when (assoc to gnus-server-alist)
446     (error "%s already exists" to))
447   (unless (gnus-server-to-method from)
448     (error "%s: no such server" from))
449   (let ((to-entry (cons from (gnus-copy-sequence
450                               (gnus-server-to-method from)))))
451     (setcar to-entry to)
452     (setcar (nthcdr 2 to-entry) to)
453     (push to-entry gnus-server-killed-servers)
454     (gnus-server-yank-server)))
455
456 (defun gnus-server-add-server (how where)
457   (interactive
458    (list (intern (completing-read "Server method: "
459                                   gnus-valid-select-methods nil t))
460          (read-string "Server name: ")))
461   (when (assq where gnus-server-alist)
462     (error "Server with that name already defined"))
463   (push (list where how where) gnus-server-killed-servers)
464   (gnus-server-yank-server))
465
466 (defun gnus-server-goto-server (server)
467   "Jump to a server line."
468   (interactive
469    (list (completing-read "Goto server: " gnus-server-alist nil t)))
470   (let ((to (text-property-any (point-min) (point-max)
471                                'gnus-server (intern server))))
472     (when to
473       (goto-char to)
474       (gnus-server-position-point))))
475
476 (defun gnus-server-edit-server (server)
477   "Edit the server on the current line."
478   (interactive (list (gnus-server-server-name)))
479   (unless server
480     (error "No server on current line"))
481   (unless (assoc server gnus-server-alist)
482     (error "This server can't be edited"))
483   (let ((info (cdr (assoc server gnus-server-alist))))
484     (gnus-close-server info)
485     (gnus-edit-form
486      info "Editing the server."
487      `(lambda (form)
488         (gnus-server-set-info ,server form)
489         (gnus-server-list-servers)
490         (gnus-server-position-point)))))
491
492 (defun gnus-server-scan-server (server)
493   "Request a scan from the current server."
494   (interactive (list (gnus-server-server-name)))
495   (let ((method (gnus-server-to-method server)))
496     (if (not (gnus-get-function method 'request-scan))
497         (error "Server %s can't scan" (car method))
498       (gnus-message 3 "Scanning %s..." server)
499       (gnus-request-scan nil method)
500       (gnus-message 3 "Scanning %s...done" server))))
501
502 (defun gnus-server-read-server-in-server-buffer (server)
503   "Browse a server in server buffer."
504   (interactive (list (gnus-server-server-name)))
505   (let (gnus-server-browse-in-group-buffer)
506     (gnus-server-read-server server)))
507
508 (defun gnus-server-read-server (server)
509   "Browse a server."
510   (interactive (list (gnus-server-server-name)))
511   (let ((buf (current-buffer)))
512     (prog1
513         (gnus-browse-foreign-server server buf)
514       (save-excursion
515         (set-buffer buf)
516         (gnus-server-update-server (gnus-server-server-name))
517         (gnus-server-position-point)))))
518
519 (defun gnus-server-pick-server (e)
520   (interactive "e")
521   (mouse-set-point e)
522   (gnus-server-read-server (gnus-server-server-name)))
523
524 \f
525 ;;;
526 ;;; Browse Server Mode
527 ;;;
528
529 (defvar gnus-browse-menu-hook nil
530   "*Hook run after the creation of the browse mode menu.")
531
532 (defvar gnus-browse-mode-hook nil)
533 (defvar gnus-browse-mode-map nil)
534 (put 'gnus-browse-mode 'mode-class 'special)
535
536 (unless gnus-browse-mode-map
537   (setq gnus-browse-mode-map (make-keymap))
538   (suppress-keymap gnus-browse-mode-map)
539
540   (gnus-define-keys
541       gnus-browse-mode-map
542     " " gnus-browse-read-group
543     "=" gnus-browse-select-group
544     "n" gnus-browse-next-group
545     "p" gnus-browse-prev-group
546     "\177" gnus-browse-prev-group
547     [delete] gnus-browse-prev-group
548     "N" gnus-browse-next-group
549     "P" gnus-browse-prev-group
550     "\M-n" gnus-browse-next-group
551     "\M-p" gnus-browse-prev-group
552     "\r" gnus-browse-select-group
553     "u" gnus-browse-unsubscribe-current-group
554     "l" gnus-browse-exit
555     "L" gnus-browse-exit
556     "q" gnus-browse-exit
557     "Q" gnus-browse-exit
558     "\C-c\C-c" gnus-browse-exit
559     "?" gnus-browse-describe-briefly
560
561     "\C-c\C-i" gnus-info-find-node
562     "\C-c\C-b" gnus-bug))
563
564 (defun gnus-browse-make-menu-bar ()
565   (gnus-turn-off-edit-menu 'browse)
566   (unless (boundp 'gnus-browse-menu)
567     (easy-menu-define
568      gnus-browse-menu gnus-browse-mode-map ""
569      '("Browse"
570        ["Subscribe" gnus-browse-unsubscribe-current-group t]
571        ["Read" gnus-browse-read-group t]
572        ["Select" gnus-browse-select-group t]
573        ["Next" gnus-browse-next-group t]
574        ["Prev" gnus-browse-next-group t]
575        ["Exit" gnus-browse-exit t]))
576     (gnus-run-hooks 'gnus-browse-menu-hook)))
577
578 (defvar gnus-browse-current-method nil)
579 (defvar gnus-browse-return-buffer nil)
580
581 (defvar gnus-browse-buffer "*Gnus Browse Server*")
582
583 (defun gnus-browse-foreign-server (server &optional return-buffer)
584   "Browse the server SERVER."
585   (setq gnus-browse-current-method (gnus-server-to-method server))
586   (setq gnus-browse-return-buffer return-buffer)
587   (let* ((method gnus-browse-current-method)
588          (orig-select-method gnus-select-method)
589          (gnus-select-method method)
590          groups group)
591     (gnus-message 5 "Connecting to %s..." (nth 1 method))
592     (cond
593      ((not (gnus-check-server method))
594       (gnus-message
595        1 "Unable to contact server %s: %s" (nth 1 method)
596        (gnus-status-message method))
597       nil)
598      ((not
599        (prog2
600            (gnus-message 6 "Reading active file...")
601            (gnus-request-list method)
602          (gnus-message 6 "Reading active file...done")))
603       (gnus-message
604        1 "Couldn't request list: %s" (gnus-status-message method))
605       nil)
606      (t
607       (save-excursion
608         (set-buffer nntp-server-buffer)
609         (let ((cur (current-buffer)))
610           (goto-char (point-min))
611           (unless (string= gnus-ignored-newsgroups "")
612             (delete-matching-lines gnus-ignored-newsgroups))
613           (while (not (eobp))
614             (ignore-errors
615               (push (cons
616                      (if (eq (char-after) ?\")
617                          (read cur)
618                        (let ((p (point)) (name ""))
619                          (skip-chars-forward "^ \t\\\\")
620                          (setq name (buffer-substring p (point)))
621                          (while (eq (char-after) ?\\)
622                            (setq p (1+ (point)))
623                            (forward-char 2)
624                            (skip-chars-forward "^ \t\\\\")
625                            (setq name (concat name (buffer-substring
626                                                     p (point)))))
627                          name))
628                      (let ((last (read cur)))
629                        (cons (read cur) last)))
630                     groups))
631             (forward-line))))
632       (setq groups (sort groups
633                          (lambda (l1 l2)
634                            (string< (car l1) (car l2)))))
635       (if gnus-server-browse-in-group-buffer
636           (let* ((gnus-select-method orig-select-method)
637                  (gnus-group-listed-groups
638                   (mapcar (lambda (group)
639                             (let ((name
640                                    (gnus-group-prefixed-name
641                                     (car group) method)))
642                               (gnus-set-active name (cdr group))
643                               name))
644                           groups)))
645             (gnus-configure-windows 'group)
646             (funcall gnus-group-prepare-function
647                      gnus-level-killed 'ignore 1 'ingore))
648         (gnus-get-buffer-create gnus-browse-buffer)
649         (when gnus-carpal
650           (gnus-carpal-setup-buffer 'browse))
651         (gnus-configure-windows 'browse)
652         (buffer-disable-undo)
653         (let ((buffer-read-only nil))
654           (erase-buffer))
655         (gnus-browse-mode)
656         (setq mode-line-buffer-identification
657               (list
658                (format
659                 "Gnus: %%b {%s:%s}" (car method) (cadr method))))
660         (let ((buffer-read-only nil) charset)
661           (while groups
662             (setq group (car groups))
663             (setq charset (gnus-group-name-charset method group))
664             (gnus-add-text-properties
665              (point)
666              (prog1 (1+ (point))
667                (insert
668                 (format "%c%7d: %s\n"
669                         (let ((level
670                                (let ((gnus-select-method orig-select-method))
671                                  (gnus-group-level
672                                   (gnus-group-prefixed-name (car group)
673                                                             method)))))
674                               (cond
675                                ((<= level gnus-level-subscribed) ? )
676                                ((<= level gnus-level-unsubscribed) ?U)
677                                ((= level gnus-level-zombie) ?Z)
678                                (t ?K)))
679                         (max 0 (- (1+ (cddr group)) (cadr group)))
680                         (gnus-group-name-decode (car group) charset))))
681              (list 'gnus-group (car group)))
682             (setq groups (cdr groups))))
683         (switch-to-buffer (current-buffer)))
684       (goto-char (point-min))
685       (gnus-group-position-point)
686       (gnus-message 5 "Connecting to %s...done" (nth 1 method))
687       t))))
688
689 (defun gnus-browse-mode ()
690   "Major mode for browsing a foreign server.
691
692 All normal editing commands are switched off.
693
694 \\<gnus-browse-mode-map>
695 The only things you can do in this buffer is
696
697 1) `\\[gnus-browse-unsubscribe-current-group]' to subscribe to a group.
698 The group will be inserted into the group buffer upon exit from this
699 buffer.
700
701 2) `\\[gnus-browse-read-group]' to read a group ephemerally.
702
703 3) `\\[gnus-browse-exit]' to return to the group buffer."
704   (interactive)
705   (kill-all-local-variables)
706   (when (gnus-visual-p 'browse-menu 'menu)
707     (gnus-browse-make-menu-bar))
708   (gnus-simplify-mode-line)
709   (setq major-mode 'gnus-browse-mode)
710   (setq mode-name "Browse Server")
711   (setq mode-line-process nil)
712   (use-local-map gnus-browse-mode-map)
713   (buffer-disable-undo)
714   (setq truncate-lines t)
715   (gnus-set-default-directory)
716   (setq buffer-read-only t)
717   (gnus-run-hooks 'gnus-browse-mode-hook))
718
719 (defun gnus-browse-read-group (&optional no-article)
720   "Enter the group at the current line."
721   (interactive)
722   (let ((group (gnus-browse-group-name)))
723     (if (or (not (gnus-get-info group))
724             (gnus-ephemeral-group-p group))
725         (unless (gnus-group-read-ephemeral-group
726                  (gnus-group-real-name group) gnus-browse-current-method nil
727                  (cons (current-buffer) 'browse))
728           (error "Couldn't enter %s" group))
729       (unless (gnus-group-read-group nil no-article group)
730         (error "Couldn't enter %s" group)))))
731
732 (defun gnus-browse-select-group ()
733   "Select the current group."
734   (interactive)
735   (gnus-browse-read-group 'no))
736
737 (defun gnus-browse-next-group (n)
738   "Go to the next group."
739   (interactive "p")
740   (prog1
741       (forward-line n)
742     (gnus-group-position-point)))
743
744 (defun gnus-browse-prev-group (n)
745   "Go to the next group."
746   (interactive "p")
747   (gnus-browse-next-group (- n)))
748
749 (defun gnus-browse-unsubscribe-current-group (arg)
750   "(Un)subscribe to the next ARG groups."
751   (interactive "p")
752   (when (eobp)
753     (error "No group at current line"))
754   (let ((ward (if (< arg 0) -1 1))
755         (arg (abs arg)))
756     (while (and (> arg 0)
757                 (not (eobp))
758                 (gnus-browse-unsubscribe-group)
759                 (zerop (gnus-browse-next-group ward)))
760       (decf arg))
761     (gnus-group-position-point)
762     (when (/= 0 arg)
763       (gnus-message 7 "No more newsgroups"))
764     arg))
765
766 (defun gnus-browse-group-name ()
767   (save-excursion
768     (beginning-of-line)
769     (let ((name (get-text-property (point) 'gnus-group)))
770       (when (re-search-forward ": \\(.*\\)$" (gnus-point-at-eol) t)
771         (gnus-group-prefixed-name
772          (or name
773              (match-string-no-properties 1))
774          gnus-browse-current-method)))))
775
776 (defun gnus-browse-unsubscribe-group ()
777   "Toggle subscription of the current group in the browse buffer."
778   (let ((sub nil)
779         (buffer-read-only nil)
780         group)
781     (save-excursion
782       (beginning-of-line)
783       ;; If this group it killed, then we want to subscribe it.
784       (unless (eq (char-after) ? )
785         (setq sub t))
786       (setq group (gnus-browse-group-name))
787       ;;;;
788       ;;(when (and sub
789       ;;                 (cadr (gnus-gethash group gnus-newsrc-hashtb)))
790       ;;(error "Group already subscribed"))
791       (if sub
792           (progn
793             ;; Make sure the group has been properly removed before we
794             ;; subscribe to it.
795             (gnus-kill-ephemeral-group group)
796             (gnus-group-change-level
797              (list t group gnus-level-default-subscribed
798                    nil nil (if (gnus-server-equal
799                                 gnus-browse-current-method "native")
800                                nil
801                              (gnus-method-simplify
802                               gnus-browse-current-method)))
803              gnus-level-default-subscribed (gnus-group-level group)
804              (and (car (nth 1 gnus-newsrc-alist))
805                   (gnus-gethash (car (nth 1 gnus-newsrc-alist))
806                                 gnus-newsrc-hashtb))
807              t)
808             (delete-char 1)
809             (insert ? ))
810         (gnus-group-change-level
811          group gnus-level-unsubscribed gnus-level-default-subscribed)
812         (delete-char 1)
813         (insert ?U)))
814     t))
815
816 (defun gnus-browse-exit ()
817   "Quit browsing and return to the group buffer."
818   (interactive)
819   (when (eq major-mode 'gnus-browse-mode)
820     (kill-buffer (current-buffer)))
821   ;; Insert the newly subscribed groups in the group buffer.
822   (save-excursion
823     (set-buffer gnus-group-buffer)
824     (gnus-group-list-groups nil))
825   (if gnus-browse-return-buffer
826       (gnus-configure-windows 'server 'force)
827     (gnus-configure-windows 'group 'force)))
828
829 (defun gnus-browse-describe-briefly ()
830   "Give a one line description of the group mode commands."
831   (interactive)
832   (gnus-message 6
833                 (substitute-command-keys "\\<gnus-browse-mode-map>\\[gnus-group-next-group]:Forward  \\[gnus-group-prev-group]:Backward  \\[gnus-browse-exit]:Exit  \\[gnus-info-find-node]:Run Info  \\[gnus-browse-describe-briefly]:This help")))
834
835 (defun gnus-server-regenerate-server ()
836   "Issue a command to the server to regenerate all its data structures."
837   (interactive)
838   (let ((server (gnus-server-server-name)))
839     (unless server
840       (error "No server on the current line"))
841     (if (not (gnus-check-backend-function
842               'request-regenerate (car (gnus-server-to-method server))))
843         (error "This backend doesn't support regeneration")
844       (gnus-message 5 "Requesting regeneration of %s..." server)
845       (unless (gnus-open-server server)
846         (error "Couldn't open server"))
847       (if (gnus-request-regenerate server)
848           (gnus-message 5 "Requesting regeneration of %s...done" server)
849         (gnus-message 5 "Couldn't regenerate %s" server)))))
850
851 (provide 'gnus-srvr)
852
853 ;;; gnus-srvr.el ends here.