X-Git-Url: https://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fgnus-util.el;h=7e94d4e6f2942ad4e73f3a0e04a5a54a9098862c;hb=86720e148d6539b56265ba389b73ee4ce10d213e;hp=7d19be116a6f22e6ea7b19edd5d7124562902889;hpb=205af55cba1c8d2401115ab60abd0441c0f91154;p=gnus diff --git a/lisp/gnus-util.el b/lisp/gnus-util.el index 7d19be116..7e94d4e6f 100644 --- a/lisp/gnus-util.el +++ b/lisp/gnus-util.el @@ -1,6 +1,7 @@ ;;; gnus-util.el --- utility functions for Gnus -;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 -;; Free Software Foundation, Inc. + +;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +;; 2005, 2006, 2007 Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen ;; Keywords: news @@ -19,8 +20,8 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. ;;; Commentary: @@ -29,14 +30,22 @@ ;; used by Gnus and may be used by any other package without loading ;; Gnus first. +;; [Unfortunately, it does depend on other parts of Gnus, e.g. the +;; autoloads and defvars below...] + ;;; Code: -(require 'custom) (eval-when-compile (require 'cl) ;; Fixme: this should be a gnus variable, not nnmail-. - (defvar nnmail-pathname-coding-system)) -(require 'nnheader) + (defvar nnmail-pathname-coding-system) + (defvar nnmail-active-file-coding-system) + + ;; Inappropriate references to other parts of Gnus. + (defvar gnus-emphasize-whitespace-regexp) + (defvar gnus-original-article-buffer) + (defvar gnus-user-agent) + ) (require 'time-date) (require 'netrc) @@ -45,28 +54,27 @@ (autoload 'gnus-get-buffer-window "gnus-win") (autoload 'rmail-insert-rmail-file-header "rmail") (autoload 'rmail-count-new-messages "rmail") - (autoload 'rmail-show-message "rmail")) + (autoload 'rmail-show-message "rmail") + (autoload 'nnheader-narrow-to-headers "nnheader") + (autoload 'nnheader-replace-chars-in-string "nnheader")) (eval-and-compile (cond - ((fboundp 'replace-in-string) - (defalias 'gnus-replace-in-string 'replace-in-string)) + ;; Prefer `replace-regexp-in-string' (present in Emacs, XEmacs 21.5, + ;; SXEmacs 22.1.4) over `replace-in-string'. The later leads to inf-loops + ;; on empty matches: + ;; (replace-in-string "foo" "/*$" "/") + ;; (replace-in-string "xe" "\\(x\\)?" "") ((fboundp 'replace-regexp-in-string) (defun gnus-replace-in-string (string regexp newtext &optional literal) + "Replace all matches for REGEXP with NEWTEXT in STRING. +If LITERAL is non-nil, insert NEWTEXT literally. Return a new +string containing the replacements. + +This is a compatibility function for different Emacsen." (replace-regexp-in-string regexp newtext string nil literal))) - (t - (defun gnus-replace-in-string (string regexp newtext &optional literal) - (let ((start 0) tail) - (while (string-match regexp string start) - (setq tail (- (length string) (match-end 0))) - (setq string (replace-match newtext nil literal string)) - (setq start (- (length string) tail)))) - string)))) - -;;; bring in the netrc functions as aliases -(defalias 'gnus-netrc-get 'netrc-get) -(defalias 'gnus-netrc-machine 'netrc-machine) -(defalias 'gnus-parse-netrc 'netrc-parse) + ((fboundp 'replace-in-string) + (defalias 'gnus-replace-in-string 'replace-in-string)))) (defun gnus-boundp (variable) "Return non-nil if VARIABLE is bound and non-nil." @@ -95,7 +103,7 @@ (put 'gnus-eval-in-buffer-window 'edebug-form-spec '(form body)) (defmacro gnus-intern-safe (string hashtable) - "Set hash value. Arguments are STRING, VALUE, and HASHTABLE." + "Get hash value. Arguments are STRING and HASHTABLE." `(let ((symbol (intern ,string ,hashtable))) (or (boundp symbol) (set symbol nil)) @@ -104,17 +112,12 @@ ;; Added by Geoffrey T. Dairiki . A safe way ;; to limit the length of a string. This function is necessary since ;; `(substr "abc" 0 30)' pukes with "Args out of range". +;; Fixme: Why not `truncate-string-to-width'? (defsubst gnus-limit-string (str width) (if (> (length str) width) (substring str 0 width) str)) -(defsubst gnus-functionp (form) - "Return non-nil if FORM is funcallable." - (or (and (symbolp form) (fboundp form)) - (and (listp form) (eq (car form) 'lambda)) - (byte-code-function-p form))) - (defsubst gnus-goto-char (point) (and point (goto-char point))) @@ -124,15 +127,15 @@ (funcall (if (stringp buffer) 'get-buffer 'buffer-name) buffer)))) -(defalias 'gnus-point-at-bol - (if (fboundp 'point-at-bol) - 'point-at-bol - 'line-beginning-position)) - -(defalias 'gnus-point-at-eol - (if (fboundp 'point-at-eol) - 'point-at-eol - 'line-end-position)) +;; The LOCAL arg to `add-hook' is interpreted differently in Emacs and +;; XEmacs. In Emacs we don't need to call `make-local-hook' first. +;; It's harmless, though, so the main purpose of this alias is to shut +;; up the byte compiler. +(defalias 'gnus-make-local-hook + (if (eq (get 'make-local-hook 'byte-compile) + 'byte-compile-obsolete) + 'ignore ; Emacs + 'make-local-hook)) ; XEmacs (defun gnus-delete-first (elt list) "Delete by side effect the first occurrence of ELT as a member of LIST." @@ -148,7 +151,7 @@ ;; Delete the current line (and the next N lines). (defmacro gnus-delete-line (&optional n) - `(delete-region (progn (beginning-of-line) (point)) + `(delete-region (point-at-bol) (progn (forward-line ,(or n 1)) (point)))) (defun gnus-byte-code (func) @@ -161,12 +164,22 @@ (cons 'progn (cddr fval))))) (defun gnus-extract-address-components (from) + "Extract address components from a From header. +Given an RFC-822 address FROM, extract full name and canonical address. +Returns a list of the form (FULL-NAME CANONICAL-ADDRESS). Much more simple +solution than `mail-extract-address-components', which works much better, but +is slower." (let (name address) ;; First find the address - the thing with the @ in it. This may ;; not be accurate in mail addresses, but does the trick most of ;; the time in news messages. - (when (string-match "\\b[^@ \t<>]+[!@][^@ \t<>]+\\b" from) - (setq address (substring from (match-beginning 0) (match-end 0)))) + (cond (;; Check ``'' first in order to handle the quite common + ;; form ``"abc@xyz" '' (i.e. ``@'' as part of a comment) + ;; correctly. + (string-match "<\\([^@ \t<>]+[!@][^@ \t<>]+\\)>" from) + (setq address (substring from (match-beginning 1) (match-end 1)))) + ((string-match "\\b[^@ \t<>]+[!@][^@ \t<>]+\\b" from) + (setq address (substring from (match-beginning 0) (match-end 0))))) ;; Then we check whether the "name
" format is used. (and address ;; Linear white space is not required. @@ -193,8 +206,7 @@ "Return the value of the header FIELD of current article." (save-excursion (save-restriction - (let ((case-fold-search t) - (inhibit-point-motion-hooks t)) + (let ((inhibit-point-motion-hooks t)) (nnheader-narrow-to-headers) (message-fetch-field field))))) @@ -206,7 +218,7 @@ (defun gnus-goto-colon () (beginning-of-line) - (let ((eol (gnus-point-at-eol))) + (let ((eol (point-at-eol))) (goto-char (or (text-property-any (point) eol 'gnus-position t) (search-forward ":" eol t) (point))))) @@ -221,14 +233,16 @@ (defun gnus-remove-text-with-property (prop) "Delete all text in the current buffer with text property PROP." - (save-excursion - (goto-char (point-min)) - (while (not (eobp)) - (while (get-text-property (point) prop) - (delete-char 1)) - (goto-char (next-single-property-change (point) prop nil (point-max)))))) + (let ((start (point-min)) + end) + (unless (get-text-property start prop) + (setq start (next-single-property-change start prop))) + (while start + (setq end (text-property-any start (point-max) prop nil)) + (delete-region start (or end (point-max))) + (setq start (when end + (next-single-property-change start prop)))))) -(require 'nnheader) (defun gnus-newsgroup-directory-form (newsgroup) "Make hierarchical directory name from NEWSGROUP name." (let* ((newsgroup (gnus-newsgroup-savable-name newsgroup)) @@ -304,8 +318,8 @@ (defun gnus-completing-read-with-default (default prompt &rest args) ;; Like `completing-read', except that DEFAULT is the default argument. (let* ((prompt (if default - (concat prompt " (default " default ") ") - (concat prompt " "))) + (concat prompt " (default " default "): ") + (concat prompt ": "))) (answer (apply 'completing-read prompt args))) (if (or (null answer) (zerop (length answer))) default @@ -327,19 +341,20 @@ ;; age-depending date representations. (e.g. just the time if it's ;; from today, the day of the week if it's within the last 7 days and ;; the full date if it's older) + (defun gnus-seconds-today () - "Returns the number of seconds passed today" + "Return the number of seconds passed today." (let ((now (decode-time (current-time)))) (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)))) (defun gnus-seconds-month () - "Returns the number of seconds passed this month" + "Return the number of seconds passed this month." (let ((now (decode-time (current-time)))) (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) (* (- (car (nthcdr 3 now)) 1) 3600 24)))) (defun gnus-seconds-year () - "Returns the number of seconds passed this year" + "Return the number of seconds passed this year." (let ((now (decode-time (current-time))) (days (format-time-string "%j" (current-time)))) (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) @@ -370,30 +385,25 @@ seconds passed since the start of today, of this month, of this year, respectively.") (defun gnus-user-date (messy-date) - "Format the messy-date acording to gnus-user-date-format-alist. + "Format the messy-date according to gnus-user-date-format-alist. Returns \" ? \" if there's bad input or if an other error occurs. Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"." (condition-case () - (let* ((messy-date (safe-date-to-time messy-date)) - (now (current-time)) + (let* ((messy-date (time-to-seconds (safe-date-to-time messy-date))) + (now (time-to-seconds (current-time))) ;;If we don't find something suitable we'll use this one - (my-format "%b %m '%y") - (high (lsh (- (car now) (car messy-date)) 16))) - (if (and (> high -1) (= (logand high 65535) 0)) - ;;overflow and bad input - (let* ((difference (+ high (- (car (cdr now)) - (car (cdr messy-date))))) - (templist gnus-user-date-format-alist) - (top (eval (caar templist)))) - (while (if (numberp top) (< top difference) (not top)) - (progn - (setq templist (cdr templist)) - (setq top (eval (caar templist))))) - (if (stringp (cdr (car templist))) - (setq my-format (cdr (car templist)))))) - (format-time-string (eval my-format) messy-date)) + (my-format "%b %d '%y")) + (let* ((difference (- now messy-date)) + (templist gnus-user-date-format-alist) + (top (eval (caar templist)))) + (while (if (numberp top) (< top difference) (not top)) + (progn + (setq templist (cdr templist)) + (setq top (eval (caar templist))))) + (if (stringp (cdr (car templist))) + (setq my-format (cdr (car templist))))) + (format-time-string (eval my-format) (seconds-to-time messy-date))) (error " ? "))) -;;end of Frank's code (defun gnus-dd-mmm (messy-date) "Return a string like DD-MMM from a big messy string." @@ -455,12 +465,13 @@ jabbering all the time." :group 'gnus-start :type 'integer) -;; Show message if message has a lower level than `gnus-verbose'. -;; Guideline for numbers: -;; 1 - error messages, 3 - non-serious error messages, 5 - messages -;; for things that take a long time, 7 - not very important messages -;; on stuff, 9 - messages inside loops. (defun gnus-message (level &rest args) + "If LEVEL is lower than `gnus-verbose' print ARGS using `message'. + +Guideline for numbers: +1 - error messages, 3 - non-serious error messages, 5 - messages for things +that take a long time, 7 - not very important messages on stuff, 9 - messages +inside loops." (if (<= level gnus-verbose) (apply 'message args) ;; We have to do this format thingy here even if the result isn't @@ -469,7 +480,8 @@ jabbering all the time." (apply 'format args))) (defun gnus-error (level &rest args) - "Beep an error if LEVEL is equal to or less than `gnus-verbose'." + "Beep an error if LEVEL is equal to or less than `gnus-verbose'. +ARGS are passed to `message'." (when (<= (floor level) gnus-verbose) (apply 'message args) (ding) @@ -482,12 +494,23 @@ jabbering all the time." (defun gnus-split-references (references) "Return a list of Message-IDs in REFERENCES." (let ((beg 0) + (references (or references "")) ids) (while (string-match "<[^<]+[^< \t]" references beg) (push (substring references (match-beginning 0) (setq beg (match-end 0))) ids)) (nreverse ids))) +(defun gnus-extract-references (references) + "Return a list of Message-IDs in REFERENCES (in In-Reply-To + format), trimmed to only contain the Message-IDs." + (let ((ids (gnus-split-references references)) + refs) + (dolist (id ids) + (when (string-match "<[^<>]+>" id) + (push (match-string 0 id) refs))) + refs)) + (defsubst gnus-parent-id (references &optional n) "Return the last Message-ID in REFERENCES. If N, return the Nth ancestor instead." @@ -573,11 +596,22 @@ If N, return the Nth ancestor instead." (substring gname (match-end 0)) gname))) +(defmacro gnus-group-server (group) + "Find the server name of a foreign newsgroup. +For example, (gnus-group-server \"nnimap+yxa:INBOX.foo\") would +yield \"nnimap:yxa\"." + `(let ((gname ,group)) + (if (string-match "^\\([^:+]+\\)\\(?:\\+\\([^:]*\\)\\)?:" gname) + (format "%s:%s" (match-string 1 gname) (or + (match-string 2 gname) + "")) + (format "%s:%s" (car gnus-select-method) (cadr gnus-select-method))))) + (defun gnus-make-sort-function (funs) - "Return a composite sort condition based on the functions in FUNC." + "Return a composite sort condition based on the functions in FUNS." (cond ;; Just a simple function. - ((gnus-functionp funs) funs) + ((functionp funs) funs) ;; No functions at all. ((null funs) funs) ;; A list of functions. @@ -591,7 +625,7 @@ If N, return the Nth ancestor instead." (car funs)))) (defun gnus-make-sort-function-1 (funs) - "Return a composite sort condition based on the functions in FUNC." + "Return a composite sort condition based on the functions in FUNS." (let ((function (car funs)) (first 't1) (last 't2)) @@ -602,7 +636,7 @@ If N, return the Nth ancestor instead." (setq function (cadr function) first 't2 last 't1)) - ((gnus-functionp function) + ((functionp function) ;; Do nothing. ) (t @@ -618,24 +652,49 @@ If N, return the Nth ancestor instead." (define-key (symbol-value (intern (format "gnus-%s-mode-map" type))) [menu-bar edit] 'undefined)) +(defmacro gnus-bind-print-variables (&rest forms) + "Bind print-* variables and evaluate FORMS. +This macro is used with `prin1', `pp', etc. in order to ensure printed +Lisp objects are loadable. Bind `print-quoted' and `print-readably' +to t, and `print-escape-multibyte', `print-escape-newlines', +`print-escape-nonascii', `print-length', `print-level' and +`print-string-length' to nil." + `(let ((print-quoted t) + (print-readably t) + ;;print-circle + ;;print-continuous-numbering + print-escape-multibyte + print-escape-newlines + print-escape-nonascii + ;;print-gensym + print-length + print-level + print-string-length) + ,@forms)) + (defun gnus-prin1 (form) "Use `prin1' on FORM in the current buffer. -Bind `print-quoted' and `print-readably' to t while printing." - (let ((print-quoted t) - (print-readably t) - (print-escape-multibyte nil) - print-level print-length) - (prin1 form (current-buffer)))) +Bind `print-quoted' and `print-readably' to t, and `print-length' and +`print-level' to nil. See also `gnus-bind-print-variables'." + (gnus-bind-print-variables (prin1 form (current-buffer)))) (defun gnus-prin1-to-string (form) "The same as `prin1'. -Bind `print-quoted' and `print-readably' to t, and `print-length' -and `print-level' to nil." - (let ((print-quoted t) - (print-readably t) - (print-length nil) - (print-level nil)) - (prin1-to-string form))) +Bind `print-quoted' and `print-readably' to t, and `print-length' and +`print-level' to nil. See also `gnus-bind-print-variables'." + (gnus-bind-print-variables (prin1-to-string form))) + +(defun gnus-pp (form &optional stream) + "Use `pp' on FORM in the current buffer. +Bind `print-quoted' and `print-readably' to t, and `print-length' and +`print-level' to nil. See also `gnus-bind-print-variables'." + (gnus-bind-print-variables (pp form (or stream (current-buffer))))) + +(defun gnus-pp-to-string (form) + "The same as `pp-to-string'. +Bind `print-quoted' and `print-readably' to t, and `print-length' and +`print-level' to nil. See also `gnus-bind-print-variables'." + (gnus-bind-print-variables (pp-to-string form))) (defun gnus-make-directory (directory) "Make DIRECTORY (and all its parents) if it doesn't exist." @@ -659,6 +718,45 @@ and `print-level' to nil." (when (file-exists-p file) (delete-file file))) +(defun gnus-delete-directory (directory) + "Delete files in DIRECTORY. Subdirectories remain. +If there's no subdirectory, delete DIRECTORY as well." + (when (file-directory-p directory) + (let ((files (directory-files + directory t "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")) + file dir) + (while files + (setq file (pop files)) + (if (eq t (car (file-attributes file))) + ;; `file' is a subdirectory. + (setq dir t) + ;; `file' is a file or a symlink. + (delete-file file))) + (unless dir + (delete-directory directory))))) + +;; The following two functions are used in gnus-registry. +;; They were contributed by Andreas Fuchs . +(defun gnus-alist-to-hashtable (alist) + "Build a hashtable from the values in ALIST." + (let ((ht (make-hash-table + :size 4096 + :test 'equal))) + (mapc + (lambda (kv-pair) + (puthash (car kv-pair) (cdr kv-pair) ht)) + alist) + ht)) + +(defun gnus-hashtable-to-alist (hash) + "Build an alist from the values in HASH." + (let ((list nil)) + (maphash + (lambda (key value) + (setq list (cons (cons key value) list))) + hash) + list)) + (defun gnus-strip-whitespace (string) "Return STRING stripped of all whitespace." (while (string-match "[\r\n\t ]+" string) @@ -792,10 +890,31 @@ with potentially long computations." ;;; Functions for saving to babyl/mail files. -(defvar rmail-default-rmail-file) +(eval-when-compile + (condition-case nil + (progn + (require 'rmail) + (autoload 'rmail-update-summary "rmailsum")) + (error + (define-compiler-macro rmail-select-summary (&rest body) + ;; Rmail of the XEmacs version is supplied by the package, and + ;; requires tm and apel packages. However, there may be those + ;; who haven't installed those packages. This macro helps such + ;; people even if they install those packages later. + `(eval '(rmail-select-summary ,@body))) + ;; If there's rmail but there's no tm (or there's apel of the + ;; mainstream, not the XEmacs version), loading rmail of the XEmacs + ;; version fails halfway, however it provides the rmail-select-summary + ;; macro which uses the following functions: + (autoload 'rmail-summary-displayed "rmail") + (autoload 'rmail-maybe-display-summary "rmail"))) + (defvar rmail-default-rmail-file) + (defvar mm-text-coding-system)) + (defun gnus-output-to-rmail (filename &optional ask) "Append the current article to an Rmail file named FILENAME." (require 'rmail) + (require 'mm-util) ;; Most of these codes are borrowed from rmailout.el. (setq filename (expand-file-name filename)) (setq rmail-default-rmail-file filename) @@ -917,7 +1036,7 @@ with potentially long computations." (insert "\^_"))) (defun gnus-map-function (funs arg) - "Applies the result of the first function in FUNS to the second, and so on. + "Apply the result of the first function in FUNS to the second, and so on. ARG is passed to the first function." (while funs (setq arg (funcall (pop funs) arg))) @@ -928,6 +1047,13 @@ ARG is passed to the first function." (save-current-buffer (apply 'run-hooks funcs))) +(defun gnus-run-mode-hooks (&rest funcs) + "Run `run-mode-hooks' if it is available, otherwise `run-hooks'. +This function saves the current buffer." + (if (fboundp 'run-mode-hooks) + (save-current-buffer (apply 'run-mode-hooks funcs)) + (save-current-buffer (apply 'run-hooks funcs)))) + ;;; Various (defvar gnus-group-buffer) ; Compiler directive @@ -939,14 +1065,6 @@ ARG is passed to the first function." (set-buffer gnus-group-buffer) (eq major-mode 'gnus-group-mode)))) -(defun gnus-remove-duplicates (list) - (let (new) - (while list - (or (member (car list) new) - (setq new (cons (car list) new))) - (setq list (cdr list))) - (nreverse new))) - (defun gnus-remove-if (predicate list) "Return a copy of LIST with all items satisfying PREDICATE removed." (let (out) @@ -974,7 +1092,7 @@ Return the modified alist." `(setq ,alist (delq (,fun ,key ,alist) ,alist)))) (defun gnus-globalify-regexp (re) - "Returns a regexp that matches a whole line, iff RE matches a part of it." + "Return a regexp that matches a whole line, iff RE matches a part of it." (concat (unless (string-match "^\\^" re) "^.*") re (unless (string-match "\\$$" re) ".*$"))) @@ -1025,6 +1143,33 @@ Return the modified alist." (while (search-backward "\\." nil t) (delete-char 1))))) +;; Fixme: Why not use `with-output-to-temp-buffer'? +(defmacro gnus-with-output-to-file (file &rest body) + (let ((buffer (make-symbol "output-buffer")) + (size (make-symbol "output-buffer-size")) + (leng (make-symbol "output-buffer-length")) + (append (make-symbol "output-buffer-append"))) + `(let* ((,size 131072) + (,buffer (make-string ,size 0)) + (,leng 0) + (,append nil) + (standard-output + (lambda (c) + (aset ,buffer ,leng c) + + (if (= ,size (setq ,leng (1+ ,leng))) + (progn (write-region ,buffer nil ,file ,append 'no-msg) + (setq ,leng 0 + ,append t)))))) + ,@body + (when (> ,leng 0) + (let ((coding-system-for-write 'no-conversion)) + (write-region (substring ,buffer 0 ,leng) nil ,file + ,append 'no-msg)))))) + +(put 'gnus-with-output-to-file 'lisp-indent-function 1) +(put 'gnus-with-output-to-file 'edebug-form-spec '(form body)) + (if (fboundp 'union) (defalias 'gnus-union 'union) (defun gnus-union (l1 l2) @@ -1066,6 +1211,16 @@ Return the modified alist." (remove-text-properties start end properties object)) t)) +(defun gnus-string-remove-all-properties (string) + (condition-case () + (let ((s string)) + (set-text-properties 0 (length string) nil string) + s) + (error string))) + +;; This might use `compare-strings' to reduce consing in the +;; case-insensitive case, but it has to cope with null args. +;; (`string-equal' uses symbol print names.) (defun gnus-string-equal (x y) "Like `string-equal', except it compares case-insensitively." (and (= (length x) (length y)) @@ -1073,11 +1228,11 @@ Return the modified alist." (string-equal (downcase x) (downcase y))))) (defcustom gnus-use-byte-compile t - "If non-nil, byte-compile crucial run-time codes. -Setting it to nil has no effect after first time running -`gnus-byte-compile'." + "If non-nil, byte-compile crucial run-time code. +Setting it to nil has no effect after the first time `gnus-byte-compile' +is run." :type 'boolean - :version "21.1" + :version "22.1" :group 'gnus-various) (defun gnus-byte-compile (form) @@ -1089,15 +1244,18 @@ Setting it to nil has no effect after first time running (require 'byte-optimize) (error)) (require 'bytecomp) - (defalias 'gnus-byte-compile 'byte-compile) - (byte-compile form)) + (defalias 'gnus-byte-compile + (lambda (form) + (let ((byte-compile-warnings '(unresolved callargs redefine))) + (byte-compile form)))) + (gnus-byte-compile form)) form)) (defun gnus-remassoc (key alist) "Delete by side effect any elements of LIST whose car is `equal' to KEY. The modified LIST is returned. If the first member of LIST has a car that is `equal' to KEY, there is no way to remove it -by side effect; therefore, write `(setq foo (remassoc key foo))' to be +by side effect; therefore, write `(setq foo (gnus-remassoc key foo))' to be sure of changing the value of `foo'." (when alist (if (equal key (caar alist)) @@ -1134,12 +1292,12 @@ If you find some problem with the directory separator character, try (+ 10 (- x ?A))) (- x ?0))) +;; Fixme: Do it like QP. (defun gnus-url-unhex-string (str &optional allow-newlines) - "Remove %XXX embedded spaces, etc in a url. + "Remove %XX, embedded spaces, etc in a url. If optional second argument ALLOW-NEWLINES is non-nil, then allow the decoding of carriage returns and line feeds in the string, which is normally forbidden in URL encoding." - (setq str (or (mm-subst-char-in-string ?+ ? str) "")) (let ((tmp "") (case-fold-search t)) (while (string-match "%[0-9a-f][0-9a-f]" str) @@ -1174,16 +1332,6 @@ SPEC is a predicate specifier that contains stuff like `or', `and', `(,(car spec) ,@(mapcar 'gnus-make-predicate-1 (cdr spec))) (error "Invalid predicate specifier: %s" spec))))) -(defun gnus-local-map-property (map) - "Return a list suitable for a text property list specifying keymap MAP." - (cond - ((featurep 'xemacs) - (list 'keymap map)) - ((>= emacs-major-version 21) - (list 'keymap map)) - (t - (list 'local-map map)))) - (defun gnus-completing-read (prompt table &optional predicate require-match history) (when (and history @@ -1244,8 +1392,9 @@ CHOICE is a list of the choice char and help message at IDX." (while (not tchar) (message "%s (%s): " prompt - (mapconcat (lambda (s) (char-to-string (car s))) - choice ", ")) + (concat + (mapconcat (lambda (s) (char-to-string (car s))) + choice ", ") ", ?")) (setq tchar (read-char)) (when (not (assq tchar choice)) (setq tchar nil) @@ -1289,20 +1438,19 @@ CHOICE is a list of the choice char and help message at IDX." (defun gnus-select-frame-set-input-focus (frame) "Select FRAME, raise it, and set input focus, if possible." (cond ((featurep 'xemacs) - (raise-frame frame) - (select-frame frame) - (focus-frame frame)) - ;; The function `select-frame-set-input-focus' won't set - ;; the input focus under Emacs 21.2 and X window system. - ;;((fboundp 'select-frame-set-input-focus) - ;; (defalias 'gnus-select-frame-set-input-focus - ;; 'select-frame-set-input-focus) - ;; (select-frame-set-input-focus frame)) + (if (fboundp 'select-frame-set-input-focus) + (select-frame-set-input-focus frame) + (raise-frame frame) + (select-frame frame) + (focus-frame frame))) + ;; `select-frame-set-input-focus' defined in Emacs 21 will not + ;; set the input focus. + ((>= emacs-major-version 22) + (select-frame-set-input-focus frame)) (t (raise-frame frame) (select-frame frame) - (cond ((and (eq window-system 'x) - (fboundp 'x-focus-frame)) + (cond ((memq window-system '(x mac)) (x-focus-frame frame)) ((eq window-system 'w32) (w32-focus-frame frame))) @@ -1324,6 +1472,173 @@ Return nil otherwise." display)) display))))) +(eval-when-compile + (defvar tool-bar-mode)) + +(defun gnus-tool-bar-update (&rest ignore) + "Update the tool bar." + (when (and (boundp 'tool-bar-mode) + tool-bar-mode) + (let* ((args nil) + (func (cond ((featurep 'xemacs) + 'ignore) + ((fboundp 'tool-bar-update) + 'tool-bar-update) + ((fboundp 'force-window-update) + 'force-window-update) + ((fboundp 'redraw-frame) + (setq args (list (selected-frame))) + 'redraw-frame) + (t 'ignore)))) + (apply func args)))) + +;; Fixme: This has only one use (in gnus-agent), which isn't worthwhile. +(defmacro gnus-mapcar (function seq1 &rest seqs2_n) + "Apply FUNCTION to each element of the sequences, and make a list of the results. +If there are several sequences, FUNCTION is called with that many arguments, +and mapping stops as soon as the shortest sequence runs out. With just one +sequence, this is like `mapcar'. With several, it is like the Common Lisp +`mapcar' function extended to arbitrary sequence types." + + (if seqs2_n + (let* ((seqs (cons seq1 seqs2_n)) + (cnt 0) + (heads (mapcar (lambda (seq) + (make-symbol (concat "head" + (int-to-string + (setq cnt (1+ cnt)))))) + seqs)) + (result (make-symbol "result")) + (result-tail (make-symbol "result-tail"))) + `(let* ,(let* ((bindings (cons nil nil)) + (heads heads)) + (nconc bindings (list (list result '(cons nil nil)))) + (nconc bindings (list (list result-tail result))) + (while heads + (nconc bindings (list (list (pop heads) (pop seqs))))) + (cdr bindings)) + (while (and ,@heads) + (setcdr ,result-tail (cons (funcall ,function + ,@(mapcar (lambda (h) (list 'car h)) + heads)) + nil)) + (setq ,result-tail (cdr ,result-tail) + ,@(apply 'nconc (mapcar (lambda (h) (list h (list 'cdr h))) heads)))) + (cdr ,result))) + `(mapcar ,function ,seq1))) + +(if (fboundp 'merge) + (defalias 'gnus-merge 'merge) + ;; Adapted from cl-seq.el + (defun gnus-merge (type list1 list2 pred) + "Destructively merge lists LIST1 and LIST2 to produce a new list. +Argument TYPE is for compatibility and ignored. +Ordering of the elements is preserved according to PRED, a `less-than' +predicate on the elements." + (let ((res nil)) + (while (and list1 list2) + (if (funcall pred (car list2) (car list1)) + (push (pop list2) res) + (push (pop list1) res))) + (nconc (nreverse res) list1 list2)))) + +(eval-when-compile + (defvar xemacs-codename) + (defvar sxemacs-codename) + (defvar emacs-program-version)) + +(defun gnus-emacs-version () + "Stringified Emacs version." + (let* ((lst (if (listp gnus-user-agent) + gnus-user-agent + '(gnus emacs type))) + (system-v (cond ((memq 'config lst) + system-configuration) + ((memq 'type lst) + (symbol-name system-type)) + (t nil))) + codename emacsname) + (cond ((featurep 'sxemacs) + (setq emacsname "SXEmacs" + codename sxemacs-codename)) + ((featurep 'xemacs) + (setq emacsname "XEmacs" + codename xemacs-codename)) + (t + (setq emacsname "Emacs"))) + (cond + ((not (memq 'emacs lst)) + nil) + ((string-match "^\\(\\([.0-9]+\\)*\\)\\.[0-9]+$" emacs-version) + ;; Emacs: + (concat "Emacs/" (match-string 1 emacs-version) + (if system-v + (concat " (" system-v ")") + ""))) + ((or (featurep 'sxemacs) (featurep 'xemacs)) + ;; XEmacs or SXEmacs: + (concat emacsname "/" emacs-program-version + " (" + (when (and (memq 'codename lst) + codename) + (concat codename + (when system-v ", "))) + (when system-v system-v) + ")")) + (t emacs-version)))) + +(defun gnus-rename-file (old-path new-path &optional trim) + "Rename OLD-PATH as NEW-PATH. If TRIM, recursively delete +empty directories from OLD-PATH." + (when (file-exists-p old-path) + (let* ((old-dir (file-name-directory old-path)) + (old-name (file-name-nondirectory old-path)) + (new-dir (file-name-directory new-path)) + (new-name (file-name-nondirectory new-path)) + temp) + (gnus-make-directory new-dir) + (rename-file old-path new-path t) + (when trim + (while (progn (setq temp (directory-files old-dir)) + (while (member (car temp) '("." "..")) + (setq temp (cdr temp))) + (= (length temp) 0)) + (delete-directory old-dir) + (setq old-dir (file-name-as-directory + (file-truename + (concat old-dir ".."))))))))) + +(defun gnus-set-file-modes (filename mode) + "Wrapper for set-file-modes." + (ignore-errors + (set-file-modes filename mode))) + +(if (fboundp 'set-process-query-on-exit-flag) + (defalias 'gnus-set-process-query-on-exit-flag + 'set-process-query-on-exit-flag) + (defalias 'gnus-set-process-query-on-exit-flag + 'process-kill-without-query)) + +(if (fboundp 'with-local-quit) + (defalias 'gnus-with-local-quit 'with-local-quit) + (defmacro gnus-with-local-quit (&rest body) + "Execute BODY, allowing quits to terminate BODY but not escape further. +When a quit terminates BODY, `gnus-with-local-quit' returns nil but +requests another quit. That quit will be processed as soon as quitting +is allowed once again. (Immediately, if `inhibit-quit' is nil.)" + ;;(declare (debug t) (indent 0)) + `(condition-case nil + (let ((inhibit-quit nil)) + ,@body) + (quit (setq quit-flag t) + ;; This call is to give a chance to handle quit-flag + ;; in case inhibit-quit is nil. + ;; Without this, it will not be handled until the next function + ;; call, and that might allow it to exit thru a condition-case + ;; that intends to handle the quit signal next time. + (eval '(ignore nil)))))) + (provide 'gnus-util) +;;; arch-tag: f94991af-d32b-4c97-8c26-ca12a934de49 ;;; gnus-util.el ends here