;;; gnus-util.el --- utility functions for Gnus
;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;; 2005, 2006 Free Software Foundation, Inc.
+;; 2005, 2006, 2007 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: news
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
(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.
string containing the replacements.
This is a compatibility function for different Emacsen."
- (replace-regexp-in-string regexp newtext string nil literal)))))
+ (replace-regexp-in-string regexp newtext string nil literal)))
+ ((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."
(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))
symbol))
-;; Added by Geoffrey T. Dairiki <dairiki@u.washington.edu>. 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-goto-char (point)
(and point (goto-char point)))
;; 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 ``<foo@bar>'' first in order to handle the quite common
+ ;; form ``"abc@xyz" <foo@bar>'' (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 <address>" format is used.
(and address
;; Linear white space is not required.
(not (or (string< s1 s2)
(string= s1 s2))))
+(defun gnus-string< (s1 s2)
+ "Return t if first arg string is less than second in lexicographic order.
+Case is significant if and only if `case-fold-search' is nil.
+Symbols are also allowed; their print names are used instead."
+ (if case-fold-search
+ (string-lessp (downcase (if (symbolp s1) (symbol-name s1) s1))
+ (downcase (if (symbolp s2) (symbol-name s2) s2)))
+ (string-lessp s1 s2)))
+
;;; Time functions.
(defun gnus-file-newer-than (file date)
:group 'gnus-start
:type 'integer)
+(defcustom gnus-add-timestamp-to-message nil
+ "Non-nil means add timestamps to messages that Gnus issues.
+If it is `log', add timestamps to only the messages that go into the
+\"*Messages*\" buffer (in XEmacs, it is the \" *Message-Log*\" buffer).
+If it is neither nil nor `log', add timestamps not only to log messages
+but also to the ones displayed in the echo area."
+ :version "23.0" ;; No Gnus
+ :group 'gnus-various
+ :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
+ (const :tag "Logged messages only" log)
+ (sexp :tag "All messages"
+ :match (lambda (widget value) value)
+ :value t)
+ (const :tag "No timestamp" nil)))
+
+(eval-when-compile
+ (defmacro gnus-message-with-timestamp-1 (format-string args)
+ (let ((timestamp '((format-time-string "%Y%m%dT%H%M%S" time)
+ "." (format "%03d" (/ (nth 2 time) 1000)) "> ")))
+ (if (featurep 'xemacs)
+ `(let (str time)
+ (if (or (and (null ,format-string) (null ,args))
+ (progn
+ (setq str (apply 'format ,format-string ,args))
+ (zerop (length str))))
+ (prog1
+ (and ,format-string str)
+ (clear-message nil))
+ (cond ((eq gnus-add-timestamp-to-message 'log)
+ (setq time (current-time))
+ (display-message 'no-log str)
+ (log-message 'message (concat ,@timestamp str)))
+ (gnus-add-timestamp-to-message
+ (setq time (current-time))
+ (display-message 'message (concat ,@timestamp str)))
+ (t
+ (display-message 'message str))))
+ str)
+ `(let (str time)
+ (cond ((eq gnus-add-timestamp-to-message 'log)
+ (setq str (let (message-log-max)
+ (apply 'message ,format-string ,args)))
+ (when (and message-log-max
+ (> message-log-max 0)
+ (/= (length str) 0))
+ (setq time (current-time))
+ (with-current-buffer (get-buffer-create "*Messages*")
+ (goto-char (point-max))
+ (insert ,@timestamp str "\n")
+ (forward-line (- message-log-max))
+ (delete-region (point-min) (point))
+ (goto-char (point-max))))
+ str)
+ (gnus-add-timestamp-to-message
+ (if (or (and (null ,format-string) (null ,args))
+ (progn
+ (setq str (apply 'format ,format-string ,args))
+ (zerop (length str))))
+ (prog1
+ (and ,format-string str)
+ (message nil))
+ (setq time (current-time))
+ (message "%s" (concat ,@timestamp str))
+ str))
+ (t
+ (apply 'message ,format-string ,args))))))))
+
+(defun gnus-message-with-timestamp (format-string &rest args)
+ "Display message with timestamp. Arguments are the same as `message'.
+The `gnus-add-timestamp-to-message' variable controls how to add
+timestamp to message."
+ (gnus-message-with-timestamp-1 format-string args))
+
(defun gnus-message (level &rest args)
"If LEVEL is lower than `gnus-verbose' print ARGS using `message'.
that take a long time, 7 - not very important messages on stuff, 9 - messages
inside loops."
(if (<= level gnus-verbose)
- (apply 'message args)
+ (if gnus-add-timestamp-to-message
+ (apply 'gnus-message-with-timestamp args)
+ (apply 'message args))
;; We have to do this format thingy here even if the result isn't
;; shown - the return value has to be the same as the return value
;; from `message'.
(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))
+ (let ((ids (gnus-split-references references))
refs)
(dolist (id ids)
(when (string-match "<[^<>]+>" id)
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) (match-string 2 gname))
+ (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)
(defun gnus-write-buffer (file)
"Write the current buffer's contents to FILE."
- ;; Make sure the directory exists.
- (gnus-make-directory (file-name-directory file))
(let ((file-name-coding-system nnmail-pathname-coding-system))
+ ;; Make sure the directory exists.
+ (gnus-make-directory (file-name-directory file))
;; Write the buffer.
(write-region (point-min) (point-max) file nil 'quietly)))
(unless dir
(delete-directory directory)))))
+;; The following two functions are used in gnus-registry.
+;; They were contributed by Andreas Fuchs <asf@void.at>.
+(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)
`(setq ,alist (delq (,fun ,key ,alist) ,alist))))
(defun gnus-globalify-regexp (re)
- "Return a regexp that matches a whole line, iff RE matches a part of it."
+ "Return a regexp that matches a whole line, if RE matches a part of it."
(concat (unless (string-match "^\\^" re) "^.*")
re
(unless (string-match "\\$$" re) ".*$")))
t))
(defun gnus-write-active-file (file hashtb &optional full-names)
+ ;; `coding-system-for-write' should be `raw-text' or equivalent.
(let ((coding-system-for-write nnmail-active-file-coding-system))
(with-temp-file file
+ ;; The buffer should be in the unibyte mode because group names
+ ;; are ASCII text or encoded non-ASCII text (i.e., unibyte).
+ (mm-disable-multibyte)
(mapatoms
(lambda (sym)
(when (and sym
(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-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)))
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.
((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)
- ")"))
+ (let (plst)
+ (when (memq 'codename lst)
+ (push codename plst))
+ (when system-v
+ (push system-v plst))
+ (unless (featurep 'mule)
+ (push "no MULE" plst))
+ (when (> (length plst) 0)
+ (concat
+ " (" (mapconcat 'identity (reverse plst) ", ") ")")))))
(t emacs-version))))
(defun gnus-rename-file (old-path new-path &optional trim)
(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