Mercurial > emacs
comparison lisp/gnus/gnus-util.el @ 88155:d7ddb3e565de
sync with trunk
author | Henrik Enberg <henrik.enberg@telia.com> |
---|---|
date | Mon, 16 Jan 2006 00:03:54 +0000 |
parents | 52d99cc2e9e3 |
children |
comparison
equal
deleted
inserted
replaced
88154:8ce476d3ba36 | 88155:d7ddb3e565de |
---|---|
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 |
3 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 | |
3 ;; Free Software Foundation, Inc. | 4 ;; Free Software Foundation, Inc. |
4 | 5 |
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> | 6 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> |
6 ;; Keywords: news | 7 ;; Keywords: news |
7 | 8 |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 ;; GNU General Public License for more details. | 19 ;; GNU General Public License for more details. |
19 | 20 |
20 ;; You should have received a copy of the GNU General Public License | 21 ;; You should have received a copy of the GNU General Public License |
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the | 22 ;; along with GNU Emacs; see the file COPYING. If not, write to the |
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
23 ;; Boston, MA 02111-1307, USA. | 24 ;; Boston, MA 02110-1301, USA. |
24 | 25 |
25 ;;; Commentary: | 26 ;;; Commentary: |
26 | 27 |
27 ;; Nothing in this file depends on any other parts of Gnus -- all | 28 ;; Nothing in this file depends on any other parts of Gnus -- all |
28 ;; functions and macros in this file are utility functions that are | 29 ;; 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 | 30 ;; used by Gnus and may be used by any other package without loading |
30 ;; Gnus first. | 31 ;; Gnus first. |
31 | 32 |
33 ;; [Unfortunately, it does depend on other parts of Gnus, e.g. the | |
34 ;; autoloads below...] | |
35 | |
32 ;;; Code: | 36 ;;; Code: |
33 | 37 |
34 (require 'custom) | 38 (require 'custom) |
35 (eval-when-compile | 39 (eval-when-compile |
36 (require 'cl) | 40 (require 'cl) |
37 ;; Fixme: this should be a gnus variable, not nnmail-. | 41 ;; Fixme: this should be a gnus variable, not nnmail-. |
38 (defvar nnmail-pathname-coding-system)) | 42 (defvar nnmail-pathname-coding-system) |
39 (require 'nnheader) | 43 (defvar nnmail-active-file-coding-system) |
44 | |
45 ;; Inappropriate references to other parts of Gnus. | |
46 (defvar gnus-emphasize-whitespace-regexp) | |
47 (defvar gnus-original-article-buffer) | |
48 (defvar gnus-user-agent) | |
49 ) | |
40 (require 'time-date) | 50 (require 'time-date) |
51 (require 'netrc) | |
41 | 52 |
42 (eval-and-compile | 53 (eval-and-compile |
43 (autoload 'message-fetch-field "message") | 54 (autoload 'message-fetch-field "message") |
55 (autoload 'gnus-get-buffer-window "gnus-win") | |
44 (autoload 'rmail-insert-rmail-file-header "rmail") | 56 (autoload 'rmail-insert-rmail-file-header "rmail") |
45 (autoload 'rmail-count-new-messages "rmail") | 57 (autoload 'rmail-count-new-messages "rmail") |
46 (autoload 'rmail-show-message "rmail")) | 58 (autoload 'rmail-show-message "rmail") |
59 (autoload 'nnheader-narrow-to-headers "nnheader") | |
60 (autoload 'nnheader-replace-chars-in-string "nnheader")) | |
61 | |
62 (eval-and-compile | |
63 (cond | |
64 ((fboundp 'replace-in-string) | |
65 (defalias 'gnus-replace-in-string 'replace-in-string)) | |
66 ((fboundp 'replace-regexp-in-string) | |
67 (defun gnus-replace-in-string (string regexp newtext &optional literal) | |
68 "Replace all matches for REGEXP with NEWTEXT in STRING. | |
69 If LITERAL is non-nil, insert NEWTEXT literally. Return a new | |
70 string containing the replacements. | |
71 | |
72 This is a compatibility function for different Emacsen." | |
73 (replace-regexp-in-string regexp newtext string nil literal))) | |
74 (t | |
75 (defun gnus-replace-in-string (string regexp newtext &optional literal) | |
76 "Replace all matches for REGEXP with NEWTEXT in STRING. | |
77 If LITERAL is non-nil, insert NEWTEXT literally. Return a new | |
78 string containing the replacements. | |
79 | |
80 This is a compatibility function for different Emacsen." | |
81 (let ((start 0) tail) | |
82 (while (string-match regexp string start) | |
83 (setq tail (- (length string) (match-end 0))) | |
84 (setq string (replace-match newtext nil literal string)) | |
85 (setq start (- (length string) tail)))) | |
86 string)))) | |
87 | |
88 ;;; bring in the netrc functions as aliases | |
89 (defalias 'gnus-netrc-get 'netrc-get) | |
90 (defalias 'gnus-netrc-machine 'netrc-machine) | |
91 (defalias 'gnus-parse-netrc 'netrc-parse) | |
47 | 92 |
48 (defun gnus-boundp (variable) | 93 (defun gnus-boundp (variable) |
49 "Return non-nil if VARIABLE is bound and non-nil." | 94 "Return non-nil if VARIABLE is bound and non-nil." |
50 (and (boundp variable) | 95 (and (boundp variable) |
51 (symbol-value variable))) | 96 (symbol-value variable))) |
52 | 97 |
53 (defmacro gnus-eval-in-buffer-window (buffer &rest forms) | 98 (defmacro gnus-eval-in-buffer-window (buffer &rest forms) |
54 "Pop to BUFFER, evaluate FORMS, and then return to the original window." | 99 "Pop to BUFFER, evaluate FORMS, and then return to the original window." |
55 (let ((tempvar (make-symbol "GnusStartBufferWindow")) | 100 (let ((tempvar (make-symbol "GnusStartBufferWindow")) |
56 (w (make-symbol "w")) | 101 (w (make-symbol "w")) |
57 (buf (make-symbol "buf"))) | 102 (buf (make-symbol "buf"))) |
58 `(let* ((,tempvar (selected-window)) | 103 `(let* ((,tempvar (selected-window)) |
59 (,buf ,buffer) | 104 (,buf ,buffer) |
60 (,w (get-buffer-window ,buf 'visible))) | 105 (,w (gnus-get-buffer-window ,buf 'visible))) |
61 (unwind-protect | 106 (unwind-protect |
62 (progn | 107 (progn |
63 (if ,w | 108 (if ,w |
64 (progn | 109 (progn |
65 (select-window ,w) | 110 (select-window ,w) |
66 (set-buffer (window-buffer ,w))) | 111 (set-buffer (window-buffer ,w))) |
67 (pop-to-buffer ,buf)) | 112 (pop-to-buffer ,buf)) |
68 ,@forms) | 113 ,@forms) |
69 (select-window ,tempvar))))) | 114 (select-window ,tempvar))))) |
70 | 115 |
71 (put 'gnus-eval-in-buffer-window 'lisp-indent-function 1) | 116 (put 'gnus-eval-in-buffer-window 'lisp-indent-function 1) |
72 (put 'gnus-eval-in-buffer-window 'edebug-form-spec '(form body)) | 117 (put 'gnus-eval-in-buffer-window 'edebug-form-spec '(form body)) |
73 | 118 |
74 (defmacro gnus-intern-safe (string hashtable) | 119 (defmacro gnus-intern-safe (string hashtable) |
79 symbol)) | 124 symbol)) |
80 | 125 |
81 ;; Added by Geoffrey T. Dairiki <dairiki@u.washington.edu>. A safe way | 126 ;; 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 | 127 ;; to limit the length of a string. This function is necessary since |
83 ;; `(substr "abc" 0 30)' pukes with "Args out of range". | 128 ;; `(substr "abc" 0 30)' pukes with "Args out of range". |
129 ;; Fixme: Why not `truncate-string-to-width'? | |
84 (defsubst gnus-limit-string (str width) | 130 (defsubst gnus-limit-string (str width) |
85 (if (> (length str) width) | 131 (if (> (length str) width) |
86 (substring str 0 width) | 132 (substring str 0 width) |
87 str)) | 133 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 | 134 |
95 (defsubst gnus-goto-char (point) | 135 (defsubst gnus-goto-char (point) |
96 (and point (goto-char point))) | 136 (and point (goto-char point))) |
97 | 137 |
98 (defmacro gnus-buffer-exists-p (buffer) | 138 (defmacro gnus-buffer-exists-p (buffer) |
99 `(let ((buffer ,buffer)) | 139 `(let ((buffer ,buffer)) |
100 (when buffer | 140 (when buffer |
101 (funcall (if (stringp buffer) 'get-buffer 'buffer-name) | 141 (funcall (if (stringp buffer) 'get-buffer 'buffer-name) |
102 buffer)))) | 142 buffer)))) |
103 | 143 |
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 | 144 (defalias 'gnus-point-at-bol |
110 (if (fboundp 'point-at-bol) | 145 (if (fboundp 'point-at-bol) |
111 'point-at-bol | 146 'point-at-bol |
112 'line-beginning-position)) | 147 'line-beginning-position)) |
113 | 148 |
114 (defalias 'gnus-point-at-eol | 149 (defalias 'gnus-point-at-eol |
115 (if (fboundp 'point-at-eol) | 150 (if (fboundp 'point-at-eol) |
116 'point-at-eol | 151 'point-at-eol |
117 'line-end-position)) | 152 'line-end-position)) |
153 | |
154 ;; The LOCAL arg to `add-hook' is interpreted differently in Emacs and | |
155 ;; XEmacs. In Emacs we don't need to call `make-local-hook' first. | |
156 ;; It's harmless, though, so the main purpose of this alias is to shut | |
157 ;; up the byte compiler. | |
158 (defalias 'gnus-make-local-hook | |
159 (if (eq (get 'make-local-hook 'byte-compile) | |
160 'byte-compile-obsolete) | |
161 'ignore ; Emacs | |
162 'make-local-hook)) ; XEmacs | |
118 | 163 |
119 (defun gnus-delete-first (elt list) | 164 (defun gnus-delete-first (elt list) |
120 "Delete by side effect the first occurrence of ELT as a member of LIST." | 165 "Delete by side effect the first occurrence of ELT as a member of LIST." |
121 (if (equal (car list) elt) | 166 (if (equal (car list) elt) |
122 (cdr list) | 167 (cdr list) |
128 (setcdr list (cddr list))) | 173 (setcdr list (cddr list))) |
129 total))) | 174 total))) |
130 | 175 |
131 ;; Delete the current line (and the next N lines). | 176 ;; Delete the current line (and the next N lines). |
132 (defmacro gnus-delete-line (&optional n) | 177 (defmacro gnus-delete-line (&optional n) |
133 `(delete-region (progn (beginning-of-line) (point)) | 178 `(delete-region (gnus-point-at-bol) |
134 (progn (forward-line ,(or n 1)) (point)))) | 179 (progn (forward-line ,(or n 1)) (point)))) |
135 | 180 |
136 (defun gnus-byte-code (func) | 181 (defun gnus-byte-code (func) |
137 "Return a form that can be `eval'ed based on FUNC." | 182 "Return a form that can be `eval'ed based on FUNC." |
138 (let ((fval (indirect-function func))) | 183 (let ((fval (indirect-function func))) |
141 (setcar flist 'byte-code) | 186 (setcar flist 'byte-code) |
142 flist) | 187 flist) |
143 (cons 'progn (cddr fval))))) | 188 (cons 'progn (cddr fval))))) |
144 | 189 |
145 (defun gnus-extract-address-components (from) | 190 (defun gnus-extract-address-components (from) |
191 "Extract address components from a From header. | |
192 Given an RFC-822 address FROM, extract full name and canonical address. | |
193 Returns a list of the form (FULL-NAME CANONICAL-ADDRESS). Much more simple | |
194 solution than `mail-extract-address-components', which works much better, but | |
195 is slower." | |
146 (let (name address) | 196 (let (name address) |
147 ;; First find the address - the thing with the @ in it. This may | 197 ;; 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 | 198 ;; not be accurate in mail addresses, but does the trick most of |
149 ;; the time in news messages. | 199 ;; the time in news messages. |
150 (when (string-match "\\b[^@ \t<>]+[!@][^@ \t<>]+\\b" from) | 200 (when (string-match "\\b[^@ \t<>]+[!@][^@ \t<>]+\\b" from) |
153 (and address | 203 (and address |
154 ;; Linear white space is not required. | 204 ;; Linear white space is not required. |
155 (string-match (concat "[ \t]*<" (regexp-quote address) ">") from) | 205 (string-match (concat "[ \t]*<" (regexp-quote address) ">") from) |
156 (and (setq name (substring from 0 (match-beginning 0))) | 206 (and (setq name (substring from 0 (match-beginning 0))) |
157 ;; Strip any quotes from the name. | 207 ;; Strip any quotes from the name. |
158 (string-match "\".*\"" name) | 208 (string-match "^\".*\"$" name) |
159 (setq name (substring name 1 (1- (match-end 0)))))) | 209 (setq name (substring name 1 (1- (match-end 0)))))) |
160 ;; If not, then "address (name)" is used. | 210 ;; If not, then "address (name)" is used. |
161 (or name | 211 (or name |
162 (and (string-match "(.+)" from) | 212 (and (string-match "(.+)" from) |
163 (setq name (substring from (1+ (match-beginning 0)) | 213 (setq name (substring from (1+ (match-beginning 0)) |
178 (let ((case-fold-search t) | 228 (let ((case-fold-search t) |
179 (inhibit-point-motion-hooks t)) | 229 (inhibit-point-motion-hooks t)) |
180 (nnheader-narrow-to-headers) | 230 (nnheader-narrow-to-headers) |
181 (message-fetch-field field))))) | 231 (message-fetch-field field))))) |
182 | 232 |
233 (defun gnus-fetch-original-field (field) | |
234 "Fetch FIELD from the original version of the current article." | |
235 (with-current-buffer gnus-original-article-buffer | |
236 (gnus-fetch-field field))) | |
237 | |
238 | |
183 (defun gnus-goto-colon () | 239 (defun gnus-goto-colon () |
184 (beginning-of-line) | 240 (beginning-of-line) |
185 (search-forward ":" (gnus-point-at-eol) t)) | 241 (let ((eol (gnus-point-at-eol))) |
242 (goto-char (or (text-property-any (point) eol 'gnus-position t) | |
243 (search-forward ":" eol t) | |
244 (point))))) | |
245 | |
246 (defun gnus-decode-newsgroups (newsgroups group &optional method) | |
247 (let ((method (or method (gnus-find-method-for-group group)))) | |
248 (mapconcat (lambda (group) | |
249 (gnus-group-name-decode group (gnus-group-name-charset | |
250 method group))) | |
251 (message-tokenize-header newsgroups) | |
252 ","))) | |
186 | 253 |
187 (defun gnus-remove-text-with-property (prop) | 254 (defun gnus-remove-text-with-property (prop) |
188 "Delete all text in the current buffer with text property PROP." | 255 "Delete all text in the current buffer with text property PROP." |
189 (save-excursion | 256 (save-excursion |
190 (goto-char (point-min)) | 257 (goto-char (point-min)) |
193 (delete-char 1)) | 260 (delete-char 1)) |
194 (goto-char (next-single-property-change (point) prop nil (point-max)))))) | 261 (goto-char (next-single-property-change (point) prop nil (point-max)))))) |
195 | 262 |
196 (defun gnus-newsgroup-directory-form (newsgroup) | 263 (defun gnus-newsgroup-directory-form (newsgroup) |
197 "Make hierarchical directory name from NEWSGROUP name." | 264 "Make hierarchical directory name from NEWSGROUP name." |
198 (let ((newsgroup (gnus-newsgroup-savable-name newsgroup)) | 265 (let* ((newsgroup (gnus-newsgroup-savable-name newsgroup)) |
199 (len (length newsgroup)) | 266 (idx (string-match ":" newsgroup))) |
200 idx) | 267 (concat |
201 ;; If this is a foreign group, we don't want to translate the | 268 (if idx (substring newsgroup 0 idx)) |
202 ;; entire name. | 269 (if idx "/") |
203 (if (setq idx (string-match ":" newsgroup)) | 270 (nnheader-replace-chars-in-string |
204 (aset newsgroup idx ?/) | 271 (if idx (substring newsgroup (1+ idx)) newsgroup) |
205 (setq idx 0)) | 272 ?. ?/)))) |
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 | 273 |
213 (defun gnus-newsgroup-savable-name (group) | 274 (defun gnus-newsgroup-savable-name (group) |
214 ;; Replace any slashes in a group name (eg. an ange-ftp nndoc group) | 275 ;; Replace any slashes in a group name (eg. an ange-ftp nndoc group) |
215 ;; with dots. | 276 ;; with dots. |
216 (nnheader-replace-chars-in-string group ?/ ?.)) | 277 (nnheader-replace-chars-in-string group ?/ ?.)) |
269 (if (or (not safe) | 330 (if (or (not safe) |
270 (eq (lookup-key keymap key) 'undefined)) | 331 (eq (lookup-key keymap key) 'undefined)) |
271 (define-key keymap key (pop plist)) | 332 (define-key keymap key (pop plist)) |
272 (pop plist))))) | 333 (pop plist))))) |
273 | 334 |
274 (defun gnus-completing-read (default prompt &rest args) | 335 (defun gnus-completing-read-with-default (default prompt &rest args) |
275 ;; Like `completing-read', except that DEFAULT is the default argument. | 336 ;; Like `completing-read', except that DEFAULT is the default argument. |
276 (let* ((prompt (if default | 337 (let* ((prompt (if default |
277 (concat prompt " (default " default ") ") | 338 (concat prompt " (default " default "): ") |
278 (concat prompt " "))) | 339 (concat prompt ": "))) |
279 (answer (apply 'completing-read prompt args))) | 340 (answer (apply 'completing-read prompt args))) |
280 (if (or (null answer) (zerop (length answer))) | 341 (if (or (null answer) (zerop (length answer))) |
281 default | 342 default |
282 answer))) | 343 answer))) |
283 | 344 |
290 | 351 |
291 (defun gnus-yes-or-no-p (prompt) | 352 (defun gnus-yes-or-no-p (prompt) |
292 (prog1 | 353 (prog1 |
293 (yes-or-no-p prompt) | 354 (yes-or-no-p prompt) |
294 (message ""))) | 355 (message ""))) |
356 | |
357 ;; By Frank Schmitt <ich@Frank-Schmitt.net>. Allows to have | |
358 ;; age-depending date representations. (e.g. just the time if it's | |
359 ;; from today, the day of the week if it's within the last 7 days and | |
360 ;; the full date if it's older) | |
361 | |
362 (defun gnus-seconds-today () | |
363 "Return the number of seconds passed today." | |
364 (let ((now (decode-time (current-time)))) | |
365 (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)))) | |
366 | |
367 (defun gnus-seconds-month () | |
368 "Return the number of seconds passed this month." | |
369 (let ((now (decode-time (current-time)))) | |
370 (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) | |
371 (* (- (car (nthcdr 3 now)) 1) 3600 24)))) | |
372 | |
373 (defun gnus-seconds-year () | |
374 "Return the number of seconds passed this year." | |
375 (let ((now (decode-time (current-time))) | |
376 (days (format-time-string "%j" (current-time)))) | |
377 (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) | |
378 (* (- (string-to-number days) 1) 3600 24)))) | |
379 | |
380 (defvar gnus-user-date-format-alist | |
381 '(((gnus-seconds-today) . "%k:%M") | |
382 (604800 . "%a %k:%M") ;;that's one week | |
383 ((gnus-seconds-month) . "%a %d") | |
384 ((gnus-seconds-year) . "%b %d") | |
385 (t . "%b %d '%y")) ;;this one is used when no | |
386 ;;other does match | |
387 "Specifies date format depending on age of article. | |
388 This is an alist of items (AGE . FORMAT). AGE can be a number (of | |
389 seconds) or a Lisp expression evaluating to a number. When the age of | |
390 the article is less than this number, then use `format-time-string' | |
391 with the corresponding FORMAT for displaying the date of the article. | |
392 If AGE is not a number or a Lisp expression evaluating to a | |
393 non-number, then the corresponding FORMAT is used as a default value. | |
394 | |
395 Note that the list is processed from the beginning, so it should be | |
396 sorted by ascending AGE. Also note that items following the first | |
397 non-number AGE will be ignored. | |
398 | |
399 You can use the functions `gnus-seconds-today', `gnus-seconds-month' | |
400 and `gnus-seconds-year' in the AGE spec. They return the number of | |
401 seconds passed since the start of today, of this month, of this year, | |
402 respectively.") | |
403 | |
404 (defun gnus-user-date (messy-date) | |
405 "Format the messy-date according to gnus-user-date-format-alist. | |
406 Returns \" ? \" if there's bad input or if an other error occurs. | |
407 Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"." | |
408 (condition-case () | |
409 (let* ((messy-date (time-to-seconds (safe-date-to-time messy-date))) | |
410 (now (time-to-seconds (current-time))) | |
411 ;;If we don't find something suitable we'll use this one | |
412 (my-format "%b %d '%y")) | |
413 (let* ((difference (- now messy-date)) | |
414 (templist gnus-user-date-format-alist) | |
415 (top (eval (caar templist)))) | |
416 (while (if (numberp top) (< top difference) (not top)) | |
417 (progn | |
418 (setq templist (cdr templist)) | |
419 (setq top (eval (caar templist))))) | |
420 (if (stringp (cdr (car templist))) | |
421 (setq my-format (cdr (car templist))))) | |
422 (format-time-string (eval my-format) (seconds-to-time messy-date))) | |
423 (error " ? "))) | |
295 | 424 |
296 (defun gnus-dd-mmm (messy-date) | 425 (defun gnus-dd-mmm (messy-date) |
297 "Return a string like DD-MMM from a big messy string." | 426 "Return a string like DD-MMM from a big messy string." |
298 (condition-case () | 427 (condition-case () |
299 (format-time-string "%d-%b" (safe-date-to-time messy-date)) | 428 (format-time-string "%d-%b" (safe-date-to-time messy-date)) |
323 (gnus-time-iso8601 (gnus-date-get-time date)) | 452 (gnus-time-iso8601 (gnus-date-get-time date)) |
324 (error ""))) | 453 (error ""))) |
325 | 454 |
326 (defun gnus-mode-string-quote (string) | 455 (defun gnus-mode-string-quote (string) |
327 "Quote all \"%\"'s in STRING." | 456 "Quote all \"%\"'s in STRING." |
328 (save-excursion | 457 (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 | 458 |
336 ;; Make a hash table (default and minimum size is 256). | 459 ;; Make a hash table (default and minimum size is 256). |
337 ;; Optional argument HASHSIZE specifies the table size. | 460 ;; Optional argument HASHSIZE specifies the table size. |
338 (defun gnus-make-hashtable (&optional hashsize) | 461 (defun gnus-make-hashtable (&optional hashsize) |
339 (make-vector (if hashsize (max (gnus-create-hash-size hashsize) 256) 256) 0)) | 462 (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 | 480 display most important messages; and at ten, Gnus will keep on |
358 jabbering all the time." | 481 jabbering all the time." |
359 :group 'gnus-start | 482 :group 'gnus-start |
360 :type 'integer) | 483 :type 'integer) |
361 | 484 |
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) | 485 (defun gnus-message (level &rest args) |
486 "If LEVEL is lower than `gnus-verbose' print ARGS using `message'. | |
487 | |
488 Guideline for numbers: | |
489 1 - error messages, 3 - non-serious error messages, 5 - messages for things | |
490 that take a long time, 7 - not very important messages on stuff, 9 - messages | |
491 inside loops." | |
368 (if (<= level gnus-verbose) | 492 (if (<= level gnus-verbose) |
369 (apply 'message args) | 493 (apply 'message args) |
370 ;; We have to do this format thingy here even if the result isn't | 494 ;; 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 | 495 ;; shown - the return value has to be the same as the return value |
372 ;; from `message'. | 496 ;; from `message'. |
385 | 509 |
386 (defun gnus-split-references (references) | 510 (defun gnus-split-references (references) |
387 "Return a list of Message-IDs in REFERENCES." | 511 "Return a list of Message-IDs in REFERENCES." |
388 (let ((beg 0) | 512 (let ((beg 0) |
389 ids) | 513 ids) |
390 (while (string-match "<[^>]+>" references beg) | 514 (while (string-match "<[^<]+[^< \t]" references beg) |
391 (push (substring references (match-beginning 0) (setq beg (match-end 0))) | 515 (push (substring references (match-beginning 0) (setq beg (match-end 0))) |
392 ids)) | 516 ids)) |
393 (nreverse ids))) | 517 (nreverse ids))) |
394 | 518 |
395 (defsubst gnus-parent-id (references &optional n) | 519 (defsubst gnus-parent-id (references &optional n) |
396 "Return the last Message-ID in REFERENCES. | 520 "Return the last Message-ID in REFERENCES. |
397 If N, return the Nth ancestor instead." | 521 If N, return the Nth ancestor instead." |
398 (when references | 522 (when (and references |
399 (let ((ids (inline (gnus-split-references references)))) | 523 (not (zerop (length references)))) |
400 (while (nthcdr (or n 1) ids) | 524 (if n |
401 (setq ids (cdr ids))) | 525 (let ((ids (inline (gnus-split-references references)))) |
402 (car ids)))) | 526 (while (nthcdr n ids) |
403 | 527 (setq ids (cdr ids))) |
404 (defsubst gnus-buffer-live-p (buffer) | 528 (car ids)) |
529 (when (string-match "\\(<[^<]+>\\)[ \t]*\\'" references) | |
530 (match-string 1 references))))) | |
531 | |
532 (defun gnus-buffer-live-p (buffer) | |
405 "Say whether BUFFER is alive or not." | 533 "Say whether BUFFER is alive or not." |
406 (and buffer | 534 (and buffer |
407 (get-buffer buffer) | 535 (get-buffer buffer) |
408 (buffer-name (get-buffer buffer)))) | 536 (buffer-name (get-buffer buffer)))) |
409 | 537 |
410 (defun gnus-horizontal-recenter () | 538 (defun gnus-horizontal-recenter () |
411 "Recenter the current buffer horizontally." | 539 "Recenter the current buffer horizontally." |
412 (if (< (current-column) (/ (window-width) 2)) | 540 (if (< (current-column) (/ (window-width) 2)) |
413 (set-window-hscroll (get-buffer-window (current-buffer) t) 0) | 541 (set-window-hscroll (gnus-get-buffer-window (current-buffer) t) 0) |
414 (let* ((orig (point)) | 542 (let* ((orig (point)) |
415 (end (window-end (get-buffer-window (current-buffer) t))) | 543 (end (window-end (gnus-get-buffer-window (current-buffer) t))) |
416 (max 0)) | 544 (max 0)) |
417 (when end | 545 (when end |
418 ;; Find the longest line currently displayed in the window. | 546 ;; Find the longest line currently displayed in the window. |
419 (goto-char (window-start)) | 547 (goto-char (window-start)) |
420 (while (and (not (eobp)) | 548 (while (and (not (eobp)) |
424 (forward-line 1)) | 552 (forward-line 1)) |
425 (goto-char orig) | 553 (goto-char orig) |
426 ;; Scroll horizontally to center (sort of) the point. | 554 ;; Scroll horizontally to center (sort of) the point. |
427 (if (> max (window-width)) | 555 (if (> max (window-width)) |
428 (set-window-hscroll | 556 (set-window-hscroll |
429 (get-buffer-window (current-buffer) t) | 557 (gnus-get-buffer-window (current-buffer) t) |
430 (min (- (current-column) (/ (window-width) 3)) | 558 (min (- (current-column) (/ (window-width) 3)) |
431 (+ 2 (- max (window-width))))) | 559 (+ 2 (- max (window-width))))) |
432 (set-window-hscroll (get-buffer-window (current-buffer) t) 0)) | 560 (set-window-hscroll (gnus-get-buffer-window (current-buffer) t) 0)) |
433 max)))) | 561 max)))) |
434 | 562 |
435 (defun gnus-read-event-char () | 563 (defun gnus-read-event-char (&optional prompt) |
436 "Get the next event." | 564 "Get the next event." |
437 (let ((event (read-event))) | 565 (let ((event (read-event prompt))) |
438 ;; should be gnus-characterp, but this can't be called in XEmacs anyway | 566 ;; should be gnus-characterp, but this can't be called in XEmacs anyway |
439 (cons (and (numberp event) event) event))) | 567 (cons (and (numberp event) event) event))) |
440 | 568 |
441 (defun gnus-sortable-date (date) | 569 (defun gnus-sortable-date (date) |
442 "Make string suitable for sorting from DATE." | 570 "Make string suitable for sorting from DATE." |
472 (if (string-match "^[^:]+:" gname) | 600 (if (string-match "^[^:]+:" gname) |
473 (substring gname (match-end 0)) | 601 (substring gname (match-end 0)) |
474 gname))) | 602 gname))) |
475 | 603 |
476 (defun gnus-make-sort-function (funs) | 604 (defun gnus-make-sort-function (funs) |
477 "Return a composite sort condition based on the functions in FUNC." | 605 "Return a composite sort condition based on the functions in FUNS." |
478 (cond | 606 (cond |
479 ;; Just a simple function. | 607 ;; Just a simple function. |
480 ((gnus-functionp funs) funs) | 608 ((functionp funs) funs) |
481 ;; No functions at all. | 609 ;; No functions at all. |
482 ((null funs) funs) | 610 ((null funs) funs) |
483 ;; A list of functions. | 611 ;; A list of functions. |
484 ((or (cdr funs) | 612 ((or (cdr funs) |
485 (listp (car funs))) | 613 (listp (car funs))) |
486 `(lambda (t1 t2) | 614 (gnus-byte-compile |
487 ,(gnus-make-sort-function-1 (reverse funs)))) | 615 `(lambda (t1 t2) |
616 ,(gnus-make-sort-function-1 (reverse funs))))) | |
488 ;; A list containing just one function. | 617 ;; A list containing just one function. |
489 (t | 618 (t |
490 (car funs)))) | 619 (car funs)))) |
491 | 620 |
492 (defun gnus-make-sort-function-1 (funs) | 621 (defun gnus-make-sort-function-1 (funs) |
493 "Return a composite sort condition based on the functions in FUNC." | 622 "Return a composite sort condition based on the functions in FUNS." |
494 (let ((function (car funs)) | 623 (let ((function (car funs)) |
495 (first 't1) | 624 (first 't1) |
496 (last 't2)) | 625 (last 't2)) |
497 (when (consp function) | 626 (when (consp function) |
498 (cond | 627 (cond |
499 ;; Reversed spec. | 628 ;; Reversed spec. |
500 ((eq (car function) 'not) | 629 ((eq (car function) 'not) |
501 (setq function (cadr function) | 630 (setq function (cadr function) |
502 first 't2 | 631 first 't2 |
503 last 't1)) | 632 last 't1)) |
504 ((gnus-functionp function) | 633 ((functionp function) |
505 ;; Do nothing. | 634 ;; Do nothing. |
506 ) | 635 ) |
507 (t | 636 (t |
508 (error "Invalid sort spec: %s" function)))) | 637 (error "Invalid sort spec: %s" function)))) |
509 (if (cdr funs) | 638 (if (cdr funs) |
515 (defun gnus-turn-off-edit-menu (type) | 644 (defun gnus-turn-off-edit-menu (type) |
516 "Turn off edit menu in `gnus-TYPE-mode-map'." | 645 "Turn off edit menu in `gnus-TYPE-mode-map'." |
517 (define-key (symbol-value (intern (format "gnus-%s-mode-map" type))) | 646 (define-key (symbol-value (intern (format "gnus-%s-mode-map" type))) |
518 [menu-bar edit] 'undefined)) | 647 [menu-bar edit] 'undefined)) |
519 | 648 |
649 (defmacro gnus-bind-print-variables (&rest forms) | |
650 "Bind print-* variables and evaluate FORMS. | |
651 This macro is used with `prin1', `pp', etc. in order to ensure printed | |
652 Lisp objects are loadable. Bind `print-quoted' and `print-readably' | |
653 to t, and `print-escape-multibyte', `print-escape-newlines', | |
654 `print-escape-nonascii', `print-length', `print-level' and | |
655 `print-string-length' to nil." | |
656 `(let ((print-quoted t) | |
657 (print-readably t) | |
658 ;;print-circle | |
659 ;;print-continuous-numbering | |
660 print-escape-multibyte | |
661 print-escape-newlines | |
662 print-escape-nonascii | |
663 ;;print-gensym | |
664 print-length | |
665 print-level | |
666 print-string-length) | |
667 ,@forms)) | |
668 | |
520 (defun gnus-prin1 (form) | 669 (defun gnus-prin1 (form) |
521 "Use `prin1' on FORM in the current buffer. | 670 "Use `prin1' on FORM in the current buffer. |
522 Bind `print-quoted' and `print-readably' to t while printing." | 671 Bind `print-quoted' and `print-readably' to t, and `print-length' and |
523 (let ((print-quoted t) | 672 `print-level' to nil. See also `gnus-bind-print-variables'." |
524 (print-readably t) | 673 (gnus-bind-print-variables (prin1 form (current-buffer)))) |
525 (print-escape-multibyte nil) | |
526 print-level print-length) | |
527 (prin1 form (current-buffer)))) | |
528 | 674 |
529 (defun gnus-prin1-to-string (form) | 675 (defun gnus-prin1-to-string (form) |
530 "The same as `prin1', but bind `print-quoted' and `print-readably' to t." | 676 "The same as `prin1'. |
531 (let ((print-quoted t) | 677 Bind `print-quoted' and `print-readably' to t, and `print-length' and |
532 (print-readably t)) | 678 `print-level' to nil. See also `gnus-bind-print-variables'." |
533 (prin1-to-string form))) | 679 (gnus-bind-print-variables (prin1-to-string form))) |
680 | |
681 (defun gnus-pp (form) | |
682 "Use `pp' on FORM in the current buffer. | |
683 Bind `print-quoted' and `print-readably' to t, and `print-length' and | |
684 `print-level' to nil. See also `gnus-bind-print-variables'." | |
685 (gnus-bind-print-variables (pp form (current-buffer)))) | |
686 | |
687 (defun gnus-pp-to-string (form) | |
688 "The same as `pp-to-string'. | |
689 Bind `print-quoted' and `print-readably' to t, and `print-length' and | |
690 `print-level' to nil. See also `gnus-bind-print-variables'." | |
691 (gnus-bind-print-variables (pp-to-string form))) | |
534 | 692 |
535 (defun gnus-make-directory (directory) | 693 (defun gnus-make-directory (directory) |
536 "Make DIRECTORY (and all its parents) if it doesn't exist." | 694 "Make DIRECTORY (and all its parents) if it doesn't exist." |
537 (require 'nnmail) | 695 (require 'nnmail) |
538 (let ((file-name-coding-system nnmail-pathname-coding-system)) | 696 (let ((file-name-coding-system nnmail-pathname-coding-system)) |
551 | 709 |
552 (defun gnus-delete-file (file) | 710 (defun gnus-delete-file (file) |
553 "Delete FILE if it exists." | 711 "Delete FILE if it exists." |
554 (when (file-exists-p file) | 712 (when (file-exists-p file) |
555 (delete-file file))) | 713 (delete-file file))) |
714 | |
715 (defun gnus-delete-directory (directory) | |
716 "Delete files in DIRECTORY. Subdirectories remain. | |
717 If there's no subdirectory, delete DIRECTORY as well." | |
718 (when (file-directory-p directory) | |
719 (let ((files (directory-files | |
720 directory t "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")) | |
721 file dir) | |
722 (while files | |
723 (setq file (pop files)) | |
724 (if (eq t (car (file-attributes file))) | |
725 ;; `file' is a subdirectory. | |
726 (setq dir t) | |
727 ;; `file' is a file or a symlink. | |
728 (delete-file file))) | |
729 (unless dir | |
730 (delete-directory directory))))) | |
556 | 731 |
557 (defun gnus-strip-whitespace (string) | 732 (defun gnus-strip-whitespace (string) |
558 "Return STRING stripped of all whitespace." | 733 "Return STRING stripped of all whitespace." |
559 (while (string-match "[\r\n\t ]+" string) | 734 (while (string-match "[\r\n\t ]+" string) |
560 (setq string (replace-match "" t t string))) | 735 (setq string (replace-match "" t t string))) |
569 (while (re-search-forward gnus-emphasize-whitespace-regexp end 'move) | 744 (while (re-search-forward gnus-emphasize-whitespace-regexp end 'move) |
570 (gnus-put-text-property beg (match-beginning 0) prop val) | 745 (gnus-put-text-property beg (match-beginning 0) prop val) |
571 (setq beg (point))) | 746 (setq beg (point))) |
572 (gnus-put-text-property beg (point) prop val))))) | 747 (gnus-put-text-property beg (point) prop val))))) |
573 | 748 |
749 (defsubst gnus-put-overlay-excluding-newlines (beg end prop val) | |
750 "The same as `put-text-property', but don't put this prop on any newlines in the region." | |
751 (save-match-data | |
752 (save-excursion | |
753 (save-restriction | |
754 (goto-char beg) | |
755 (while (re-search-forward gnus-emphasize-whitespace-regexp end 'move) | |
756 (gnus-overlay-put | |
757 (gnus-make-overlay beg (match-beginning 0)) | |
758 prop val) | |
759 (setq beg (point))) | |
760 (gnus-overlay-put (gnus-make-overlay beg (point)) prop val))))) | |
761 | |
574 (defun gnus-put-text-property-excluding-characters-with-faces (beg end | 762 (defun gnus-put-text-property-excluding-characters-with-faces (beg end |
575 prop val) | 763 prop val) |
576 "The same as `put-text-property', but don't put props on characters with the `gnus-face' property." | 764 "The same as `put-text-property', but don't put props on characters with the `gnus-face' property." |
577 (let ((b beg)) | 765 (let ((b beg)) |
578 (while (/= b end) | 766 (while (/= b end) |
579 (when (get-text-property b 'gnus-face) | 767 (when (get-text-property b 'gnus-face) |
580 (setq b (next-single-property-change b 'gnus-face nil end))) | 768 (setq b (next-single-property-change b 'gnus-face nil end))) |
581 (when (/= b end) | 769 (when (/= b end) |
582 (gnus-put-text-property | 770 (inline |
583 b (setq b (next-single-property-change b 'gnus-face nil end)) | 771 (gnus-put-text-property |
584 prop val))))) | 772 b (setq b (next-single-property-change b 'gnus-face nil end)) |
773 prop val)))))) | |
774 | |
775 (defmacro gnus-faces-at (position) | |
776 "Return a list of faces at POSITION." | |
777 (if (featurep 'xemacs) | |
778 `(let ((pos ,position)) | |
779 (mapcar-extents 'extent-face | |
780 nil (current-buffer) pos pos nil 'face)) | |
781 `(let ((pos ,position)) | |
782 (delq nil (cons (get-text-property pos 'face) | |
783 (mapcar | |
784 (lambda (overlay) | |
785 (overlay-get overlay 'face)) | |
786 (overlays-at pos))))))) | |
585 | 787 |
586 ;;; Protected and atomic operations. dmoore@ucsd.edu 21.11.1996 | 788 ;;; Protected and atomic operations. dmoore@ucsd.edu 21.11.1996 |
587 ;;; The primary idea here is to try to protect internal datastructures | 789 ;;; 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 | 790 ;;; 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 | 791 ;;; similar blows up. Often in Gnus multiple tables/lists need to be |
658 ;(put 'gnus-atomic-setq 'edebug-form-spec '(body)) | 860 ;(put 'gnus-atomic-setq 'edebug-form-spec '(body)) |
659 | 861 |
660 | 862 |
661 ;;; Functions for saving to babyl/mail files. | 863 ;;; Functions for saving to babyl/mail files. |
662 | 864 |
663 (defvar rmail-default-rmail-file) | 865 (eval-when-compile |
866 (condition-case nil | |
867 (progn | |
868 (require 'rmail) | |
869 (autoload 'rmail-update-summary "rmailsum")) | |
870 (error | |
871 (define-compiler-macro rmail-select-summary (&rest body) | |
872 ;; Rmail of the XEmacs version is supplied by the package, and | |
873 ;; requires tm and apel packages. However, there may be those | |
874 ;; who haven't installed those packages. This macro helps such | |
875 ;; people even if they install those packages later. | |
876 `(eval '(rmail-select-summary ,@body))) | |
877 ;; If there's rmail but there's no tm (or there's apel of the | |
878 ;; mainstream, not the XEmacs version), loading rmail of the XEmacs | |
879 ;; version fails halfway, however it provides the rmail-select-summary | |
880 ;; macro which uses the following functions: | |
881 (autoload 'rmail-summary-displayed "rmail") | |
882 (autoload 'rmail-maybe-display-summary "rmail"))) | |
883 (defvar rmail-default-rmail-file) | |
884 (defvar mm-text-coding-system)) | |
885 | |
664 (defun gnus-output-to-rmail (filename &optional ask) | 886 (defun gnus-output-to-rmail (filename &optional ask) |
665 "Append the current article to an Rmail file named FILENAME." | 887 "Append the current article to an Rmail file named FILENAME." |
666 (require 'rmail) | 888 (require 'rmail) |
889 (require 'mm-util) | |
667 ;; Most of these codes are borrowed from rmailout.el. | 890 ;; Most of these codes are borrowed from rmailout.el. |
668 (setq filename (expand-file-name filename)) | 891 (setq filename (expand-file-name filename)) |
669 (setq rmail-default-rmail-file filename) | 892 (setq rmail-default-rmail-file filename) |
670 (let ((artbuf (current-buffer)) | 893 (let ((artbuf (current-buffer)) |
671 (tmpbuf (get-buffer-create " *Gnus-output*"))) | 894 (tmpbuf (get-buffer-create " *Gnus-output*"))) |
704 (narrow-to-region (point-max) (point-max))) | 927 (narrow-to-region (point-max) (point-max))) |
705 (insert-buffer-substring tmpbuf) | 928 (insert-buffer-substring tmpbuf) |
706 (when msg | 929 (when msg |
707 (goto-char (point-min)) | 930 (goto-char (point-min)) |
708 (widen) | 931 (widen) |
709 (search-backward "\n\^_") | 932 (search-backward "\n\^_") |
710 (narrow-to-region (point) (point-max)) | 933 (narrow-to-region (point) (point-max)) |
711 (rmail-count-new-messages t) | 934 (rmail-count-new-messages t) |
712 (when (rmail-summary-exists) | 935 (when (rmail-summary-exists) |
713 (rmail-select-summary | 936 (rmail-select-summary |
714 (rmail-update-summary))) | 937 (rmail-update-summary))) |
715 (rmail-count-new-messages t) | 938 (rmail-count-new-messages t) |
716 (rmail-show-message msg)) | 939 (rmail-show-message msg)) |
717 (save-buffer))))) | 940 (save-buffer))))) |
783 (replace-match "\n^_" t t)) ;2 chars: "^" and "_" | 1006 (replace-match "\n^_" t t)) ;2 chars: "^" and "_" |
784 (goto-char (point-max)) | 1007 (goto-char (point-max)) |
785 (insert "\^_"))) | 1008 (insert "\^_"))) |
786 | 1009 |
787 (defun gnus-map-function (funs arg) | 1010 (defun gnus-map-function (funs arg) |
788 "Applies the result of the first function in FUNS to the second, and so on. | 1011 "Apply the result of the first function in FUNS to the second, and so on. |
789 ARG is passed to the first function." | 1012 ARG is passed to the first function." |
790 (let ((myfuns funs)) | 1013 (while funs |
791 (while myfuns | 1014 (setq arg (funcall (pop funs) arg))) |
792 (setq arg (funcall (pop myfuns) arg))) | 1015 arg) |
793 arg)) | |
794 | 1016 |
795 (defun gnus-run-hooks (&rest funcs) | 1017 (defun gnus-run-hooks (&rest funcs) |
796 "Does the same as `run-hooks', but saves excursion." | 1018 "Does the same as `run-hooks', but saves the current buffer." |
797 (let ((buf (current-buffer))) | 1019 (save-current-buffer |
798 (unwind-protect | 1020 (apply 'run-hooks funcs))) |
799 (apply 'run-hooks funcs) | 1021 |
800 (set-buffer buf)))) | 1022 (defun gnus-run-mode-hooks (&rest funcs) |
801 | 1023 "Run `run-mode-hooks' if it is available, otherwise `run-hooks'. |
802 ;;; | 1024 This function saves the current buffer." |
803 ;;; .netrc and .authinforc parsing | 1025 (if (fboundp 'run-mode-hooks) |
804 ;;; | 1026 (save-current-buffer (apply 'run-mode-hooks funcs)) |
805 | 1027 (save-current-buffer (apply 'run-hooks funcs)))) |
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 | 1028 |
889 ;;; Various | 1029 ;;; Various |
890 | 1030 |
891 (defvar gnus-group-buffer) ; Compiler directive | 1031 (defvar gnus-group-buffer) ; Compiler directive |
892 (defun gnus-alive-p () | 1032 (defun gnus-alive-p () |
895 (get-buffer gnus-group-buffer) | 1035 (get-buffer gnus-group-buffer) |
896 (save-excursion | 1036 (save-excursion |
897 (set-buffer gnus-group-buffer) | 1037 (set-buffer gnus-group-buffer) |
898 (eq major-mode 'gnus-group-mode)))) | 1038 (eq major-mode 'gnus-group-mode)))) |
899 | 1039 |
900 (defun gnus-remove-duplicates (list) | 1040 (defun gnus-remove-if (predicate list) |
901 (let (new (tail list)) | 1041 "Return a copy of LIST with all items satisfying PREDICATE removed." |
902 (while tail | |
903 (or (member (car tail) new) | |
904 (setq new (cons (car tail) new))) | |
905 (setq tail (cdr tail))) | |
906 (nreverse new))) | |
907 | |
908 (defun gnus-delete-if (predicate list) | |
909 "Delete elements from LIST that satisfy PREDICATE." | |
910 (let (out) | 1042 (let (out) |
911 (while list | 1043 (while list |
912 (unless (funcall predicate (car list)) | 1044 (unless (funcall predicate (car list)) |
913 (push (car list) out)) | 1045 (push (car list) out)) |
914 (pop list)) | 1046 (setq list (cdr list))) |
915 (nreverse out))) | 1047 (nreverse out))) |
916 | 1048 |
917 (defun gnus-delete-alist (key alist) | 1049 (if (fboundp 'assq-delete-all) |
918 "Delete all entries in ALIST that have a key eq to KEY." | 1050 (defalias 'gnus-delete-alist 'assq-delete-all) |
919 (let (entry) | 1051 (defun gnus-delete-alist (key alist) |
920 (while (setq entry (assq key alist)) | 1052 "Delete from ALIST all elements whose car is KEY. |
921 (setq alist (delq entry alist))) | 1053 Return the modified alist." |
922 alist)) | 1054 (let (entry) |
1055 (while (setq entry (assq key alist)) | |
1056 (setq alist (delq entry alist))) | |
1057 alist))) | |
923 | 1058 |
924 (defmacro gnus-pull (key alist &optional assoc-p) | 1059 (defmacro gnus-pull (key alist &optional assoc-p) |
925 "Modify ALIST to be without KEY." | 1060 "Modify ALIST to be without KEY." |
926 (unless (symbolp alist) | 1061 (unless (symbolp alist) |
927 (error "Not a symbol: %s" alist)) | 1062 (error "Not a symbol: %s" alist)) |
928 (let ((fun (if assoc-p 'assoc 'assq))) | 1063 (let ((fun (if assoc-p 'assoc 'assq))) |
929 `(setq ,alist (delq (,fun ,key ,alist) ,alist)))) | 1064 `(setq ,alist (delq (,fun ,key ,alist) ,alist)))) |
930 | 1065 |
931 (defun gnus-globalify-regexp (re) | 1066 (defun gnus-globalify-regexp (re) |
932 "Returns a regexp that matches a whole line, iff RE matches a part of it." | 1067 "Return a regexp that matches a whole line, iff RE matches a part of it." |
933 (concat (unless (string-match "^\\^" re) "^.*") | 1068 (concat (unless (string-match "^\\^" re) "^.*") |
934 re | 1069 re |
935 (unless (string-match "\\$$" re) ".*$"))) | 1070 (unless (string-match "\\$$" re) ".*$"))) |
936 | 1071 |
937 (defun gnus-set-window-start (&optional point) | 1072 (defun gnus-set-window-start (&optional point) |
938 "Set the window start to POINT, or (point) if nil." | 1073 "Set the window start to POINT, or (point) if nil." |
939 (let ((win (get-buffer-window (current-buffer) t))) | 1074 (let ((win (gnus-get-buffer-window (current-buffer) t))) |
940 (when win | 1075 (when win |
941 (set-window-start win (or point (point)))))) | 1076 (set-window-start win (or point (point)))))) |
942 | 1077 |
943 (defun gnus-annotation-in-region-p (b e) | 1078 (defun gnus-annotation-in-region-p (b e) |
944 (if (= b e) | 1079 (if (= b e) |
978 hashtb) | 1113 hashtb) |
979 (goto-char (point-max)) | 1114 (goto-char (point-max)) |
980 (while (search-backward "\\." nil t) | 1115 (while (search-backward "\\." nil t) |
981 (delete-char 1))))) | 1116 (delete-char 1))))) |
982 | 1117 |
1118 ;; Fixme: Why not use `with-output-to-temp-buffer'? | |
1119 (defmacro gnus-with-output-to-file (file &rest body) | |
1120 (let ((buffer (make-symbol "output-buffer")) | |
1121 (size (make-symbol "output-buffer-size")) | |
1122 (leng (make-symbol "output-buffer-length")) | |
1123 (append (make-symbol "output-buffer-append"))) | |
1124 `(let* ((,size 131072) | |
1125 (,buffer (make-string ,size 0)) | |
1126 (,leng 0) | |
1127 (,append nil) | |
1128 (standard-output | |
1129 (lambda (c) | |
1130 (aset ,buffer ,leng c) | |
1131 | |
1132 (if (= ,size (setq ,leng (1+ ,leng))) | |
1133 (progn (write-region ,buffer nil ,file ,append 'no-msg) | |
1134 (setq ,leng 0 | |
1135 ,append t)))))) | |
1136 ,@body | |
1137 (when (> ,leng 0) | |
1138 (let ((coding-system-for-write 'no-conversion)) | |
1139 (write-region (substring ,buffer 0 ,leng) nil ,file | |
1140 ,append 'no-msg)))))) | |
1141 | |
1142 (put 'gnus-with-output-to-file 'lisp-indent-function 1) | |
1143 (put 'gnus-with-output-to-file 'edebug-form-spec '(form body)) | |
1144 | |
1145 (if (fboundp 'union) | |
1146 (defalias 'gnus-union 'union) | |
1147 (defun gnus-union (l1 l2) | |
1148 "Set union of lists L1 and L2." | |
1149 (cond ((null l1) l2) | |
1150 ((null l2) l1) | |
1151 ((equal l1 l2) l1) | |
1152 (t | |
1153 (or (>= (length l1) (length l2)) | |
1154 (setq l1 (prog1 l2 (setq l2 l1)))) | |
1155 (while l2 | |
1156 (or (member (car l2) l1) | |
1157 (push (car l2) l1)) | |
1158 (pop l2)) | |
1159 l1)))) | |
1160 | |
983 (defun gnus-add-text-properties-when | 1161 (defun gnus-add-text-properties-when |
984 (property value start end properties &optional object) | 1162 (property value start end properties &optional object) |
985 "Like `gnus-add-text-properties', only applied on where PROPERTY is VALUE." | 1163 "Like `gnus-add-text-properties', only applied on where PROPERTY is VALUE." |
986 (let (point) | 1164 (let (point) |
987 (while (and start | 1165 (while (and start |
1166 (< start end) ;; XEmacs will loop for every when start=end. | |
988 (setq point (text-property-not-all start end property value))) | 1167 (setq point (text-property-not-all start end property value))) |
989 (gnus-add-text-properties start point properties object) | 1168 (gnus-add-text-properties start point properties object) |
990 (setq start (text-property-any point end property value))) | 1169 (setq start (text-property-any point end property value))) |
991 (if start | 1170 (if start |
992 (gnus-add-text-properties start end properties object)))) | 1171 (gnus-add-text-properties start end properties object)))) |
994 (defun gnus-remove-text-properties-when | 1173 (defun gnus-remove-text-properties-when |
995 (property value start end properties &optional object) | 1174 (property value start end properties &optional object) |
996 "Like `remove-text-properties', only applied on where PROPERTY is VALUE." | 1175 "Like `remove-text-properties', only applied on where PROPERTY is VALUE." |
997 (let (point) | 1176 (let (point) |
998 (while (and start | 1177 (while (and start |
1178 (< start end) | |
999 (setq point (text-property-not-all start end property value))) | 1179 (setq point (text-property-not-all start end property value))) |
1000 (remove-text-properties start point properties object) | 1180 (remove-text-properties start point properties object) |
1001 (setq start (text-property-any point end property value))) | 1181 (setq start (text-property-any point end property value))) |
1002 (if start | 1182 (if start |
1003 (remove-text-properties start end properties object)) | 1183 (remove-text-properties start end properties object)) |
1004 t)) | 1184 t)) |
1005 | 1185 |
1186 ;; This might use `compare-strings' to reduce consing in the | |
1187 ;; case-insensitive case, but it has to cope with null args. | |
1188 ;; (`string-equal' uses symbol print names.) | |
1189 (defun gnus-string-equal (x y) | |
1190 "Like `string-equal', except it compares case-insensitively." | |
1191 (and (= (length x) (length y)) | |
1192 (or (string-equal x y) | |
1193 (string-equal (downcase x) (downcase y))))) | |
1194 | |
1195 (defcustom gnus-use-byte-compile t | |
1196 "If non-nil, byte-compile crucial run-time code. | |
1197 Setting it to nil has no effect after the first time `gnus-byte-compile' | |
1198 is run." | |
1199 :type 'boolean | |
1200 :version "22.1" | |
1201 :group 'gnus-various) | |
1202 | |
1203 (defun gnus-byte-compile (form) | |
1204 "Byte-compile FORM if `gnus-use-byte-compile' is non-nil." | |
1205 (if gnus-use-byte-compile | |
1206 (progn | |
1207 (condition-case nil | |
1208 ;; Work around a bug in XEmacs 21.4 | |
1209 (require 'byte-optimize) | |
1210 (error)) | |
1211 (require 'bytecomp) | |
1212 (defalias 'gnus-byte-compile | |
1213 (lambda (form) | |
1214 (let ((byte-compile-warnings '(unresolved callargs redefine))) | |
1215 (byte-compile form)))) | |
1216 (gnus-byte-compile form)) | |
1217 form)) | |
1218 | |
1219 (defun gnus-remassoc (key alist) | |
1220 "Delete by side effect any elements of LIST whose car is `equal' to KEY. | |
1221 The modified LIST is returned. If the first member | |
1222 of LIST has a car that is `equal' to KEY, there is no way to remove it | |
1223 by side effect; therefore, write `(setq foo (gnus-remassoc key foo))' to be | |
1224 sure of changing the value of `foo'." | |
1225 (when alist | |
1226 (if (equal key (caar alist)) | |
1227 (cdr alist) | |
1228 (setcdr alist (gnus-remassoc key (cdr alist))) | |
1229 alist))) | |
1230 | |
1231 (defun gnus-update-alist-soft (key value alist) | |
1232 (if value | |
1233 (cons (cons key value) (gnus-remassoc key alist)) | |
1234 (gnus-remassoc key alist))) | |
1235 | |
1236 (defun gnus-create-info-command (node) | |
1237 "Create a command that will go to info NODE." | |
1238 `(lambda () | |
1239 (interactive) | |
1240 ,(concat "Enter the info system at node " node) | |
1241 (Info-goto-node ,node) | |
1242 (setq gnus-info-buffer (current-buffer)) | |
1243 (gnus-configure-windows 'info))) | |
1244 | |
1245 (defun gnus-not-ignore (&rest args) | |
1246 t) | |
1247 | |
1006 (defvar gnus-directory-sep-char-regexp "/" | 1248 (defvar gnus-directory-sep-char-regexp "/" |
1007 "The regexp of directory separator character. | 1249 "The regexp of directory separator character. |
1008 If you find some problem with the directory separator character, try | 1250 If you find some problem with the directory separator character, try |
1009 \"[/\\\\\]\" for some systems.") | 1251 \"[/\\\\\]\" for some systems.") |
1010 | 1252 |
1253 (defun gnus-url-unhex (x) | |
1254 (if (> x ?9) | |
1255 (if (>= x ?a) | |
1256 (+ 10 (- x ?a)) | |
1257 (+ 10 (- x ?A))) | |
1258 (- x ?0))) | |
1259 | |
1260 ;; Fixme: Do it like QP. | |
1261 (defun gnus-url-unhex-string (str &optional allow-newlines) | |
1262 "Remove %XX, embedded spaces, etc in a url. | |
1263 If optional second argument ALLOW-NEWLINES is non-nil, then allow the | |
1264 decoding of carriage returns and line feeds in the string, which is normally | |
1265 forbidden in URL encoding." | |
1266 (let ((tmp "") | |
1267 (case-fold-search t)) | |
1268 (while (string-match "%[0-9a-f][0-9a-f]" str) | |
1269 (let* ((start (match-beginning 0)) | |
1270 (ch1 (gnus-url-unhex (elt str (+ start 1)))) | |
1271 (code (+ (* 16 ch1) | |
1272 (gnus-url-unhex (elt str (+ start 2)))))) | |
1273 (setq tmp (concat | |
1274 tmp (substring str 0 start) | |
1275 (cond | |
1276 (allow-newlines | |
1277 (char-to-string code)) | |
1278 ((or (= code ?\n) (= code ?\r)) | |
1279 " ") | |
1280 (t (char-to-string code)))) | |
1281 str (substring str (match-end 0))))) | |
1282 (setq tmp (concat tmp str)) | |
1283 tmp)) | |
1284 | |
1285 (defun gnus-make-predicate (spec) | |
1286 "Transform SPEC into a function that can be called. | |
1287 SPEC is a predicate specifier that contains stuff like `or', `and', | |
1288 `not', lists and functions. The functions all take one parameter." | |
1289 `(lambda (elem) ,(gnus-make-predicate-1 spec))) | |
1290 | |
1291 (defun gnus-make-predicate-1 (spec) | |
1292 (cond | |
1293 ((symbolp spec) | |
1294 `(,spec elem)) | |
1295 ((listp spec) | |
1296 (if (memq (car spec) '(or and not)) | |
1297 `(,(car spec) ,@(mapcar 'gnus-make-predicate-1 (cdr spec))) | |
1298 (error "Invalid predicate specifier: %s" spec))))) | |
1299 | |
1300 (defun gnus-local-map-property (map) | |
1301 "Return a list suitable for a text property list specifying keymap MAP." | |
1302 (cond | |
1303 ((featurep 'xemacs) | |
1304 (list 'keymap map)) | |
1305 ((>= emacs-major-version 21) | |
1306 (list 'keymap map)) | |
1307 (t | |
1308 (list 'local-map map)))) | |
1309 | |
1310 (defmacro gnus-completing-read-maybe-default (prompt table &optional predicate | |
1311 require-match initial-contents | |
1312 history default) | |
1313 "Like `completing-read', allowing for non-existent 7th arg in older XEmacsen." | |
1314 `(completing-read ,prompt ,table ,predicate ,require-match | |
1315 ,initial-contents ,history | |
1316 ,@(if (and (featurep 'xemacs) (< emacs-minor-version 2)) | |
1317 () | |
1318 (list default)))) | |
1319 | |
1320 (defun gnus-completing-read (prompt table &optional predicate require-match | |
1321 history) | |
1322 (when (and history | |
1323 (not (boundp history))) | |
1324 (set history nil)) | |
1325 (gnus-completing-read-maybe-default | |
1326 (if (symbol-value history) | |
1327 (concat prompt " (" (car (symbol-value history)) "): ") | |
1328 (concat prompt ": ")) | |
1329 table | |
1330 predicate | |
1331 require-match | |
1332 nil | |
1333 history | |
1334 (car (symbol-value history)))) | |
1335 | |
1336 (defun gnus-graphic-display-p () | |
1337 (or (and (fboundp 'display-graphic-p) | |
1338 (display-graphic-p)) | |
1339 ;;;!!!This is bogus. Fixme! | |
1340 (and (featurep 'xemacs) | |
1341 t))) | |
1342 | |
1343 (put 'gnus-parse-without-error 'lisp-indent-function 0) | |
1344 (put 'gnus-parse-without-error 'edebug-form-spec '(body)) | |
1345 | |
1346 (defmacro gnus-parse-without-error (&rest body) | |
1347 "Allow continuing onto the next line even if an error occurs." | |
1348 `(while (not (eobp)) | |
1349 (condition-case () | |
1350 (progn | |
1351 ,@body | |
1352 (goto-char (point-max))) | |
1353 (error | |
1354 (gnus-error 4 "Invalid data on line %d" | |
1355 (count-lines (point-min) (point))) | |
1356 (forward-line 1))))) | |
1357 | |
1358 (defun gnus-cache-file-contents (file variable function) | |
1359 "Cache the contents of FILE in VARIABLE. The contents come from FUNCTION." | |
1360 (let ((time (nth 5 (file-attributes file))) | |
1361 contents value) | |
1362 (if (or (null (setq value (symbol-value variable))) | |
1363 (not (equal (car value) file)) | |
1364 (not (equal (nth 1 value) time))) | |
1365 (progn | |
1366 (setq contents (funcall function file)) | |
1367 (set variable (list file time contents)) | |
1368 contents) | |
1369 (nth 2 value)))) | |
1370 | |
1371 (defun gnus-multiple-choice (prompt choice &optional idx) | |
1372 "Ask user a multiple choice question. | |
1373 CHOICE is a list of the choice char and help message at IDX." | |
1374 (let (tchar buf) | |
1375 (save-window-excursion | |
1376 (save-excursion | |
1377 (while (not tchar) | |
1378 (message "%s (%s): " | |
1379 prompt | |
1380 (concat | |
1381 (mapconcat (lambda (s) (char-to-string (car s))) | |
1382 choice ", ") ", ?")) | |
1383 (setq tchar (read-char)) | |
1384 (when (not (assq tchar choice)) | |
1385 (setq tchar nil) | |
1386 (setq buf (get-buffer-create "*Gnus Help*")) | |
1387 (pop-to-buffer buf) | |
1388 (fundamental-mode) ; for Emacs 20.4+ | |
1389 (buffer-disable-undo) | |
1390 (erase-buffer) | |
1391 (insert prompt ":\n\n") | |
1392 (let ((max -1) | |
1393 (list choice) | |
1394 (alist choice) | |
1395 (idx (or idx 1)) | |
1396 (i 0) | |
1397 n width pad format) | |
1398 ;; find the longest string to display | |
1399 (while list | |
1400 (setq n (length (nth idx (car list)))) | |
1401 (unless (> max n) | |
1402 (setq max n)) | |
1403 (setq list (cdr list))) | |
1404 (setq max (+ max 4)) ; %c, `:', SPACE, a SPACE at end | |
1405 (setq n (/ (1- (window-width)) max)) ; items per line | |
1406 (setq width (/ (1- (window-width)) n)) ; width of each item | |
1407 ;; insert `n' items, each in a field of width `width' | |
1408 (while alist | |
1409 (if (< i n) | |
1410 () | |
1411 (setq i 0) | |
1412 (delete-char -1) ; the `\n' takes a char | |
1413 (insert "\n")) | |
1414 (setq pad (- width 3)) | |
1415 (setq format (concat "%c: %-" (int-to-string pad) "s")) | |
1416 (insert (format format (caar alist) (nth idx (car alist)))) | |
1417 (setq alist (cdr alist)) | |
1418 (setq i (1+ i)))))))) | |
1419 (if (buffer-live-p buf) | |
1420 (kill-buffer buf)) | |
1421 tchar)) | |
1422 | |
1423 (defun gnus-select-frame-set-input-focus (frame) | |
1424 "Select FRAME, raise it, and set input focus, if possible." | |
1425 (cond ((featurep 'xemacs) | |
1426 (raise-frame frame) | |
1427 (select-frame frame) | |
1428 (focus-frame frame)) | |
1429 ;; The function `select-frame-set-input-focus' won't set | |
1430 ;; the input focus under Emacs 21.2 and X window system. | |
1431 ;;((fboundp 'select-frame-set-input-focus) | |
1432 ;; (defalias 'gnus-select-frame-set-input-focus | |
1433 ;; 'select-frame-set-input-focus) | |
1434 ;; (select-frame-set-input-focus frame)) | |
1435 (t | |
1436 (raise-frame frame) | |
1437 (select-frame frame) | |
1438 (cond ((and (eq window-system 'x) | |
1439 (fboundp 'x-focus-frame)) | |
1440 (x-focus-frame frame)) | |
1441 ((eq window-system 'w32) | |
1442 (w32-focus-frame frame))) | |
1443 (when focus-follows-mouse | |
1444 (set-mouse-position frame (1- (frame-width frame)) 0))))) | |
1445 | |
1446 (defun gnus-frame-or-window-display-name (object) | |
1447 "Given a frame or window, return the associated display name. | |
1448 Return nil otherwise." | |
1449 (if (featurep 'xemacs) | |
1450 (device-connection (dfw-device object)) | |
1451 (if (or (framep object) | |
1452 (and (windowp object) | |
1453 (setq object (window-frame object)))) | |
1454 (let ((display (frame-parameter object 'display))) | |
1455 (if (and (stringp display) | |
1456 ;; Exclude invalid display names. | |
1457 (string-match "\\`[^:]*:[0-9]+\\(\\.[0-9]+\\)?\\'" | |
1458 display)) | |
1459 display))))) | |
1460 | |
1461 ;; Fixme: This has only one use (in gnus-agent), which isn't worthwhile. | |
1462 (defmacro gnus-mapcar (function seq1 &rest seqs2_n) | |
1463 "Apply FUNCTION to each element of the sequences, and make a list of the results. | |
1464 If there are several sequences, FUNCTION is called with that many arguments, | |
1465 and mapping stops as soon as the shortest sequence runs out. With just one | |
1466 sequence, this is like `mapcar'. With several, it is like the Common Lisp | |
1467 `mapcar' function extended to arbitrary sequence types." | |
1468 | |
1469 (if seqs2_n | |
1470 (let* ((seqs (cons seq1 seqs2_n)) | |
1471 (cnt 0) | |
1472 (heads (mapcar (lambda (seq) | |
1473 (make-symbol (concat "head" | |
1474 (int-to-string | |
1475 (setq cnt (1+ cnt)))))) | |
1476 seqs)) | |
1477 (result (make-symbol "result")) | |
1478 (result-tail (make-symbol "result-tail"))) | |
1479 `(let* ,(let* ((bindings (cons nil nil)) | |
1480 (heads heads)) | |
1481 (nconc bindings (list (list result '(cons nil nil)))) | |
1482 (nconc bindings (list (list result-tail result))) | |
1483 (while heads | |
1484 (nconc bindings (list (list (pop heads) (pop seqs))))) | |
1485 (cdr bindings)) | |
1486 (while (and ,@heads) | |
1487 (setcdr ,result-tail (cons (funcall ,function | |
1488 ,@(mapcar (lambda (h) (list 'car h)) | |
1489 heads)) | |
1490 nil)) | |
1491 (setq ,result-tail (cdr ,result-tail) | |
1492 ,@(apply 'nconc (mapcar (lambda (h) (list h (list 'cdr h))) heads)))) | |
1493 (cdr ,result))) | |
1494 `(mapcar ,function ,seq1))) | |
1495 | |
1496 (if (fboundp 'merge) | |
1497 (defalias 'gnus-merge 'merge) | |
1498 ;; Adapted from cl-seq.el | |
1499 (defun gnus-merge (type list1 list2 pred) | |
1500 "Destructively merge lists LIST1 and LIST2 to produce a new list. | |
1501 Argument TYPE is for compatibility and ignored. | |
1502 Ordering of the elements is preserved according to PRED, a `less-than' | |
1503 predicate on the elements." | |
1504 (let ((res nil)) | |
1505 (while (and list1 list2) | |
1506 (if (funcall pred (car list2) (car list1)) | |
1507 (push (pop list2) res) | |
1508 (push (pop list1) res))) | |
1509 (nconc (nreverse res) list1 list2)))) | |
1510 | |
1511 (eval-when-compile | |
1512 (defvar xemacs-codename) | |
1513 (defvar sxemacs-codename) | |
1514 (defvar emacs-program-version)) | |
1515 | |
1516 (defun gnus-emacs-version () | |
1517 "Stringified Emacs version." | |
1518 (let* ((lst (if (listp gnus-user-agent) | |
1519 gnus-user-agent | |
1520 '(gnus emacs type))) | |
1521 (system-v (cond ((memq 'config lst) | |
1522 system-configuration) | |
1523 ((memq 'type lst) | |
1524 (symbol-name system-type)) | |
1525 (t nil))) | |
1526 codename emacsname) | |
1527 (cond ((featurep 'sxemacs) | |
1528 (setq emacsname "SXEmacs" | |
1529 codename sxemacs-codename)) | |
1530 ((featurep 'xemacs) | |
1531 (setq emacsname "XEmacs" | |
1532 codename xemacs-codename)) | |
1533 (t | |
1534 (setq emacsname "Emacs"))) | |
1535 (cond | |
1536 ((not (memq 'emacs lst)) | |
1537 nil) | |
1538 ((string-match "^\\(\\([.0-9]+\\)*\\)\\.[0-9]+$" emacs-version) | |
1539 ;; Emacs: | |
1540 (concat "Emacs/" (match-string 1 emacs-version) | |
1541 (if system-v | |
1542 (concat " (" system-v ")") | |
1543 ""))) | |
1544 ((or (featurep 'sxemacs) (featurep 'xemacs)) | |
1545 ;; XEmacs or SXEmacs: | |
1546 (concat emacsname "/" emacs-program-version | |
1547 " (" | |
1548 (when (and (memq 'codename lst) | |
1549 codename) | |
1550 (concat codename | |
1551 (when system-v ", "))) | |
1552 (when system-v system-v) | |
1553 ")")) | |
1554 (t emacs-version)))) | |
1555 | |
1556 (defun gnus-rename-file (old-path new-path &optional trim) | |
1557 "Rename OLD-PATH as NEW-PATH. If TRIM, recursively delete | |
1558 empty directories from OLD-PATH." | |
1559 (when (file-exists-p old-path) | |
1560 (let* ((old-dir (file-name-directory old-path)) | |
1561 (old-name (file-name-nondirectory old-path)) | |
1562 (new-dir (file-name-directory new-path)) | |
1563 (new-name (file-name-nondirectory new-path)) | |
1564 temp) | |
1565 (gnus-make-directory new-dir) | |
1566 (rename-file old-path new-path t) | |
1567 (when trim | |
1568 (while (progn (setq temp (directory-files old-dir)) | |
1569 (while (member (car temp) '("." "..")) | |
1570 (setq temp (cdr temp))) | |
1571 (= (length temp) 0)) | |
1572 (delete-directory old-dir) | |
1573 (setq old-dir (file-name-as-directory | |
1574 (file-truename | |
1575 (concat old-dir ".."))))))))) | |
1576 | |
1577 (if (fboundp 'set-process-query-on-exit-flag) | |
1578 (defalias 'gnus-set-process-query-on-exit-flag | |
1579 'set-process-query-on-exit-flag) | |
1580 (defalias 'gnus-set-process-query-on-exit-flag | |
1581 'process-kill-without-query)) | |
1582 | |
1011 (provide 'gnus-util) | 1583 (provide 'gnus-util) |
1012 | 1584 |
1585 ;;; arch-tag: f94991af-d32b-4c97-8c26-ca12a934de49 | |
1013 ;;; gnus-util.el ends here | 1586 ;;; gnus-util.el ends here |