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