Mercurial > emacs
comparison lisp/gnus/gnus-util.el @ 56927:55fd4f77387a after-merge-gnus-5_10
Revision: miles@gnu.org--gnu-2004/emacs--cvs-trunk--0--patch-523
Merge from emacs--gnus--5.10, gnus--rel--5.10
Patches applied:
* miles@gnu.org--gnu-2004/emacs--gnus--5.10--base-0
tag of miles@gnu.org--gnu-2004/emacs--cvs-trunk--0--patch-464
* miles@gnu.org--gnu-2004/emacs--gnus--5.10--patch-1
Import from CVS branch gnus-5_10-branch
* miles@gnu.org--gnu-2004/emacs--gnus--5.10--patch-2
Merge from lorentey@elte.hu--2004/emacs--multi-tty--0, emacs--cvs-trunk--0
* miles@gnu.org--gnu-2004/emacs--gnus--5.10--patch-3
Merge from gnus--rel--5.10
* miles@gnu.org--gnu-2004/emacs--gnus--5.10--patch-4
Merge from gnus--rel--5.10
* miles@gnu.org--gnu-2004/gnus--rel--5.10--patch-18
Update from CVS
* miles@gnu.org--gnu-2004/gnus--rel--5.10--patch-19
Remove autoconf-generated files from archive
* miles@gnu.org--gnu-2004/gnus--rel--5.10--patch-20
Update from CVS
author | Miles Bader <miles@gnu.org> |
---|---|
date | Sat, 04 Sep 2004 13:13:48 +0000 |
parents | 695cf19ef79e |
children | 497f0d2ca551 cce1c0ee76ee |
comparison
equal
deleted
inserted
replaced
56926:f8e248e9a717 | 56927:55fd4f77387a |
---|---|
1 ;;; gnus-util.el --- utility functions for Gnus | 1 ;;; gnus-util.el --- utility functions for Gnus |
2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002 | 2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 |
3 ;; Free Software Foundation, Inc. | 3 ;; Free Software Foundation, Inc. |
4 | 4 |
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> | 5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> |
6 ;; Keywords: news | 6 ;; Keywords: news |
7 | 7 |
26 | 26 |
27 ;; Nothing in this file depends on any other parts of Gnus -- all | 27 ;; Nothing in this file depends on any other parts of Gnus -- all |
28 ;; functions and macros in this file are utility functions that are | 28 ;; functions and macros in this file are utility functions that are |
29 ;; used by Gnus and may be used by any other package without loading | 29 ;; used by Gnus and may be used by any other package without loading |
30 ;; Gnus first. | 30 ;; Gnus first. |
31 | |
32 ;; [Unfortunately, it does depend on other parts of Gnus, e.g. the | |
33 ;; autoloads below...] | |
31 | 34 |
32 ;;; Code: | 35 ;;; Code: |
33 | 36 |
34 (require 'custom) | 37 (require 'custom) |
35 (eval-when-compile | 38 (eval-when-compile |
36 (require 'cl) | 39 (require 'cl) |
37 ;; Fixme: this should be a gnus variable, not nnmail-. | 40 ;; Fixme: this should be a gnus variable, not nnmail-. |
38 (defvar nnmail-pathname-coding-system)) | 41 (defvar nnmail-pathname-coding-system)) |
39 (require 'nnheader) | |
40 (require 'time-date) | 42 (require 'time-date) |
43 (require 'netrc) | |
41 | 44 |
42 (eval-and-compile | 45 (eval-and-compile |
43 (autoload 'message-fetch-field "message") | 46 (autoload 'message-fetch-field "message") |
47 (autoload 'gnus-get-buffer-window "gnus-win") | |
44 (autoload 'rmail-insert-rmail-file-header "rmail") | 48 (autoload 'rmail-insert-rmail-file-header "rmail") |
45 (autoload 'rmail-count-new-messages "rmail") | 49 (autoload 'rmail-count-new-messages "rmail") |
46 (autoload 'rmail-show-message "rmail")) | 50 (autoload 'rmail-show-message "rmail") |
51 (autoload 'nnheader-narrow-to-headers "nnheader") | |
52 (autoload 'nnheader-replace-chars-in-string "nnheader")) | |
53 | |
54 (eval-and-compile | |
55 (cond | |
56 ((fboundp 'replace-in-string) | |
57 (defalias 'gnus-replace-in-string 'replace-in-string)) | |
58 ((fboundp 'replace-regexp-in-string) | |
59 (defun gnus-replace-in-string (string regexp newtext &optional literal) | |
60 (replace-regexp-in-string regexp newtext string nil literal))) | |
61 (t | |
62 (defun gnus-replace-in-string (string regexp newtext &optional literal) | |
63 (let ((start 0) tail) | |
64 (while (string-match regexp string start) | |
65 (setq tail (- (length string) (match-end 0))) | |
66 (setq string (replace-match newtext nil literal string)) | |
67 (setq start (- (length string) tail)))) | |
68 string)))) | |
69 | |
70 ;;; bring in the netrc functions as aliases | |
71 (defalias 'gnus-netrc-get 'netrc-get) | |
72 (defalias 'gnus-netrc-machine 'netrc-machine) | |
73 (defalias 'gnus-parse-netrc 'netrc-parse) | |
47 | 74 |
48 (defun gnus-boundp (variable) | 75 (defun gnus-boundp (variable) |
49 "Return non-nil if VARIABLE is bound and non-nil." | 76 "Return non-nil if VARIABLE is bound and non-nil." |
50 (and (boundp variable) | 77 (and (boundp variable) |
51 (symbol-value variable))) | 78 (symbol-value variable))) |
52 | 79 |
53 (defmacro gnus-eval-in-buffer-window (buffer &rest forms) | 80 (defmacro gnus-eval-in-buffer-window (buffer &rest forms) |
54 "Pop to BUFFER, evaluate FORMS, and then return to the original window." | 81 "Pop to BUFFER, evaluate FORMS, and then return to the original window." |
55 (let ((tempvar (make-symbol "GnusStartBufferWindow")) | 82 (let ((tempvar (make-symbol "GnusStartBufferWindow")) |
56 (w (make-symbol "w")) | 83 (w (make-symbol "w")) |
57 (buf (make-symbol "buf"))) | 84 (buf (make-symbol "buf"))) |
58 `(let* ((,tempvar (selected-window)) | 85 `(let* ((,tempvar (selected-window)) |
59 (,buf ,buffer) | 86 (,buf ,buffer) |
60 (,w (get-buffer-window ,buf 'visible))) | 87 (,w (gnus-get-buffer-window ,buf 'visible))) |
61 (unwind-protect | 88 (unwind-protect |
62 (progn | 89 (progn |
63 (if ,w | 90 (if ,w |
64 (progn | 91 (progn |
65 (select-window ,w) | 92 (select-window ,w) |
66 (set-buffer (window-buffer ,w))) | 93 (set-buffer (window-buffer ,w))) |
67 (pop-to-buffer ,buf)) | 94 (pop-to-buffer ,buf)) |
68 ,@forms) | 95 ,@forms) |
69 (select-window ,tempvar))))) | 96 (select-window ,tempvar))))) |
70 | 97 |
71 (put 'gnus-eval-in-buffer-window 'lisp-indent-function 1) | 98 (put 'gnus-eval-in-buffer-window 'lisp-indent-function 1) |
72 (put 'gnus-eval-in-buffer-window 'edebug-form-spec '(form body)) | 99 (put 'gnus-eval-in-buffer-window 'edebug-form-spec '(form body)) |
73 | 100 |
74 (defmacro gnus-intern-safe (string hashtable) | 101 (defmacro gnus-intern-safe (string hashtable) |
79 symbol)) | 106 symbol)) |
80 | 107 |
81 ;; Added by Geoffrey T. Dairiki <dairiki@u.washington.edu>. A safe way | 108 ;; Added by Geoffrey T. Dairiki <dairiki@u.washington.edu>. A safe way |
82 ;; to limit the length of a string. This function is necessary since | 109 ;; to limit the length of a string. This function is necessary since |
83 ;; `(substr "abc" 0 30)' pukes with "Args out of range". | 110 ;; `(substr "abc" 0 30)' pukes with "Args out of range". |
111 ;; Fixme: Why not `truncate-string-to-width'? | |
84 (defsubst gnus-limit-string (str width) | 112 (defsubst gnus-limit-string (str width) |
85 (if (> (length str) width) | 113 (if (> (length str) width) |
86 (substring str 0 width) | 114 (substring str 0 width) |
87 str)) | 115 str)) |
88 | |
89 (defsubst gnus-functionp (form) | |
90 "Return non-nil if FORM is funcallable." | |
91 (or (and (symbolp form) (fboundp form)) | |
92 (and (listp form) (eq (car form) 'lambda)) | |
93 (byte-code-function-p form))) | |
94 | 116 |
95 (defsubst gnus-goto-char (point) | 117 (defsubst gnus-goto-char (point) |
96 (and point (goto-char point))) | 118 (and point (goto-char point))) |
97 | 119 |
98 (defmacro gnus-buffer-exists-p (buffer) | 120 (defmacro gnus-buffer-exists-p (buffer) |
99 `(let ((buffer ,buffer)) | 121 `(let ((buffer ,buffer)) |
100 (when buffer | 122 (when buffer |
101 (funcall (if (stringp buffer) 'get-buffer 'buffer-name) | 123 (funcall (if (stringp buffer) 'get-buffer 'buffer-name) |
102 buffer)))) | 124 buffer)))) |
103 | 125 |
104 (defmacro gnus-kill-buffer (buffer) | |
105 `(let ((buf ,buffer)) | |
106 (when (gnus-buffer-exists-p buf) | |
107 (kill-buffer buf)))) | |
108 | |
109 (defalias 'gnus-point-at-bol | 126 (defalias 'gnus-point-at-bol |
110 (if (fboundp 'point-at-bol) | 127 (if (fboundp 'point-at-bol) |
111 'point-at-bol | 128 'point-at-bol |
112 'line-beginning-position)) | 129 'line-beginning-position)) |
113 | 130 |
114 (defalias 'gnus-point-at-eol | 131 (defalias 'gnus-point-at-eol |
115 (if (fboundp 'point-at-eol) | 132 (if (fboundp 'point-at-eol) |
116 'point-at-eol | 133 'point-at-eol |
117 'line-end-position)) | 134 'line-end-position)) |
135 | |
136 ;; The LOCAL arg to `add-hook' is interpreted differently in Emacs and | |
137 ;; XEmacs. In Emacs we don't need to call `make-local-hook' first. | |
138 ;; It's harmless, though, so the main purpose of this alias is to shut | |
139 ;; up the byte compiler. | |
140 (defalias 'gnus-make-local-hook | |
141 (if (eq (get 'make-local-hook 'byte-compile) | |
142 'byte-compile-obsolete) | |
143 'ignore ; Emacs | |
144 'make-local-hook)) ; XEmacs | |
118 | 145 |
119 (defun gnus-delete-first (elt list) | 146 (defun gnus-delete-first (elt list) |
120 "Delete by side effect the first occurrence of ELT as a member of LIST." | 147 "Delete by side effect the first occurrence of ELT as a member of LIST." |
121 (if (equal (car list) elt) | 148 (if (equal (car list) elt) |
122 (cdr list) | 149 (cdr list) |
128 (setcdr list (cddr list))) | 155 (setcdr list (cddr list))) |
129 total))) | 156 total))) |
130 | 157 |
131 ;; Delete the current line (and the next N lines). | 158 ;; Delete the current line (and the next N lines). |
132 (defmacro gnus-delete-line (&optional n) | 159 (defmacro gnus-delete-line (&optional n) |
133 `(delete-region (progn (beginning-of-line) (point)) | 160 `(delete-region (gnus-point-at-bol) |
134 (progn (forward-line ,(or n 1)) (point)))) | 161 (progn (forward-line ,(or n 1)) (point)))) |
135 | 162 |
136 (defun gnus-byte-code (func) | 163 (defun gnus-byte-code (func) |
137 "Return a form that can be `eval'ed based on FUNC." | 164 "Return a form that can be `eval'ed based on FUNC." |
138 (let ((fval (indirect-function func))) | 165 (let ((fval (indirect-function func))) |
141 (setcar flist 'byte-code) | 168 (setcar flist 'byte-code) |
142 flist) | 169 flist) |
143 (cons 'progn (cddr fval))))) | 170 (cons 'progn (cddr fval))))) |
144 | 171 |
145 (defun gnus-extract-address-components (from) | 172 (defun gnus-extract-address-components (from) |
173 "Extract address components from a From header. | |
174 Given an RFC-822 address FROM, extract full name and canonical address. | |
175 Returns a list of the form (FULL-NAME CANONICAL-ADDRESS). Much more simple | |
176 solution than `mail-extract-address-components', which works much better, but | |
177 is slower." | |
146 (let (name address) | 178 (let (name address) |
147 ;; First find the address - the thing with the @ in it. This may | 179 ;; First find the address - the thing with the @ in it. This may |
148 ;; not be accurate in mail addresses, but does the trick most of | 180 ;; not be accurate in mail addresses, but does the trick most of |
149 ;; the time in news messages. | 181 ;; the time in news messages. |
150 (when (string-match "\\b[^@ \t<>]+[!@][^@ \t<>]+\\b" from) | 182 (when (string-match "\\b[^@ \t<>]+[!@][^@ \t<>]+\\b" from) |
153 (and address | 185 (and address |
154 ;; Linear white space is not required. | 186 ;; Linear white space is not required. |
155 (string-match (concat "[ \t]*<" (regexp-quote address) ">") from) | 187 (string-match (concat "[ \t]*<" (regexp-quote address) ">") from) |
156 (and (setq name (substring from 0 (match-beginning 0))) | 188 (and (setq name (substring from 0 (match-beginning 0))) |
157 ;; Strip any quotes from the name. | 189 ;; Strip any quotes from the name. |
158 (string-match "\".*\"" name) | 190 (string-match "^\".*\"$" name) |
159 (setq name (substring name 1 (1- (match-end 0)))))) | 191 (setq name (substring name 1 (1- (match-end 0)))))) |
160 ;; If not, then "address (name)" is used. | 192 ;; If not, then "address (name)" is used. |
161 (or name | 193 (or name |
162 (and (string-match "(.+)" from) | 194 (and (string-match "(.+)" from) |
163 (setq name (substring from (1+ (match-beginning 0)) | 195 (setq name (substring from (1+ (match-beginning 0)) |
178 (let ((case-fold-search t) | 210 (let ((case-fold-search t) |
179 (inhibit-point-motion-hooks t)) | 211 (inhibit-point-motion-hooks t)) |
180 (nnheader-narrow-to-headers) | 212 (nnheader-narrow-to-headers) |
181 (message-fetch-field field))))) | 213 (message-fetch-field field))))) |
182 | 214 |
215 (defun gnus-fetch-original-field (field) | |
216 "Fetch FIELD from the original version of the current article." | |
217 (with-current-buffer gnus-original-article-buffer | |
218 (gnus-fetch-field field))) | |
219 | |
220 | |
183 (defun gnus-goto-colon () | 221 (defun gnus-goto-colon () |
184 (beginning-of-line) | 222 (beginning-of-line) |
185 (search-forward ":" (gnus-point-at-eol) t)) | 223 (let ((eol (gnus-point-at-eol))) |
224 (goto-char (or (text-property-any (point) eol 'gnus-position t) | |
225 (search-forward ":" eol t) | |
226 (point))))) | |
227 | |
228 (defun gnus-decode-newsgroups (newsgroups group &optional method) | |
229 (let ((method (or method (gnus-find-method-for-group group)))) | |
230 (mapconcat (lambda (group) | |
231 (gnus-group-name-decode group (gnus-group-name-charset | |
232 method group))) | |
233 (message-tokenize-header newsgroups) | |
234 ","))) | |
186 | 235 |
187 (defun gnus-remove-text-with-property (prop) | 236 (defun gnus-remove-text-with-property (prop) |
188 "Delete all text in the current buffer with text property PROP." | 237 "Delete all text in the current buffer with text property PROP." |
189 (save-excursion | 238 (save-excursion |
190 (goto-char (point-min)) | 239 (goto-char (point-min)) |
193 (delete-char 1)) | 242 (delete-char 1)) |
194 (goto-char (next-single-property-change (point) prop nil (point-max)))))) | 243 (goto-char (next-single-property-change (point) prop nil (point-max)))))) |
195 | 244 |
196 (defun gnus-newsgroup-directory-form (newsgroup) | 245 (defun gnus-newsgroup-directory-form (newsgroup) |
197 "Make hierarchical directory name from NEWSGROUP name." | 246 "Make hierarchical directory name from NEWSGROUP name." |
198 (let ((newsgroup (gnus-newsgroup-savable-name newsgroup)) | 247 (let* ((newsgroup (gnus-newsgroup-savable-name newsgroup)) |
199 (len (length newsgroup)) | 248 (idx (string-match ":" newsgroup))) |
200 idx) | 249 (concat |
201 ;; If this is a foreign group, we don't want to translate the | 250 (if idx (substring newsgroup 0 idx)) |
202 ;; entire name. | 251 (if idx "/") |
203 (if (setq idx (string-match ":" newsgroup)) | 252 (nnheader-replace-chars-in-string |
204 (aset newsgroup idx ?/) | 253 (if idx (substring newsgroup (1+ idx)) newsgroup) |
205 (setq idx 0)) | 254 ?. ?/)))) |
206 ;; Replace all occurrences of `.' with `/'. | |
207 (while (< idx len) | |
208 (when (= (aref newsgroup idx) ?.) | |
209 (aset newsgroup idx ?/)) | |
210 (setq idx (1+ idx))) | |
211 newsgroup)) | |
212 | 255 |
213 (defun gnus-newsgroup-savable-name (group) | 256 (defun gnus-newsgroup-savable-name (group) |
214 ;; Replace any slashes in a group name (eg. an ange-ftp nndoc group) | 257 ;; Replace any slashes in a group name (eg. an ange-ftp nndoc group) |
215 ;; with dots. | 258 ;; with dots. |
216 (nnheader-replace-chars-in-string group ?/ ?.)) | 259 (nnheader-replace-chars-in-string group ?/ ?.)) |
269 (if (or (not safe) | 312 (if (or (not safe) |
270 (eq (lookup-key keymap key) 'undefined)) | 313 (eq (lookup-key keymap key) 'undefined)) |
271 (define-key keymap key (pop plist)) | 314 (define-key keymap key (pop plist)) |
272 (pop plist))))) | 315 (pop plist))))) |
273 | 316 |
274 (defun gnus-completing-read (default prompt &rest args) | 317 (defun gnus-completing-read-with-default (default prompt &rest args) |
275 ;; Like `completing-read', except that DEFAULT is the default argument. | 318 ;; Like `completing-read', except that DEFAULT is the default argument. |
276 (let* ((prompt (if default | 319 (let* ((prompt (if default |
277 (concat prompt " (default " default ") ") | 320 (concat prompt " (default " default ") ") |
278 (concat prompt " "))) | 321 (concat prompt " "))) |
279 (answer (apply 'completing-read prompt args))) | 322 (answer (apply 'completing-read prompt args))) |
290 | 333 |
291 (defun gnus-yes-or-no-p (prompt) | 334 (defun gnus-yes-or-no-p (prompt) |
292 (prog1 | 335 (prog1 |
293 (yes-or-no-p prompt) | 336 (yes-or-no-p prompt) |
294 (message ""))) | 337 (message ""))) |
338 | |
339 ;; By Frank Schmitt <ich@Frank-Schmitt.net>. Allows to have | |
340 ;; age-depending date representations. (e.g. just the time if it's | |
341 ;; from today, the day of the week if it's within the last 7 days and | |
342 ;; the full date if it's older) | |
343 | |
344 (defun gnus-seconds-today () | |
345 "Return the number of seconds passed today." | |
346 (let ((now (decode-time (current-time)))) | |
347 (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)))) | |
348 | |
349 (defun gnus-seconds-month () | |
350 "Return the number of seconds passed this month." | |
351 (let ((now (decode-time (current-time)))) | |
352 (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) | |
353 (* (- (car (nthcdr 3 now)) 1) 3600 24)))) | |
354 | |
355 (defun gnus-seconds-year () | |
356 "Return the number of seconds passed this year." | |
357 (let ((now (decode-time (current-time))) | |
358 (days (format-time-string "%j" (current-time)))) | |
359 (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) | |
360 (* (- (string-to-number days) 1) 3600 24)))) | |
361 | |
362 (defvar gnus-user-date-format-alist | |
363 '(((gnus-seconds-today) . "%k:%M") | |
364 (604800 . "%a %k:%M") ;;that's one week | |
365 ((gnus-seconds-month) . "%a %d") | |
366 ((gnus-seconds-year) . "%b %d") | |
367 (t . "%b %d '%y")) ;;this one is used when no | |
368 ;;other does match | |
369 "Specifies date format depending on age of article. | |
370 This is an alist of items (AGE . FORMAT). AGE can be a number (of | |
371 seconds) or a Lisp expression evaluating to a number. When the age of | |
372 the article is less than this number, then use `format-time-string' | |
373 with the corresponding FORMAT for displaying the date of the article. | |
374 If AGE is not a number or a Lisp expression evaluating to a | |
375 non-number, then the corresponding FORMAT is used as a default value. | |
376 | |
377 Note that the list is processed from the beginning, so it should be | |
378 sorted by ascending AGE. Also note that items following the first | |
379 non-number AGE will be ignored. | |
380 | |
381 You can use the functions `gnus-seconds-today', `gnus-seconds-month' | |
382 and `gnus-seconds-year' in the AGE spec. They return the number of | |
383 seconds passed since the start of today, of this month, of this year, | |
384 respectively.") | |
385 | |
386 (defun gnus-user-date (messy-date) | |
387 "Format the messy-date according to gnus-user-date-format-alist. | |
388 Returns \" ? \" if there's bad input or if an other error occurs. | |
389 Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"." | |
390 (condition-case () | |
391 (let* ((messy-date (time-to-seconds (safe-date-to-time messy-date))) | |
392 (now (time-to-seconds (current-time))) | |
393 ;;If we don't find something suitable we'll use this one | |
394 (my-format "%b %d '%y")) | |
395 (let* ((difference (- now messy-date)) | |
396 (templist gnus-user-date-format-alist) | |
397 (top (eval (caar templist)))) | |
398 (while (if (numberp top) (< top difference) (not top)) | |
399 (progn | |
400 (setq templist (cdr templist)) | |
401 (setq top (eval (caar templist))))) | |
402 (if (stringp (cdr (car templist))) | |
403 (setq my-format (cdr (car templist))))) | |
404 (format-time-string (eval my-format) (seconds-to-time messy-date))) | |
405 (error " ? "))) | |
295 | 406 |
296 (defun gnus-dd-mmm (messy-date) | 407 (defun gnus-dd-mmm (messy-date) |
297 "Return a string like DD-MMM from a big messy string." | 408 "Return a string like DD-MMM from a big messy string." |
298 (condition-case () | 409 (condition-case () |
299 (format-time-string "%d-%b" (safe-date-to-time messy-date)) | 410 (format-time-string "%d-%b" (safe-date-to-time messy-date)) |
323 (gnus-time-iso8601 (gnus-date-get-time date)) | 434 (gnus-time-iso8601 (gnus-date-get-time date)) |
324 (error ""))) | 435 (error ""))) |
325 | 436 |
326 (defun gnus-mode-string-quote (string) | 437 (defun gnus-mode-string-quote (string) |
327 "Quote all \"%\"'s in STRING." | 438 "Quote all \"%\"'s in STRING." |
328 (save-excursion | 439 (gnus-replace-in-string string "%" "%%")) |
329 (gnus-set-work-buffer) | |
330 (insert string) | |
331 (goto-char (point-min)) | |
332 (while (search-forward "%" nil t) | |
333 (insert "%")) | |
334 (buffer-string))) | |
335 | 440 |
336 ;; Make a hash table (default and minimum size is 256). | 441 ;; Make a hash table (default and minimum size is 256). |
337 ;; Optional argument HASHSIZE specifies the table size. | 442 ;; Optional argument HASHSIZE specifies the table size. |
338 (defun gnus-make-hashtable (&optional hashsize) | 443 (defun gnus-make-hashtable (&optional hashsize) |
339 (make-vector (if hashsize (max (gnus-create-hash-size hashsize) 256) 256) 0)) | 444 (make-vector (if hashsize (max (gnus-create-hash-size hashsize) 256) 256) 0)) |
357 display most important messages; and at ten, Gnus will keep on | 462 display most important messages; and at ten, Gnus will keep on |
358 jabbering all the time." | 463 jabbering all the time." |
359 :group 'gnus-start | 464 :group 'gnus-start |
360 :type 'integer) | 465 :type 'integer) |
361 | 466 |
362 ;; Show message if message has a lower level than `gnus-verbose'. | |
363 ;; Guideline for numbers: | |
364 ;; 1 - error messages, 3 - non-serious error messages, 5 - messages | |
365 ;; for things that take a long time, 7 - not very important messages | |
366 ;; on stuff, 9 - messages inside loops. | |
367 (defun gnus-message (level &rest args) | 467 (defun gnus-message (level &rest args) |
468 "If LEVEL is lower than `gnus-verbose' print ARGS using `message'. | |
469 | |
470 Guideline for numbers: | |
471 1 - error messages, 3 - non-serious error messages, 5 - messages for things | |
472 that take a long time, 7 - not very important messages on stuff, 9 - messages | |
473 inside loops." | |
368 (if (<= level gnus-verbose) | 474 (if (<= level gnus-verbose) |
369 (apply 'message args) | 475 (apply 'message args) |
370 ;; We have to do this format thingy here even if the result isn't | 476 ;; We have to do this format thingy here even if the result isn't |
371 ;; shown - the return value has to be the same as the return value | 477 ;; shown - the return value has to be the same as the return value |
372 ;; from `message'. | 478 ;; from `message'. |
385 | 491 |
386 (defun gnus-split-references (references) | 492 (defun gnus-split-references (references) |
387 "Return a list of Message-IDs in REFERENCES." | 493 "Return a list of Message-IDs in REFERENCES." |
388 (let ((beg 0) | 494 (let ((beg 0) |
389 ids) | 495 ids) |
390 (while (string-match "<[^>]+>" references beg) | 496 (while (string-match "<[^<]+[^< \t]" references beg) |
391 (push (substring references (match-beginning 0) (setq beg (match-end 0))) | 497 (push (substring references (match-beginning 0) (setq beg (match-end 0))) |
392 ids)) | 498 ids)) |
393 (nreverse ids))) | 499 (nreverse ids))) |
394 | 500 |
395 (defsubst gnus-parent-id (references &optional n) | 501 (defsubst gnus-parent-id (references &optional n) |
396 "Return the last Message-ID in REFERENCES. | 502 "Return the last Message-ID in REFERENCES. |
397 If N, return the Nth ancestor instead." | 503 If N, return the Nth ancestor instead." |
398 (when references | 504 (when (and references |
399 (let ((ids (inline (gnus-split-references references)))) | 505 (not (zerop (length references)))) |
400 (while (nthcdr (or n 1) ids) | 506 (if n |
401 (setq ids (cdr ids))) | 507 (let ((ids (inline (gnus-split-references references)))) |
402 (car ids)))) | 508 (while (nthcdr n ids) |
403 | 509 (setq ids (cdr ids))) |
404 (defsubst gnus-buffer-live-p (buffer) | 510 (car ids)) |
511 (when (string-match "\\(<[^<]+>\\)[ \t]*\\'" references) | |
512 (match-string 1 references))))) | |
513 | |
514 (defun gnus-buffer-live-p (buffer) | |
405 "Say whether BUFFER is alive or not." | 515 "Say whether BUFFER is alive or not." |
406 (and buffer | 516 (and buffer |
407 (get-buffer buffer) | 517 (get-buffer buffer) |
408 (buffer-name (get-buffer buffer)))) | 518 (buffer-name (get-buffer buffer)))) |
409 | 519 |
410 (defun gnus-horizontal-recenter () | 520 (defun gnus-horizontal-recenter () |
411 "Recenter the current buffer horizontally." | 521 "Recenter the current buffer horizontally." |
412 (if (< (current-column) (/ (window-width) 2)) | 522 (if (< (current-column) (/ (window-width) 2)) |
413 (set-window-hscroll (get-buffer-window (current-buffer) t) 0) | 523 (set-window-hscroll (gnus-get-buffer-window (current-buffer) t) 0) |
414 (let* ((orig (point)) | 524 (let* ((orig (point)) |
415 (end (window-end (get-buffer-window (current-buffer) t))) | 525 (end (window-end (gnus-get-buffer-window (current-buffer) t))) |
416 (max 0)) | 526 (max 0)) |
417 (when end | 527 (when end |
418 ;; Find the longest line currently displayed in the window. | 528 ;; Find the longest line currently displayed in the window. |
419 (goto-char (window-start)) | 529 (goto-char (window-start)) |
420 (while (and (not (eobp)) | 530 (while (and (not (eobp)) |
424 (forward-line 1)) | 534 (forward-line 1)) |
425 (goto-char orig) | 535 (goto-char orig) |
426 ;; Scroll horizontally to center (sort of) the point. | 536 ;; Scroll horizontally to center (sort of) the point. |
427 (if (> max (window-width)) | 537 (if (> max (window-width)) |
428 (set-window-hscroll | 538 (set-window-hscroll |
429 (get-buffer-window (current-buffer) t) | 539 (gnus-get-buffer-window (current-buffer) t) |
430 (min (- (current-column) (/ (window-width) 3)) | 540 (min (- (current-column) (/ (window-width) 3)) |
431 (+ 2 (- max (window-width))))) | 541 (+ 2 (- max (window-width))))) |
432 (set-window-hscroll (get-buffer-window (current-buffer) t) 0)) | 542 (set-window-hscroll (gnus-get-buffer-window (current-buffer) t) 0)) |
433 max)))) | 543 max)))) |
434 | 544 |
435 (defun gnus-read-event-char () | 545 (defun gnus-read-event-char (&optional prompt) |
436 "Get the next event." | 546 "Get the next event." |
437 (let ((event (read-event))) | 547 (let ((event (read-event prompt))) |
438 ;; should be gnus-characterp, but this can't be called in XEmacs anyway | 548 ;; should be gnus-characterp, but this can't be called in XEmacs anyway |
439 (cons (and (numberp event) event) event))) | 549 (cons (and (numberp event) event) event))) |
440 | 550 |
441 (defun gnus-sortable-date (date) | 551 (defun gnus-sortable-date (date) |
442 "Make string suitable for sorting from DATE." | 552 "Make string suitable for sorting from DATE." |
472 (if (string-match "^[^:]+:" gname) | 582 (if (string-match "^[^:]+:" gname) |
473 (substring gname (match-end 0)) | 583 (substring gname (match-end 0)) |
474 gname))) | 584 gname))) |
475 | 585 |
476 (defun gnus-make-sort-function (funs) | 586 (defun gnus-make-sort-function (funs) |
477 "Return a composite sort condition based on the functions in FUNC." | 587 "Return a composite sort condition based on the functions in FUNS." |
478 (cond | 588 (cond |
479 ;; Just a simple function. | 589 ;; Just a simple function. |
480 ((gnus-functionp funs) funs) | 590 ((functionp funs) funs) |
481 ;; No functions at all. | 591 ;; No functions at all. |
482 ((null funs) funs) | 592 ((null funs) funs) |
483 ;; A list of functions. | 593 ;; A list of functions. |
484 ((or (cdr funs) | 594 ((or (cdr funs) |
485 (listp (car funs))) | 595 (listp (car funs))) |
486 `(lambda (t1 t2) | 596 (gnus-byte-compile |
487 ,(gnus-make-sort-function-1 (reverse funs)))) | 597 `(lambda (t1 t2) |
598 ,(gnus-make-sort-function-1 (reverse funs))))) | |
488 ;; A list containing just one function. | 599 ;; A list containing just one function. |
489 (t | 600 (t |
490 (car funs)))) | 601 (car funs)))) |
491 | 602 |
492 (defun gnus-make-sort-function-1 (funs) | 603 (defun gnus-make-sort-function-1 (funs) |
493 "Return a composite sort condition based on the functions in FUNC." | 604 "Return a composite sort condition based on the functions in FUNS." |
494 (let ((function (car funs)) | 605 (let ((function (car funs)) |
495 (first 't1) | 606 (first 't1) |
496 (last 't2)) | 607 (last 't2)) |
497 (when (consp function) | 608 (when (consp function) |
498 (cond | 609 (cond |
499 ;; Reversed spec. | 610 ;; Reversed spec. |
500 ((eq (car function) 'not) | 611 ((eq (car function) 'not) |
501 (setq function (cadr function) | 612 (setq function (cadr function) |
502 first 't2 | 613 first 't2 |
503 last 't1)) | 614 last 't1)) |
504 ((gnus-functionp function) | 615 ((functionp function) |
505 ;; Do nothing. | 616 ;; Do nothing. |
506 ) | 617 ) |
507 (t | 618 (t |
508 (error "Invalid sort spec: %s" function)))) | 619 (error "Invalid sort spec: %s" function)))) |
509 (if (cdr funs) | 620 (if (cdr funs) |
515 (defun gnus-turn-off-edit-menu (type) | 626 (defun gnus-turn-off-edit-menu (type) |
516 "Turn off edit menu in `gnus-TYPE-mode-map'." | 627 "Turn off edit menu in `gnus-TYPE-mode-map'." |
517 (define-key (symbol-value (intern (format "gnus-%s-mode-map" type))) | 628 (define-key (symbol-value (intern (format "gnus-%s-mode-map" type))) |
518 [menu-bar edit] 'undefined)) | 629 [menu-bar edit] 'undefined)) |
519 | 630 |
631 (defmacro gnus-bind-print-variables (&rest forms) | |
632 "Bind print-* variables and evaluate FORMS. | |
633 This macro is used with `prin1', `pp', etc. in order to ensure printed | |
634 Lisp objects are loadable. Bind `print-quoted' and `print-readably' | |
635 to t, and `print-escape-multibyte', `print-escape-newlines', | |
636 `print-escape-nonascii', `print-length', `print-level' and | |
637 `print-string-length' to nil." | |
638 `(let ((print-quoted t) | |
639 (print-readably t) | |
640 ;;print-circle | |
641 ;;print-continuous-numbering | |
642 print-escape-multibyte | |
643 print-escape-newlines | |
644 print-escape-nonascii | |
645 ;;print-gensym | |
646 print-length | |
647 print-level | |
648 print-string-length) | |
649 ,@forms)) | |
650 | |
520 (defun gnus-prin1 (form) | 651 (defun gnus-prin1 (form) |
521 "Use `prin1' on FORM in the current buffer. | 652 "Use `prin1' on FORM in the current buffer. |
522 Bind `print-quoted' and `print-readably' to t while printing." | 653 Bind `print-quoted' and `print-readably' to t, and `print-length' and |
523 (let ((print-quoted t) | 654 `print-level' to nil. See also `gnus-bind-print-variables'." |
524 (print-readably t) | 655 (gnus-bind-print-variables (prin1 form (current-buffer)))) |
525 (print-escape-multibyte nil) | |
526 print-level print-length) | |
527 (prin1 form (current-buffer)))) | |
528 | 656 |
529 (defun gnus-prin1-to-string (form) | 657 (defun gnus-prin1-to-string (form) |
530 "The same as `prin1', but bind `print-quoted' and `print-readably' to t." | 658 "The same as `prin1'. |
531 (let ((print-quoted t) | 659 Bind `print-quoted' and `print-readably' to t, and `print-length' and |
532 (print-readably t)) | 660 `print-level' to nil. See also `gnus-bind-print-variables'." |
533 (prin1-to-string form))) | 661 (gnus-bind-print-variables (prin1-to-string form))) |
662 | |
663 (defun gnus-pp (form) | |
664 "Use `pp' on FORM in the current buffer. | |
665 Bind `print-quoted' and `print-readably' to t, and `print-length' and | |
666 `print-level' to nil. See also `gnus-bind-print-variables'." | |
667 (gnus-bind-print-variables (pp form (current-buffer)))) | |
668 | |
669 (defun gnus-pp-to-string (form) | |
670 "The same as `pp-to-string'. | |
671 Bind `print-quoted' and `print-readably' to t, and `print-length' and | |
672 `print-level' to nil. See also `gnus-bind-print-variables'." | |
673 (gnus-bind-print-variables (pp-to-string form))) | |
534 | 674 |
535 (defun gnus-make-directory (directory) | 675 (defun gnus-make-directory (directory) |
536 "Make DIRECTORY (and all its parents) if it doesn't exist." | 676 "Make DIRECTORY (and all its parents) if it doesn't exist." |
537 (require 'nnmail) | 677 (require 'nnmail) |
538 (let ((file-name-coding-system nnmail-pathname-coding-system)) | 678 (let ((file-name-coding-system nnmail-pathname-coding-system)) |
569 (while (re-search-forward gnus-emphasize-whitespace-regexp end 'move) | 709 (while (re-search-forward gnus-emphasize-whitespace-regexp end 'move) |
570 (gnus-put-text-property beg (match-beginning 0) prop val) | 710 (gnus-put-text-property beg (match-beginning 0) prop val) |
571 (setq beg (point))) | 711 (setq beg (point))) |
572 (gnus-put-text-property beg (point) prop val))))) | 712 (gnus-put-text-property beg (point) prop val))))) |
573 | 713 |
714 (defsubst gnus-put-overlay-excluding-newlines (beg end prop val) | |
715 "The same as `put-text-property', but don't put this prop on any newlines in the region." | |
716 (save-match-data | |
717 (save-excursion | |
718 (save-restriction | |
719 (goto-char beg) | |
720 (while (re-search-forward gnus-emphasize-whitespace-regexp end 'move) | |
721 (gnus-overlay-put | |
722 (gnus-make-overlay beg (match-beginning 0)) | |
723 prop val) | |
724 (setq beg (point))) | |
725 (gnus-overlay-put (gnus-make-overlay beg (point)) prop val))))) | |
726 | |
574 (defun gnus-put-text-property-excluding-characters-with-faces (beg end | 727 (defun gnus-put-text-property-excluding-characters-with-faces (beg end |
575 prop val) | 728 prop val) |
576 "The same as `put-text-property', but don't put props on characters with the `gnus-face' property." | 729 "The same as `put-text-property', but don't put props on characters with the `gnus-face' property." |
577 (let ((b beg)) | 730 (let ((b beg)) |
578 (while (/= b end) | 731 (while (/= b end) |
579 (when (get-text-property b 'gnus-face) | 732 (when (get-text-property b 'gnus-face) |
580 (setq b (next-single-property-change b 'gnus-face nil end))) | 733 (setq b (next-single-property-change b 'gnus-face nil end))) |
581 (when (/= b end) | 734 (when (/= b end) |
582 (gnus-put-text-property | 735 (inline |
583 b (setq b (next-single-property-change b 'gnus-face nil end)) | 736 (gnus-put-text-property |
584 prop val))))) | 737 b (setq b (next-single-property-change b 'gnus-face nil end)) |
738 prop val)))))) | |
739 | |
740 (defmacro gnus-faces-at (position) | |
741 "Return a list of faces at POSITION." | |
742 (if (featurep 'xemacs) | |
743 `(let ((pos ,position)) | |
744 (mapcar-extents 'extent-face | |
745 nil (current-buffer) pos pos nil 'face)) | |
746 `(let ((pos ,position)) | |
747 (delq nil (cons (get-text-property pos 'face) | |
748 (mapcar | |
749 (lambda (overlay) | |
750 (overlay-get overlay 'face)) | |
751 (overlays-at pos))))))) | |
585 | 752 |
586 ;;; Protected and atomic operations. dmoore@ucsd.edu 21.11.1996 | 753 ;;; Protected and atomic operations. dmoore@ucsd.edu 21.11.1996 |
587 ;;; The primary idea here is to try to protect internal datastructures | 754 ;;; The primary idea here is to try to protect internal datastructures |
588 ;;; from becoming corrupted when the user hits C-g, or if a hook or | 755 ;;; from becoming corrupted when the user hits C-g, or if a hook or |
589 ;;; similar blows up. Often in Gnus multiple tables/lists need to be | 756 ;;; similar blows up. Often in Gnus multiple tables/lists need to be |
658 ;(put 'gnus-atomic-setq 'edebug-form-spec '(body)) | 825 ;(put 'gnus-atomic-setq 'edebug-form-spec '(body)) |
659 | 826 |
660 | 827 |
661 ;;; Functions for saving to babyl/mail files. | 828 ;;; Functions for saving to babyl/mail files. |
662 | 829 |
663 (defvar rmail-default-rmail-file) | 830 (eval-when-compile |
831 (condition-case nil | |
832 (progn | |
833 (require 'rmail) | |
834 (autoload 'rmail-update-summary "rmailsum")) | |
835 (error | |
836 (define-compiler-macro rmail-select-summary (&rest body) | |
837 ;; Rmail of the XEmacs version is supplied by the package, and | |
838 ;; requires tm and apel packages. However, there may be those | |
839 ;; who haven't installed those packages. This macro helps such | |
840 ;; people even if they install those packages later. | |
841 `(eval '(rmail-select-summary ,@body))) | |
842 ;; If there's rmail but there's no tm (or there's apel of the | |
843 ;; mainstream, not the XEmacs version), loading rmail of the XEmacs | |
844 ;; version fails halfway, however it provides the rmail-select-summary | |
845 ;; macro which uses the following functions: | |
846 (autoload 'rmail-summary-displayed "rmail") | |
847 (autoload 'rmail-maybe-display-summary "rmail"))) | |
848 (defvar rmail-default-rmail-file) | |
849 (defvar mm-text-coding-system)) | |
850 | |
664 (defun gnus-output-to-rmail (filename &optional ask) | 851 (defun gnus-output-to-rmail (filename &optional ask) |
665 "Append the current article to an Rmail file named FILENAME." | 852 "Append the current article to an Rmail file named FILENAME." |
666 (require 'rmail) | 853 (require 'rmail) |
854 (require 'mm-util) | |
667 ;; Most of these codes are borrowed from rmailout.el. | 855 ;; Most of these codes are borrowed from rmailout.el. |
668 (setq filename (expand-file-name filename)) | 856 (setq filename (expand-file-name filename)) |
669 (setq rmail-default-rmail-file filename) | 857 (setq rmail-default-rmail-file filename) |
670 (let ((artbuf (current-buffer)) | 858 (let ((artbuf (current-buffer)) |
671 (tmpbuf (get-buffer-create " *Gnus-output*"))) | 859 (tmpbuf (get-buffer-create " *Gnus-output*"))) |
704 (narrow-to-region (point-max) (point-max))) | 892 (narrow-to-region (point-max) (point-max))) |
705 (insert-buffer-substring tmpbuf) | 893 (insert-buffer-substring tmpbuf) |
706 (when msg | 894 (when msg |
707 (goto-char (point-min)) | 895 (goto-char (point-min)) |
708 (widen) | 896 (widen) |
709 (search-backward "\n\^_") | 897 (search-backward "\n\^_") |
710 (narrow-to-region (point) (point-max)) | 898 (narrow-to-region (point) (point-max)) |
711 (rmail-count-new-messages t) | 899 (rmail-count-new-messages t) |
712 (when (rmail-summary-exists) | 900 (when (rmail-summary-exists) |
713 (rmail-select-summary | 901 (rmail-select-summary |
714 (rmail-update-summary))) | 902 (rmail-update-summary))) |
715 (rmail-count-new-messages t) | 903 (rmail-count-new-messages t) |
716 (rmail-show-message msg)) | 904 (rmail-show-message msg)) |
717 (save-buffer))))) | 905 (save-buffer))))) |
783 (replace-match "\n^_" t t)) ;2 chars: "^" and "_" | 971 (replace-match "\n^_" t t)) ;2 chars: "^" and "_" |
784 (goto-char (point-max)) | 972 (goto-char (point-max)) |
785 (insert "\^_"))) | 973 (insert "\^_"))) |
786 | 974 |
787 (defun gnus-map-function (funs arg) | 975 (defun gnus-map-function (funs arg) |
788 "Applies the result of the first function in FUNS to the second, and so on. | 976 "Apply the result of the first function in FUNS to the second, and so on. |
789 ARG is passed to the first function." | 977 ARG is passed to the first function." |
790 (let ((myfuns funs)) | 978 (while funs |
791 (while myfuns | 979 (setq arg (funcall (pop funs) arg))) |
792 (setq arg (funcall (pop myfuns) arg))) | 980 arg) |
793 arg)) | |
794 | 981 |
795 (defun gnus-run-hooks (&rest funcs) | 982 (defun gnus-run-hooks (&rest funcs) |
796 "Does the same as `run-hooks', but saves excursion." | 983 "Does the same as `run-hooks', but saves the current buffer." |
797 (let ((buf (current-buffer))) | 984 (save-current-buffer |
798 (unwind-protect | 985 (apply 'run-hooks funcs))) |
799 (apply 'run-hooks funcs) | |
800 (set-buffer buf)))) | |
801 | |
802 ;;; | |
803 ;;; .netrc and .authinforc parsing | |
804 ;;; | |
805 | |
806 (defun gnus-parse-netrc (file) | |
807 "Parse FILE and return a list of all entries in the file." | |
808 (when (file-exists-p file) | |
809 (with-temp-buffer | |
810 (let ((tokens '("machine" "default" "login" | |
811 "password" "account" "macdef" "force" | |
812 "port")) | |
813 alist elem result pair) | |
814 (insert-file-contents file) | |
815 (goto-char (point-min)) | |
816 ;; Go through the file, line by line. | |
817 (while (not (eobp)) | |
818 (narrow-to-region (point) (gnus-point-at-eol)) | |
819 ;; For each line, get the tokens and values. | |
820 (while (not (eobp)) | |
821 (skip-chars-forward "\t ") | |
822 ;; Skip lines that begin with a "#". | |
823 (if (eq (char-after) ?#) | |
824 (goto-char (point-max)) | |
825 (unless (eobp) | |
826 (setq elem | |
827 (if (= (following-char) ?\") | |
828 (read (current-buffer)) | |
829 (buffer-substring | |
830 (point) (progn (skip-chars-forward "^\t ") | |
831 (point))))) | |
832 (cond | |
833 ((equal elem "macdef") | |
834 ;; We skip past the macro definition. | |
835 (widen) | |
836 (while (and (zerop (forward-line 1)) | |
837 (looking-at "$"))) | |
838 (narrow-to-region (point) (point))) | |
839 ((member elem tokens) | |
840 ;; Tokens that don't have a following value are ignored, | |
841 ;; except "default". | |
842 (when (and pair (or (cdr pair) | |
843 (equal (car pair) "default"))) | |
844 (push pair alist)) | |
845 (setq pair (list elem))) | |
846 (t | |
847 ;; Values that haven't got a preceding token are ignored. | |
848 (when pair | |
849 (setcdr pair elem) | |
850 (push pair alist) | |
851 (setq pair nil))))))) | |
852 (when alist | |
853 (push (nreverse alist) result)) | |
854 (setq alist nil | |
855 pair nil) | |
856 (widen) | |
857 (forward-line 1)) | |
858 (nreverse result))))) | |
859 | |
860 (defun gnus-netrc-machine (list machine &optional port defaultport) | |
861 "Return the netrc values from LIST for MACHINE or for the default entry. | |
862 If PORT specified, only return entries with matching port tokens. | |
863 Entries without port tokens default to DEFAULTPORT." | |
864 (let ((rest list) | |
865 result) | |
866 (while list | |
867 (when (equal (cdr (assoc "machine" (car list))) machine) | |
868 (push (car list) result)) | |
869 (pop list)) | |
870 (unless result | |
871 ;; No machine name matches, so we look for default entries. | |
872 (while rest | |
873 (when (assoc "default" (car rest)) | |
874 (push (car rest) result)) | |
875 (pop rest))) | |
876 (when result | |
877 (setq result (nreverse result)) | |
878 (while (and result | |
879 (not (equal (or port defaultport "nntp") | |
880 (or (gnus-netrc-get (car result) "port") | |
881 defaultport "nntp")))) | |
882 (pop result)) | |
883 (car result)))) | |
884 | |
885 (defun gnus-netrc-get (alist type) | |
886 "Return the value of token TYPE from ALIST." | |
887 (cdr (assoc type alist))) | |
888 | 986 |
889 ;;; Various | 987 ;;; Various |
890 | 988 |
891 (defvar gnus-group-buffer) ; Compiler directive | 989 (defvar gnus-group-buffer) ; Compiler directive |
892 (defun gnus-alive-p () | 990 (defun gnus-alive-p () |
896 (save-excursion | 994 (save-excursion |
897 (set-buffer gnus-group-buffer) | 995 (set-buffer gnus-group-buffer) |
898 (eq major-mode 'gnus-group-mode)))) | 996 (eq major-mode 'gnus-group-mode)))) |
899 | 997 |
900 (defun gnus-remove-duplicates (list) | 998 (defun gnus-remove-duplicates (list) |
901 (let (new (tail list)) | 999 (let (new) |
902 (while tail | 1000 (while list |
903 (or (member (car tail) new) | 1001 (or (member (car list) new) |
904 (setq new (cons (car tail) new))) | 1002 (setq new (cons (car list) new))) |
905 (setq tail (cdr tail))) | 1003 (setq list (cdr list))) |
906 (nreverse new))) | 1004 (nreverse new))) |
907 | 1005 |
908 (defun gnus-delete-if (predicate list) | 1006 (defun gnus-remove-if (predicate list) |
909 "Delete elements from LIST that satisfy PREDICATE." | 1007 "Return a copy of LIST with all items satisfying PREDICATE removed." |
910 (let (out) | 1008 (let (out) |
911 (while list | 1009 (while list |
912 (unless (funcall predicate (car list)) | 1010 (unless (funcall predicate (car list)) |
913 (push (car list) out)) | 1011 (push (car list) out)) |
914 (pop list)) | 1012 (setq list (cdr list))) |
915 (nreverse out))) | 1013 (nreverse out))) |
916 | 1014 |
917 (defun gnus-delete-alist (key alist) | 1015 (if (fboundp 'assq-delete-all) |
918 "Delete all entries in ALIST that have a key eq to KEY." | 1016 (defalias 'gnus-delete-alist 'assq-delete-all) |
919 (let (entry) | 1017 (defun gnus-delete-alist (key alist) |
920 (while (setq entry (assq key alist)) | 1018 "Delete from ALIST all elements whose car is KEY. |
921 (setq alist (delq entry alist))) | 1019 Return the modified alist." |
922 alist)) | 1020 (let (entry) |
1021 (while (setq entry (assq key alist)) | |
1022 (setq alist (delq entry alist))) | |
1023 alist))) | |
923 | 1024 |
924 (defmacro gnus-pull (key alist &optional assoc-p) | 1025 (defmacro gnus-pull (key alist &optional assoc-p) |
925 "Modify ALIST to be without KEY." | 1026 "Modify ALIST to be without KEY." |
926 (unless (symbolp alist) | 1027 (unless (symbolp alist) |
927 (error "Not a symbol: %s" alist)) | 1028 (error "Not a symbol: %s" alist)) |
928 (let ((fun (if assoc-p 'assoc 'assq))) | 1029 (let ((fun (if assoc-p 'assoc 'assq))) |
929 `(setq ,alist (delq (,fun ,key ,alist) ,alist)))) | 1030 `(setq ,alist (delq (,fun ,key ,alist) ,alist)))) |
930 | 1031 |
931 (defun gnus-globalify-regexp (re) | 1032 (defun gnus-globalify-regexp (re) |
932 "Returns a regexp that matches a whole line, iff RE matches a part of it." | 1033 "Return a regexp that matches a whole line, iff RE matches a part of it." |
933 (concat (unless (string-match "^\\^" re) "^.*") | 1034 (concat (unless (string-match "^\\^" re) "^.*") |
934 re | 1035 re |
935 (unless (string-match "\\$$" re) ".*$"))) | 1036 (unless (string-match "\\$$" re) ".*$"))) |
936 | 1037 |
937 (defun gnus-set-window-start (&optional point) | 1038 (defun gnus-set-window-start (&optional point) |
938 "Set the window start to POINT, or (point) if nil." | 1039 "Set the window start to POINT, or (point) if nil." |
939 (let ((win (get-buffer-window (current-buffer) t))) | 1040 (let ((win (gnus-get-buffer-window (current-buffer) t))) |
940 (when win | 1041 (when win |
941 (set-window-start win (or point (point)))))) | 1042 (set-window-start win (or point (point)))))) |
942 | 1043 |
943 (defun gnus-annotation-in-region-p (b e) | 1044 (defun gnus-annotation-in-region-p (b e) |
944 (if (= b e) | 1045 (if (= b e) |
978 hashtb) | 1079 hashtb) |
979 (goto-char (point-max)) | 1080 (goto-char (point-max)) |
980 (while (search-backward "\\." nil t) | 1081 (while (search-backward "\\." nil t) |
981 (delete-char 1))))) | 1082 (delete-char 1))))) |
982 | 1083 |
1084 ;; Fixme: Why not use `with-output-to-temp-buffer'? | |
1085 (defmacro gnus-with-output-to-file (file &rest body) | |
1086 (let ((buffer (make-symbol "output-buffer")) | |
1087 (size (make-symbol "output-buffer-size")) | |
1088 (leng (make-symbol "output-buffer-length")) | |
1089 (append (make-symbol "output-buffer-append"))) | |
1090 `(let* ((,size 131072) | |
1091 (,buffer (make-string ,size 0)) | |
1092 (,leng 0) | |
1093 (,append nil) | |
1094 (standard-output | |
1095 (lambda (c) | |
1096 (aset ,buffer ,leng c) | |
1097 | |
1098 (if (= ,size (setq ,leng (1+ ,leng))) | |
1099 (progn (write-region ,buffer nil ,file ,append 'no-msg) | |
1100 (setq ,leng 0 | |
1101 ,append t)))))) | |
1102 ,@body | |
1103 (when (> ,leng 0) | |
1104 (let ((coding-system-for-write 'no-conversion)) | |
1105 (write-region (substring ,buffer 0 ,leng) nil ,file | |
1106 ,append 'no-msg)))))) | |
1107 | |
1108 (put 'gnus-with-output-to-file 'lisp-indent-function 1) | |
1109 (put 'gnus-with-output-to-file 'edebug-form-spec '(form body)) | |
1110 | |
1111 (if (fboundp 'union) | |
1112 (defalias 'gnus-union 'union) | |
1113 (defun gnus-union (l1 l2) | |
1114 "Set union of lists L1 and L2." | |
1115 (cond ((null l1) l2) | |
1116 ((null l2) l1) | |
1117 ((equal l1 l2) l1) | |
1118 (t | |
1119 (or (>= (length l1) (length l2)) | |
1120 (setq l1 (prog1 l2 (setq l2 l1)))) | |
1121 (while l2 | |
1122 (or (member (car l2) l1) | |
1123 (push (car l2) l1)) | |
1124 (pop l2)) | |
1125 l1)))) | |
1126 | |
983 (defun gnus-add-text-properties-when | 1127 (defun gnus-add-text-properties-when |
984 (property value start end properties &optional object) | 1128 (property value start end properties &optional object) |
985 "Like `gnus-add-text-properties', only applied on where PROPERTY is VALUE." | 1129 "Like `gnus-add-text-properties', only applied on where PROPERTY is VALUE." |
986 (let (point) | 1130 (let (point) |
987 (while (and start | 1131 (while (and start |
1132 (< start end) ;; XEmacs will loop for every when start=end. | |
988 (setq point (text-property-not-all start end property value))) | 1133 (setq point (text-property-not-all start end property value))) |
989 (gnus-add-text-properties start point properties object) | 1134 (gnus-add-text-properties start point properties object) |
990 (setq start (text-property-any point end property value))) | 1135 (setq start (text-property-any point end property value))) |
991 (if start | 1136 (if start |
992 (gnus-add-text-properties start end properties object)))) | 1137 (gnus-add-text-properties start end properties object)))) |
994 (defun gnus-remove-text-properties-when | 1139 (defun gnus-remove-text-properties-when |
995 (property value start end properties &optional object) | 1140 (property value start end properties &optional object) |
996 "Like `remove-text-properties', only applied on where PROPERTY is VALUE." | 1141 "Like `remove-text-properties', only applied on where PROPERTY is VALUE." |
997 (let (point) | 1142 (let (point) |
998 (while (and start | 1143 (while (and start |
1144 (< start end) | |
999 (setq point (text-property-not-all start end property value))) | 1145 (setq point (text-property-not-all start end property value))) |
1000 (remove-text-properties start point properties object) | 1146 (remove-text-properties start point properties object) |
1001 (setq start (text-property-any point end property value))) | 1147 (setq start (text-property-any point end property value))) |
1002 (if start | 1148 (if start |
1003 (remove-text-properties start end properties object)) | 1149 (remove-text-properties start end properties object)) |
1004 t)) | 1150 t)) |
1005 | 1151 |
1152 ;; This might use `compare-strings' to reduce consing in the | |
1153 ;; case-insensitive case, but it has to cope with null args. | |
1154 ;; (`string-equal' uses symbol print names.) | |
1155 (defun gnus-string-equal (x y) | |
1156 "Like `string-equal', except it compares case-insensitively." | |
1157 (and (= (length x) (length y)) | |
1158 (or (string-equal x y) | |
1159 (string-equal (downcase x) (downcase y))))) | |
1160 | |
1161 (defcustom gnus-use-byte-compile t | |
1162 "If non-nil, byte-compile crucial run-time code. | |
1163 Setting it to nil has no effect after the first time `gnus-byte-compile' | |
1164 is run." | |
1165 :type 'boolean | |
1166 :version "21.1" | |
1167 :group 'gnus-various) | |
1168 | |
1169 (defun gnus-byte-compile (form) | |
1170 "Byte-compile FORM if `gnus-use-byte-compile' is non-nil." | |
1171 (if gnus-use-byte-compile | |
1172 (progn | |
1173 (condition-case nil | |
1174 ;; Work around a bug in XEmacs 21.4 | |
1175 (require 'byte-optimize) | |
1176 (error)) | |
1177 (require 'bytecomp) | |
1178 (defalias 'gnus-byte-compile | |
1179 (lambda (form) | |
1180 (let ((byte-compile-warnings '(unresolved callargs redefine))) | |
1181 (byte-compile form)))) | |
1182 (gnus-byte-compile form)) | |
1183 form)) | |
1184 | |
1185 (defun gnus-remassoc (key alist) | |
1186 "Delete by side effect any elements of LIST whose car is `equal' to KEY. | |
1187 The modified LIST is returned. If the first member | |
1188 of LIST has a car that is `equal' to KEY, there is no way to remove it | |
1189 by side effect; therefore, write `(setq foo (remassoc key foo))' to be | |
1190 sure of changing the value of `foo'." | |
1191 (when alist | |
1192 (if (equal key (caar alist)) | |
1193 (cdr alist) | |
1194 (setcdr alist (gnus-remassoc key (cdr alist))) | |
1195 alist))) | |
1196 | |
1197 (defun gnus-update-alist-soft (key value alist) | |
1198 (if value | |
1199 (cons (cons key value) (gnus-remassoc key alist)) | |
1200 (gnus-remassoc key alist))) | |
1201 | |
1202 (defun gnus-create-info-command (node) | |
1203 "Create a command that will go to info NODE." | |
1204 `(lambda () | |
1205 (interactive) | |
1206 ,(concat "Enter the info system at node " node) | |
1207 (Info-goto-node ,node) | |
1208 (setq gnus-info-buffer (current-buffer)) | |
1209 (gnus-configure-windows 'info))) | |
1210 | |
1211 (defun gnus-not-ignore (&rest args) | |
1212 t) | |
1213 | |
1006 (defvar gnus-directory-sep-char-regexp "/" | 1214 (defvar gnus-directory-sep-char-regexp "/" |
1007 "The regexp of directory separator character. | 1215 "The regexp of directory separator character. |
1008 If you find some problem with the directory separator character, try | 1216 If you find some problem with the directory separator character, try |
1009 \"[/\\\\\]\" for some systems.") | 1217 \"[/\\\\\]\" for some systems.") |
1010 | 1218 |
1219 (defun gnus-url-unhex (x) | |
1220 (if (> x ?9) | |
1221 (if (>= x ?a) | |
1222 (+ 10 (- x ?a)) | |
1223 (+ 10 (- x ?A))) | |
1224 (- x ?0))) | |
1225 | |
1226 ;; Fixme: Do it like QP. | |
1227 (defun gnus-url-unhex-string (str &optional allow-newlines) | |
1228 "Remove %XX, embedded spaces, etc in a url. | |
1229 If optional second argument ALLOW-NEWLINES is non-nil, then allow the | |
1230 decoding of carriage returns and line feeds in the string, which is normally | |
1231 forbidden in URL encoding." | |
1232 (let ((tmp "") | |
1233 (case-fold-search t)) | |
1234 (while (string-match "%[0-9a-f][0-9a-f]" str) | |
1235 (let* ((start (match-beginning 0)) | |
1236 (ch1 (gnus-url-unhex (elt str (+ start 1)))) | |
1237 (code (+ (* 16 ch1) | |
1238 (gnus-url-unhex (elt str (+ start 2)))))) | |
1239 (setq tmp (concat | |
1240 tmp (substring str 0 start) | |
1241 (cond | |
1242 (allow-newlines | |
1243 (char-to-string code)) | |
1244 ((or (= code ?\n) (= code ?\r)) | |
1245 " ") | |
1246 (t (char-to-string code)))) | |
1247 str (substring str (match-end 0))))) | |
1248 (setq tmp (concat tmp str)) | |
1249 tmp)) | |
1250 | |
1251 (defun gnus-make-predicate (spec) | |
1252 "Transform SPEC into a function that can be called. | |
1253 SPEC is a predicate specifier that contains stuff like `or', `and', | |
1254 `not', lists and functions. The functions all take one parameter." | |
1255 `(lambda (elem) ,(gnus-make-predicate-1 spec))) | |
1256 | |
1257 (defun gnus-make-predicate-1 (spec) | |
1258 (cond | |
1259 ((symbolp spec) | |
1260 `(,spec elem)) | |
1261 ((listp spec) | |
1262 (if (memq (car spec) '(or and not)) | |
1263 `(,(car spec) ,@(mapcar 'gnus-make-predicate-1 (cdr spec))) | |
1264 (error "Invalid predicate specifier: %s" spec))))) | |
1265 | |
1266 (defun gnus-local-map-property (map) | |
1267 "Return a list suitable for a text property list specifying keymap MAP." | |
1268 (cond | |
1269 ((featurep 'xemacs) | |
1270 (list 'keymap map)) | |
1271 ((>= emacs-major-version 21) | |
1272 (list 'keymap map)) | |
1273 (t | |
1274 (list 'local-map map)))) | |
1275 | |
1276 (defmacro gnus-completing-read-maybe-default (prompt table &optional predicate | |
1277 require-match initial-contents | |
1278 history default) | |
1279 "Like `completing-read', allowing for non-existent 7th arg in older XEmacsen." | |
1280 `(completing-read ,prompt ,table ,predicate ,require-match | |
1281 ,initial-contents ,history | |
1282 ,@(if (and (featurep 'xemacs) (< emacs-minor-version 2)) | |
1283 () | |
1284 (list default)))) | |
1285 | |
1286 (defun gnus-completing-read (prompt table &optional predicate require-match | |
1287 history) | |
1288 (when (and history | |
1289 (not (boundp history))) | |
1290 (set history nil)) | |
1291 (gnus-completing-read-maybe-default | |
1292 (if (symbol-value history) | |
1293 (concat prompt " (" (car (symbol-value history)) "): ") | |
1294 (concat prompt ": ")) | |
1295 table | |
1296 predicate | |
1297 require-match | |
1298 nil | |
1299 history | |
1300 (car (symbol-value history)))) | |
1301 | |
1302 (defun gnus-graphic-display-p () | |
1303 (or (and (fboundp 'display-graphic-p) | |
1304 (display-graphic-p)) | |
1305 ;;;!!!This is bogus. Fixme! | |
1306 (and (featurep 'xemacs) | |
1307 t))) | |
1308 | |
1309 (put 'gnus-parse-without-error 'lisp-indent-function 0) | |
1310 (put 'gnus-parse-without-error 'edebug-form-spec '(body)) | |
1311 | |
1312 (defmacro gnus-parse-without-error (&rest body) | |
1313 "Allow continuing onto the next line even if an error occurs." | |
1314 `(while (not (eobp)) | |
1315 (condition-case () | |
1316 (progn | |
1317 ,@body | |
1318 (goto-char (point-max))) | |
1319 (error | |
1320 (gnus-error 4 "Invalid data on line %d" | |
1321 (count-lines (point-min) (point))) | |
1322 (forward-line 1))))) | |
1323 | |
1324 (defun gnus-cache-file-contents (file variable function) | |
1325 "Cache the contents of FILE in VARIABLE. The contents come from FUNCTION." | |
1326 (let ((time (nth 5 (file-attributes file))) | |
1327 contents value) | |
1328 (if (or (null (setq value (symbol-value variable))) | |
1329 (not (equal (car value) file)) | |
1330 (not (equal (nth 1 value) time))) | |
1331 (progn | |
1332 (setq contents (funcall function file)) | |
1333 (set variable (list file time contents)) | |
1334 contents) | |
1335 (nth 2 value)))) | |
1336 | |
1337 (defun gnus-multiple-choice (prompt choice &optional idx) | |
1338 "Ask user a multiple choice question. | |
1339 CHOICE is a list of the choice char and help message at IDX." | |
1340 (let (tchar buf) | |
1341 (save-window-excursion | |
1342 (save-excursion | |
1343 (while (not tchar) | |
1344 (message "%s (%s): " | |
1345 prompt | |
1346 (concat | |
1347 (mapconcat (lambda (s) (char-to-string (car s))) | |
1348 choice ", ") ", ?")) | |
1349 (setq tchar (read-char)) | |
1350 (when (not (assq tchar choice)) | |
1351 (setq tchar nil) | |
1352 (setq buf (get-buffer-create "*Gnus Help*")) | |
1353 (pop-to-buffer buf) | |
1354 (fundamental-mode) ; for Emacs 20.4+ | |
1355 (buffer-disable-undo) | |
1356 (erase-buffer) | |
1357 (insert prompt ":\n\n") | |
1358 (let ((max -1) | |
1359 (list choice) | |
1360 (alist choice) | |
1361 (idx (or idx 1)) | |
1362 (i 0) | |
1363 n width pad format) | |
1364 ;; find the longest string to display | |
1365 (while list | |
1366 (setq n (length (nth idx (car list)))) | |
1367 (unless (> max n) | |
1368 (setq max n)) | |
1369 (setq list (cdr list))) | |
1370 (setq max (+ max 4)) ; %c, `:', SPACE, a SPACE at end | |
1371 (setq n (/ (1- (window-width)) max)) ; items per line | |
1372 (setq width (/ (1- (window-width)) n)) ; width of each item | |
1373 ;; insert `n' items, each in a field of width `width' | |
1374 (while alist | |
1375 (if (< i n) | |
1376 () | |
1377 (setq i 0) | |
1378 (delete-char -1) ; the `\n' takes a char | |
1379 (insert "\n")) | |
1380 (setq pad (- width 3)) | |
1381 (setq format (concat "%c: %-" (int-to-string pad) "s")) | |
1382 (insert (format format (caar alist) (nth idx (car alist)))) | |
1383 (setq alist (cdr alist)) | |
1384 (setq i (1+ i)))))))) | |
1385 (if (buffer-live-p buf) | |
1386 (kill-buffer buf)) | |
1387 tchar)) | |
1388 | |
1389 (defun gnus-select-frame-set-input-focus (frame) | |
1390 "Select FRAME, raise it, and set input focus, if possible." | |
1391 (cond ((featurep 'xemacs) | |
1392 (raise-frame frame) | |
1393 (select-frame frame) | |
1394 (focus-frame frame)) | |
1395 ;; The function `select-frame-set-input-focus' won't set | |
1396 ;; the input focus under Emacs 21.2 and X window system. | |
1397 ;;((fboundp 'select-frame-set-input-focus) | |
1398 ;; (defalias 'gnus-select-frame-set-input-focus | |
1399 ;; 'select-frame-set-input-focus) | |
1400 ;; (select-frame-set-input-focus frame)) | |
1401 (t | |
1402 (raise-frame frame) | |
1403 (select-frame frame) | |
1404 (cond ((and (eq window-system 'x) | |
1405 (fboundp 'x-focus-frame)) | |
1406 (x-focus-frame frame)) | |
1407 ((eq window-system 'w32) | |
1408 (w32-focus-frame frame))) | |
1409 (when focus-follows-mouse | |
1410 (set-mouse-position frame (1- (frame-width frame)) 0))))) | |
1411 | |
1412 (defun gnus-frame-or-window-display-name (object) | |
1413 "Given a frame or window, return the associated display name. | |
1414 Return nil otherwise." | |
1415 (if (featurep 'xemacs) | |
1416 (device-connection (dfw-device object)) | |
1417 (if (or (framep object) | |
1418 (and (windowp object) | |
1419 (setq object (window-frame object)))) | |
1420 (let ((display (frame-parameter object 'display))) | |
1421 (if (and (stringp display) | |
1422 ;; Exclude invalid display names. | |
1423 (string-match "\\`[^:]*:[0-9]+\\(\\.[0-9]+\\)?\\'" | |
1424 display)) | |
1425 display))))) | |
1426 | |
1427 ;; Fixme: This has only one use (in gnus-agent), which isn't worthwhile. | |
1428 (defmacro gnus-mapcar (function seq1 &rest seqs2_n) | |
1429 "Apply FUNCTION to each element of the sequences, and make a list of the results. | |
1430 If there are several sequences, FUNCTION is called with that many arguments, | |
1431 and mapping stops as soon as the shortest sequence runs out. With just one | |
1432 sequence, this is like `mapcar'. With several, it is like the Common Lisp | |
1433 `mapcar' function extended to arbitrary sequence types." | |
1434 | |
1435 (if seqs2_n | |
1436 (let* ((seqs (cons seq1 seqs2_n)) | |
1437 (cnt 0) | |
1438 (heads (mapcar (lambda (seq) | |
1439 (make-symbol (concat "head" | |
1440 (int-to-string | |
1441 (setq cnt (1+ cnt)))))) | |
1442 seqs)) | |
1443 (result (make-symbol "result")) | |
1444 (result-tail (make-symbol "result-tail"))) | |
1445 `(let* ,(let* ((bindings (cons nil nil)) | |
1446 (heads heads)) | |
1447 (nconc bindings (list (list result '(cons nil nil)))) | |
1448 (nconc bindings (list (list result-tail result))) | |
1449 (while heads | |
1450 (nconc bindings (list (list (pop heads) (pop seqs))))) | |
1451 (cdr bindings)) | |
1452 (while (and ,@heads) | |
1453 (setcdr ,result-tail (cons (funcall ,function | |
1454 ,@(mapcar (lambda (h) (list 'car h)) | |
1455 heads)) | |
1456 nil)) | |
1457 (setq ,result-tail (cdr ,result-tail) | |
1458 ,@(apply 'nconc (mapcar (lambda (h) (list h (list 'cdr h))) heads)))) | |
1459 (cdr ,result))) | |
1460 `(mapcar ,function ,seq1))) | |
1461 | |
1462 (if (fboundp 'merge) | |
1463 (defalias 'gnus-merge 'merge) | |
1464 ;; Adapted from cl-seq.el | |
1465 (defun gnus-merge (type list1 list2 pred) | |
1466 "Destructively merge lists LIST1 and LIST2 to produce a new list. | |
1467 Argument TYPE is for compatibility and ignored. | |
1468 Ordering of the elements is preserved according to PRED, a `less-than' | |
1469 predicate on the elements." | |
1470 (let ((res nil)) | |
1471 (while (and list1 list2) | |
1472 (if (funcall pred (car list2) (car list1)) | |
1473 (push (pop list2) res) | |
1474 (push (pop list1) res))) | |
1475 (nconc (nreverse res) list1 list2)))) | |
1476 | |
1477 (eval-when-compile | |
1478 (defvar xemacs-codename)) | |
1479 | |
1480 (defun gnus-emacs-version () | |
1481 "Stringified Emacs version." | |
1482 (let ((system-v | |
1483 (cond | |
1484 ((eq gnus-user-agent 'emacs-gnus-config) | |
1485 system-configuration) | |
1486 ((eq gnus-user-agent 'emacs-gnus-type) | |
1487 (symbol-name system-type)) | |
1488 (t nil)))) | |
1489 (cond | |
1490 ((eq gnus-user-agent 'gnus) | |
1491 nil) | |
1492 ((string-match "^\\(\\([.0-9]+\\)*\\)\\.[0-9]+$" emacs-version) | |
1493 (concat "Emacs/" (match-string 1 emacs-version) | |
1494 (if system-v | |
1495 (concat " (" system-v ")") | |
1496 ""))) | |
1497 ((string-match | |
1498 "\\([A-Z]*[Mm][Aa][Cc][Ss]\\)[^(]*\\(\\((beta.*)\\|'\\)\\)?" | |
1499 emacs-version) | |
1500 (concat | |
1501 (match-string 1 emacs-version) | |
1502 (format "/%d.%d" emacs-major-version emacs-minor-version) | |
1503 (if (match-beginning 3) | |
1504 (match-string 3 emacs-version) | |
1505 "") | |
1506 (if (boundp 'xemacs-codename) | |
1507 (concat | |
1508 " (" xemacs-codename | |
1509 (if system-v | |
1510 (concat ", " system-v ")") | |
1511 ")")) | |
1512 ""))) | |
1513 (t emacs-version)))) | |
1514 | |
1011 (provide 'gnus-util) | 1515 (provide 'gnus-util) |
1012 | 1516 |
1013 ;;; arch-tag: f94991af-d32b-4c97-8c26-ca12a934de49 | 1517 ;;; arch-tag: f94991af-d32b-4c97-8c26-ca12a934de49 |
1014 ;;; gnus-util.el ends here | 1518 ;;; gnus-util.el ends here |