Default to nil since browsing in group buffer takes too long.
[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 nil
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   (dolist (server gnus-inserted-opened-servers)
411     (gnus-server-close-server (car server))))
412
413 (defun gnus-server-deny-server (server)
414   "Make sure SERVER will never be attempted opened."
415   (interactive (list (gnus-server-server-name)))
416   (let ((method (gnus-server-to-method server)))
417     (unless method
418       (error "No such server: %s" server))
419     (gnus-server-set-status method 'denied))
420   (gnus-server-update-server server)
421   (gnus-server-position-point)
422   t)
423
424 (defun gnus-server-remove-denials ()
425   "Make all denied servers into closed servers."
426   (interactive)
427   (dolist (server gnus-opened-servers)
428     (when (eq (nth 1 server) 'denied)
429       (setcar (nthcdr 1 server) 'closed)))
430   (gnus-server-list-servers))
431
432 (defun gnus-server-copy-server (from to)
433   (interactive
434    (list
435     (or (gnus-server-server-name)
436         (error "No server on the current line"))
437     (read-string "Copy to: ")))
438   (unless from
439     (error "No server on current line"))
440   (unless (and to (not (string= to "")))
441     (error "No name to copy to"))
442   (when (assoc to gnus-server-alist)
443     (error "%s already exists" to))
444   (unless (gnus-server-to-method from)
445     (error "%s: no such server" from))
446   (let ((to-entry (cons from (gnus-copy-sequence
447                               (gnus-server-to-method from)))))
448     (setcar to-entry to)
449     (setcar (nthcdr 2 to-entry) to)
450     (push to-entry gnus-server-killed-servers)
451     (gnus-server-yank-server)))
452
453 (defun gnus-server-add-server (how where)
454   (interactive
455    (list (intern (completing-read "Server method: "
456                                   gnus-valid-select-methods nil t))
457          (read-string "Server name: ")))
458   (when (assq where gnus-server-alist)
459     (error "Server with that name already defined"))
460   (push (list where how where) gnus-server-killed-servers)
461   (gnus-server-yank-server))
462
463 (defun gnus-server-goto-server (server)
464   "Jump to a server line."
465   (interactive
466    (list (completing-read "Goto server: " gnus-server-alist nil t)))
467   (let ((to (text-property-any (point-min) (point-max)
468                                'gnus-server (intern server))))
469     (when to
470       (goto-char to)
471       (gnus-server-position-point))))
472
473 (defun gnus-server-edit-server (server)
474   "Edit the server on the current line."
475   (interactive (list (gnus-server-server-name)))
476   (unless server
477     (error "No server on current line"))
478   (unless (assoc server gnus-server-alist)
479     (error "This server can't be edited"))
480   (let ((info (cdr (assoc server gnus-server-alist))))
481     (gnus-close-server info)
482     (gnus-edit-form
483      info "Editing the server."
484      `(lambda (form)
485         (gnus-server-set-info ,server form)
486         (gnus-server-list-servers)
487         (gnus-server-position-point)))))
488
489 (defun gnus-server-scan-server (server)
490   "Request a scan from the current server."
491   (interactive (list (gnus-server-server-name)))
492   (let ((method (gnus-server-to-method server)))
493     (if (not (gnus-get-function method 'request-scan))
494         (error "Server %s can't scan" (car method))
495       (gnus-message 3 "Scanning %s..." server)
496       (gnus-request-scan nil method)
497       (gnus-message 3 "Scanning %s...done" server))))
498
499 (defun gnus-server-read-server-in-server-buffer (server)
500   "Browse a server in server buffer."
501   (interactive (list (gnus-server-server-name)))
502   (let (gnus-server-browse-in-group-buffer)
503     (gnus-server-read-server server)))
504
505 (defun gnus-server-read-server (server)
506   "Browse a server."
507   (interactive (list (gnus-server-server-name)))
508   (let ((buf (current-buffer)))
509     (prog1
510         (gnus-browse-foreign-server server buf)
511       (save-excursion
512         (set-buffer buf)
513         (gnus-server-update-server (gnus-server-server-name))
514         (gnus-server-position-point)))))
515
516 (defun gnus-server-pick-server (e)
517   (interactive "e")
518   (mouse-set-point e)
519   (gnus-server-read-server (gnus-server-server-name)))
520
521 \f
522 ;;;
523 ;;; Browse Server Mode
524 ;;;
525
526 (defvar gnus-browse-menu-hook nil
527   "*Hook run after the creation of the browse mode menu.")
528
529 (defvar gnus-browse-mode-hook nil)
530 (defvar gnus-browse-mode-map nil)
531 (put 'gnus-browse-mode 'mode-class 'special)
532
533 (unless gnus-browse-mode-map
534   (setq gnus-browse-mode-map (make-keymap))
535   (suppress-keymap gnus-browse-mode-map)
536
537   (gnus-define-keys
538       gnus-browse-mode-map
539     " " gnus-browse-read-group
540     "=" gnus-browse-select-group
541     "n" gnus-browse-next-group
542     "p" gnus-browse-prev-group
543     "\177" gnus-browse-prev-group
544     [delete] gnus-browse-prev-group
545     "N" gnus-browse-next-group
546     "P" gnus-browse-prev-group
547     "\M-n" gnus-browse-next-group
548     "\M-p" gnus-browse-prev-group
549     "\r" gnus-browse-select-group
550     "u" gnus-browse-unsubscribe-current-group
551     "l" gnus-browse-exit
552     "L" gnus-browse-exit
553     "q" gnus-browse-exit
554     "Q" gnus-browse-exit
555     "\C-c\C-c" gnus-browse-exit
556     "?" gnus-browse-describe-briefly
557
558     "\C-c\C-i" gnus-info-find-node
559     "\C-c\C-b" gnus-bug))
560
561 (defun gnus-browse-make-menu-bar ()
562   (gnus-turn-off-edit-menu 'browse)
563   (unless (boundp 'gnus-browse-menu)
564     (easy-menu-define
565      gnus-browse-menu gnus-browse-mode-map ""
566      '("Browse"
567        ["Subscribe" gnus-browse-unsubscribe-current-group t]
568        ["Read" gnus-browse-read-group t]
569        ["Select" gnus-browse-select-group t]
570        ["Next" gnus-browse-next-group t]
571        ["Prev" gnus-browse-prev-group t]
572        ["Exit" gnus-browse-exit t]))
573     (gnus-run-hooks 'gnus-browse-menu-hook)))
574
575 (defvar gnus-browse-current-method nil)
576 (defvar gnus-browse-return-buffer nil)
577
578 (defvar gnus-browse-buffer "*Gnus Browse Server*")
579
580 (defun gnus-browse-foreign-server (server &optional return-buffer)
581   "Browse the server SERVER."
582   (setq gnus-browse-current-method (gnus-server-to-method server))
583   (setq gnus-browse-return-buffer return-buffer)
584   (let* ((method gnus-browse-current-method)
585          (orig-select-method gnus-select-method)
586          (gnus-select-method method)
587          groups group)
588     (gnus-message 5 "Connecting to %s..." (nth 1 method))
589     (cond
590      ((not (gnus-check-server method))
591       (gnus-message
592        1 "Unable to contact server %s: %s" (nth 1 method)
593        (gnus-status-message method))
594       nil)
595      ((not
596        (prog2
597            (gnus-message 6 "Reading active file...")
598            (gnus-request-list method)
599          (gnus-message 6 "Reading active file...done")))
600       (gnus-message
601        1 "Couldn't request list: %s" (gnus-status-message method))
602       nil)
603      (t
604       (save-excursion
605         (set-buffer nntp-server-buffer)
606         (let ((cur (current-buffer)))
607           (goto-char (point-min))
608           (unless (string= gnus-ignored-newsgroups "")
609             (delete-matching-lines gnus-ignored-newsgroups))
610           (while (not (eobp))
611             (ignore-errors
612               (push (cons
613                      (if (eq (char-after) ?\")
614                          (read cur)
615                        (let ((p (point)) (name ""))
616                          (skip-chars-forward "^ \t\\\\")
617                          (setq name (buffer-substring p (point)))
618                          (while (eq (char-after) ?\\)
619                            (setq p (1+ (point)))
620                            (forward-char 2)
621                            (skip-chars-forward "^ \t\\\\")
622                            (setq name (concat name (buffer-substring
623                                                     p (point)))))
624                          name))
625                      (let ((last (read cur)))
626                        (cons (read cur) last)))
627                     groups))
628             (forward-line))))
629       (setq groups (sort groups
630                          (lambda (l1 l2)
631                            (string< (car l1) (car l2)))))
632       (if gnus-server-browse-in-group-buffer
633           (let* ((gnus-select-method orig-select-method)
634                  (gnus-group-listed-groups
635                   (mapcar (lambda (group)
636                             (let ((name
637                                    (gnus-group-prefixed-name
638                                     (car group) method)))
639                               (gnus-set-active name (cdr group))
640                               name))
641                           groups)))
642             (gnus-configure-windows 'group)
643             (funcall gnus-group-prepare-function
644                      gnus-level-killed 'ignore 1 'ingore))
645         (gnus-get-buffer-create gnus-browse-buffer)
646         (when gnus-carpal
647           (gnus-carpal-setup-buffer 'browse))
648         (gnus-configure-windows 'browse)
649         (buffer-disable-undo)
650         (let ((buffer-read-only nil))
651           (erase-buffer))
652         (gnus-browse-mode)
653         (setq mode-line-buffer-identification
654               (list
655                (format
656                 "Gnus: %%b {%s:%s}" (car method) (cadr method))))
657         (let ((buffer-read-only nil) charset)
658           (while groups
659             (setq group (car groups))
660             (setq charset (gnus-group-name-charset method group))
661             (gnus-add-text-properties
662              (point)
663              (prog1 (1+ (point))
664                (insert
665                 (format "%c%7d: %s\n"
666                         (let ((level
667                                (let ((gnus-select-method orig-select-method))
668                                  (gnus-group-level
669                                   (gnus-group-prefixed-name (car group)
670                                                             method)))))
671                               (cond
672                                ((<= level gnus-level-subscribed) ? )
673                                ((<= level gnus-level-unsubscribed) ?U)
674                                ((= level gnus-level-zombie) ?Z)
675                                (t ?K)))
676                         (max 0 (- (1+ (cddr group)) (cadr group)))
677                         (gnus-group-name-decode (car group) charset))))
678              (list 'gnus-group (car group)))
679             (setq groups (cdr groups))))
680         (switch-to-buffer (current-buffer)))
681       (goto-char (point-min))
682       (gnus-group-position-point)
683       (gnus-message 5 "Connecting to %s...done" (nth 1 method))
684       t))))
685
686 (defun gnus-browse-mode ()
687   "Major mode for browsing a foreign server.
688
689 All normal editing commands are switched off.
690
691 \\<gnus-browse-mode-map>
692 The only things you can do in this buffer is
693
694 1) `\\[gnus-browse-unsubscribe-current-group]' to subscribe to a group.
695 The group will be inserted into the group buffer upon exit from this
696 buffer.
697
698 2) `\\[gnus-browse-read-group]' to read a group ephemerally.
699
700 3) `\\[gnus-browse-exit]' to return to the group buffer."
701   (interactive)
702   (kill-all-local-variables)
703   (when (gnus-visual-p 'browse-menu 'menu)
704     (gnus-browse-make-menu-bar))
705   (gnus-simplify-mode-line)
706   (setq major-mode 'gnus-browse-mode)
707   (setq mode-name "Browse Server")
708   (setq mode-line-process nil)
709   (use-local-map gnus-browse-mode-map)
710   (buffer-disable-undo)
711   (setq truncate-lines t)
712   (gnus-set-default-directory)
713   (setq buffer-read-only t)
714   (gnus-run-hooks 'gnus-browse-mode-hook))
715
716 (defun gnus-browse-read-group (&optional no-article)
717   "Enter the group at the current line."
718   (interactive)
719   (let ((group (gnus-browse-group-name)))
720     (if (or (not (gnus-get-info group))
721             (gnus-ephemeral-group-p group))
722         (unless (gnus-group-read-ephemeral-group
723                  (gnus-group-real-name group) gnus-browse-current-method nil
724                  (cons (current-buffer) 'browse))
725           (error "Couldn't enter %s" group))
726       (unless (gnus-group-read-group nil no-article group)
727         (error "Couldn't enter %s" group)))))
728
729 (defun gnus-browse-select-group ()
730   "Select the current group."
731   (interactive)
732   (gnus-browse-read-group 'no))
733
734 (defun gnus-browse-next-group (n)
735   "Go to the next group."
736   (interactive "p")
737   (prog1
738       (forward-line n)
739     (gnus-group-position-point)))
740
741 (defun gnus-browse-prev-group (n)
742   "Go to the next group."
743   (interactive "p")
744   (gnus-browse-next-group (- n)))
745
746 (defun gnus-browse-unsubscribe-current-group (arg)
747   "(Un)subscribe to the next ARG groups."
748   (interactive "p")
749   (when (eobp)
750     (error "No group at current line"))
751   (let ((ward (if (< arg 0) -1 1))
752         (arg (abs arg)))
753     (while (and (> arg 0)
754                 (not (eobp))
755                 (gnus-browse-unsubscribe-group)
756                 (zerop (gnus-browse-next-group ward)))
757       (decf arg))
758     (gnus-group-position-point)
759     (when (/= 0 arg)
760       (gnus-message 7 "No more newsgroups"))
761     arg))
762
763 (defun gnus-browse-group-name ()
764   (save-excursion
765     (beginning-of-line)
766     (let ((name (get-text-property (point) 'gnus-group)))
767       (when (re-search-forward ": \\(.*\\)$" (gnus-point-at-eol) t)
768         (gnus-group-prefixed-name
769          (or name
770              (match-string-no-properties 1))
771          gnus-browse-current-method)))))
772
773 (defun gnus-browse-unsubscribe-group ()
774   "Toggle subscription of the current group in the browse buffer."
775   (let ((sub nil)
776         (buffer-read-only nil)
777         group)
778     (save-excursion
779       (beginning-of-line)
780       ;; If this group it killed, then we want to subscribe it.
781       (unless (eq (char-after) ? )
782         (setq sub t))
783       (setq group (gnus-browse-group-name))
784       ;;;;
785       ;;(when (and sub
786       ;;                 (cadr (gnus-gethash group gnus-newsrc-hashtb)))
787       ;;(error "Group already subscribed"))
788       (if sub
789           (progn
790             ;; Make sure the group has been properly removed before we
791             ;; subscribe to it.
792             (gnus-kill-ephemeral-group group)
793             (gnus-group-change-level
794              (list t group gnus-level-default-subscribed
795                    nil nil (if (gnus-server-equal
796                                 gnus-browse-current-method "native")
797                                nil
798                              (gnus-method-simplify
799                               gnus-browse-current-method)))
800              gnus-level-default-subscribed (gnus-group-level group)
801              (and (car (nth 1 gnus-newsrc-alist))
802                   (gnus-gethash (car (nth 1 gnus-newsrc-alist))
803                                 gnus-newsrc-hashtb))
804              t)
805             (delete-char 1)
806             (insert ? ))
807         (gnus-group-change-level
808          group gnus-level-unsubscribed gnus-level-default-subscribed)
809         (delete-char 1)
810         (insert ?U)))
811     t))
812
813 (defun gnus-browse-exit ()
814   "Quit browsing and return to the group buffer."
815   (interactive)
816   (when (eq major-mode 'gnus-browse-mode)
817     (kill-buffer (current-buffer)))
818   ;; Insert the newly subscribed groups in the group buffer.
819   (save-excursion
820     (set-buffer gnus-group-buffer)
821     (gnus-group-list-groups nil))
822   (if gnus-browse-return-buffer
823       (gnus-configure-windows 'server 'force)
824     (gnus-configure-windows 'group 'force)))
825
826 (defun gnus-browse-describe-briefly ()
827   "Give a one line description of the group mode commands."
828   (interactive)
829   (gnus-message 6
830                 (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")))
831
832 (defun gnus-server-regenerate-server ()
833   "Issue a command to the server to regenerate all its data structures."
834   (interactive)
835   (let ((server (gnus-server-server-name)))
836     (unless server
837       (error "No server on the current line"))
838     (condition-case ()
839         (gnus-get-function (gnus-server-to-method server)
840                            'request-regenerate)
841       (error
842         (error "This backend doesn't support regeneration")))
843     (gnus-message 5 "Requesting regeneration of %s..." server)
844     (unless (gnus-open-server server)
845       (error "Couldn't open server"))
846     (if (gnus-request-regenerate server)
847         (gnus-message 5 "Requesting regeneration of %s...done" server)
848       (gnus-message 5 "Couldn't regenerate %s" server))))
849
850 (provide 'gnus-srvr)
851
852 ;;; gnus-srvr.el ends here