+2014-12-20 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * registry.el (cl-remf, cl-loop, cl-subseq):
+ Alias to remf, loop, and subseq respectively for old Emacsen.
+
+2014-12-18 Paul Eggert <eggert@cs.ucla.edu>
+
+ * registry.el (registry-db): Set default slot later.
+ This is because its value is not a literal integer.
+
+2014-12-18 Teodor Zlatanov <tzz@lifelogs.com>
+
+ * registry.el (registry-db): Fix default registry-db max-size.
+
+2014-12-18 Stefan Monnier <monnier@iro.umontreal.ca>
+
+ * mm-util.el (mm-with-unibyte-current-buffer): Mark obsolete and
+ add warning.
+
+ * gnus-art.el: Fix up compiler warnings.
+ (article-display-face, article-display-x-face): Remove unused `face'.
+ (gnus-article-browse-html-save-cid-content): Remove unused var `type'.
+ (article-date-ut): Remove unused var `first'.
+ (gnus-article-prepare): Remove unused var `gnus-article'.
+ (gnus-mime-save-part-and-strip): Remove unused var `param'.
+ (gnus-mime-inline-part): Remove unused vars `charset', `contents', and
+ `coding-system' along with corresponding dead code.
+ (gnus-mime-view-part-externally): Remove unused var
+ `mm-user-display-methods'.
+ (gnus-insert-mime-button): Let-bind gnus-tmp-id explicitly.
+ (gnus-display-mime): Remove unused var `handle'.
+ (gnus-mime-display-alternative): Remove unused var `props'.
+ (gnus-article-read-summary-keys): Remove unused var `up-to-top'.
+ (gnus-article-edit-done): Remove unused var `p'.
+ (gnus-url-mailto): Remove unused var `to'.
+ (gnus-treat-article): Let-bind gnus-treat-condition, part-number,
+ total-parts, and gnus-treat-type explicitly. Remove unused var `elem'.
+
+2014-12-18 Eric Abrahamsen <eric@ericabrahamsen.net>
+
+ * registry.el (registry-db): Consolidate the :max-hard and :max-soft
+ slots into a :max-size slot.
+ (registry-db-version): Add new variable for database version number.
+ (registry-prune): Use :max-size slot. Accept and use a sort-function
+ argument.
+ (registry-collect-prune-candidates): Add new function for finding
+ non-precious pruning candidates.
+ (registry-prune-hard-candidates, registry-prune-soft-candidates):
+ Remove obsolete functions.
+ (initialize-instance): Upgrade registry version when starting.
+
+ * gnus-registry.el (gnus-registry-prune-factor): Add new variable.
+ (gnus-registry-max-pruned-entries): Remove obsolete variable.
+ (gnus-registry-cache-file): Change default
+ filename extension to "eieio".
+ (gnus-registry-read): Add new function, split out from
+ `gnus-registry-load', that does the actual object reading.
+ (gnus-registry-load): Use it. Add condition case handler to check for
+ old filename extension and rename to the new one.
+ (gnus-registry-default-sort-function): New variable to specify a sort
+ function to use when pruning.
+ (gnus-registry-save, gnus-registry-insert): Use it.
+ (gnus-registry-sort-by-creation-time): Define a default sort function.
+
+ * tests/gnustest-registry.el (gnustest-registry-make-testable-db):
+ Adjust test for new object signature.
+ (gnustest-registry-pruning-test): Add new pruning test.
+ (gnustest-registry-sort-function): Default sort function for testing.
+ (gnustest-registry-pruning-sort-test): New test for sorting.
+
+2014-12-09 Lars Magne Ingebrigtsen <larsi@gnus.org>
+
+ * gnus-art.el (gnus-article-mime-handles): Refactored out into own
+ function for reuse.
+ (gnus-mime-buttonize-attachments-in-header): Adjusted.
+
+2014-12-07 Lars Magne Ingebrigtsen <larsi@gnus.org>
+
+ * message.el (message-change-subject): Really check whether the subject
+ changed.
+
+2014-12-05 Lars Magne Ingebrigtsen <larsi@gnus.org>
+
+ * mailcap.el (mailcap-mime-data): Add doc-view-mode as a viewer for
+ PDFs.
+ (mailcap-view-mime): New function.
+
+2014-12-01 Glenn Morris <rgm@gnu.org>
+
+ * gnus-cloud.el (gnus-cloud): Add :version tag.
+
+2014-11-27 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * gnus-art.el (gnus-use-idna):
+ * gnus-sum.el (gnus-summary-idna-message):
+ * message.el (message-use-idna):
+ Protect against nil value for idna-program.
+
+ * message.el (message-use-idna): Load Mule-UCS for XEmacs 21.4.
+
+2014-11-26 John Mastro <john.b.mastro@gmail.com> (tiny change)
+
+ * auth-source.el (auth-source-macos-keychain-search-items): Return
+ result of `auth-source-macos-keychain-result-append' (bug#19074).
+
+2014-11-25 Glenn Morris <rgm@gnu.org>
+
+ * gnus-start.el (gnus-save-newsrc-file-check-timestamp):
+ Add :version tag.
+
+2014-11-23 Lars Magne Ingebrigtsen <larsi@gnus.org>
+
+ * pop3.el (pop3-open-server): Warn unless encrypted.
+
+ * nnimap.el (nnimap-open-connection-1): Warn unless encrypted.
+
+2014-11-18 Paul Eggert <eggert@cs.ucla.edu>
+
+ Port new time stamp handling to Emacs 23.2.
+ This fix is for Gnus. Problem reported by Katsumi Yamaoka.
+ * time-date.el (time-add, time-subtract, time-less-p):
+ Use eval-and-compile, not eval-when-compile.
+
+2014-11-17 Albert Krewinkel <albert@zeitkraut.de>
+
+ * message.el (message-valid-fqdn-regexp): Add non-internaional new
+ TLDs.
+
+2014-11-17 Paul Eggert <eggert@cs.ucla.edu>
+
+ Port new time stamp handling to old Emacs and to XEmacs.
+ This is needed for Gnus, which copies time-date.el and which
+ runs on older Emacs implementations.
+ * time-date.el (with-decoded-time-value):
+ Handle 'nil' and floating-point arg more compatibly with new Emacs.
+ (encode-time-value, with-decoded-time-value):
+ Obsolete only if new Emacs.
+ (time-add, time-subtract, time-less-p): Define if not new Emacs.
+
+ Improve time stamp handling, and be more consistent about it.
+ This implements a suggestion made in:
+ http://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00587.html
+ Among other things, this means timer.el no longer needs to
+ autoload the time-date module.
+ * time-date.el (seconds-to-time, days-to-time, time-since)
+ (with-decoded-time-value):
+ Treat 'nil' as current time. This is mostly for XEmacs.
+ (encode-time-value, with-decoded-time-value): Obsolete.
+ (time-add, time-subtract, time-less-p): Use no-op autoloads, for
+ XEmacs. Define only if XEmacs, as they're now C builtins in Emacs.
+
+2014-11-14 Lars Magne Ingebrigtsen <larsi@gnus.org>
+
+ * gnus-sum.el (gnus-summary-exit-no-update): Don't query about
+ discarding changes in ephemeral groups.
+
+ * ietf-drums.el (ietf-drums-parse-address): Don't issue warnings about
+ things the user isn't interested in.
+
+2014-11-13 Julien Danjou <jd@abydos>
+
+ * gnus-notifications.el (gnus-notifications-notify): Provide both
+ app-icon and image-path.
+
+2014-11-10 Kenjiro NAKAYAMA <nakayamakenjiro@gmail.com>
+
+ * mm-url.el (mm-url-encode-multipart-form-data):
+ Restore to handle "multipart/form-data" by eww.
+
+2014-11-07 Tassilo Horn <tsdh@gnu.org>
+
+ * gnus-start.el (gnus-activate-group): Fix typo reported by Tim
+ Landscheidt.
+
+2014-10-29 Paul Eggert <eggert@cs.ucla.edu>
+
+ Simplify use of current-time and friends.
+ * gnus-delay.el (gnus-delay-article):
+ * gnus-sum.el (gnus-summary-read-document):
+ * gnus-util.el (gnus-seconds-today, gnus-seconds-month):
+ * message.el (message-make-expires-date):
+ Omit unnecessary call to current-time.
+ * gnus-util.el (gnus-float-time): Simplify to an alias because
+ time-to-seconds now behaves like float-time with respect to nil arg.
+ (gnus-seconds-year): Don't call current-time twice to get the current
+ time stamp, as this can lead to inconsistent results.
+ * time-date.el (time-to-seconds) [!float-time]:
+ Use current time if arg is nil, to be compatible with float-time.
+ (time-date--day-in-year): New function, with most of the guts of
+ the old time-to-day-in-year.
+ (time-to-day-in-year): Use it.
+ (time-to-days): Use it, to avoid decoding the same time stamp twice.
+
+2014-10-27 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * gnus.el (gnus-mode-line-buffer-identification):
+ Don't add image data for a non-graphic display (bug#18813).
+
+2014-10-24 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * gnus.el (gnus-mode-line-buffer-identification): Don't shadow
+ load-path, it blocks autoloading of find-image (bug#18813).
+
+2014-10-24 enami tsugutomo <tsugutomo.enami@jp.sony.com>
+
+ * nnimap.el (nnimap-wait-for-response): Ignore NOOP response requested
+ to keep connection open (bug#18728).
+
+2014-10-20 Glenn Morris <rgm@gnu.org>
+
+ * Merge in all changes up to 24.4 release.
+
+2014-10-15 Jorge A. Alfaro-Murillo <jorge.alfaro-murillo@yale.edu> (tiny change)
+
+ * message.el (message-insert-signature): Use `newline' instead of
+ inserting explicit "\n".
+
+2014-10-15 Sylvain Chouleur <sylvain.chouleur@gmail.com>
+
+ * gnus-icalendar.el: Support vcal format timezones.
+ (gnus-icalendar-event--decode-datefield): Use icalendar functions to
+ compute dates with associated timezone.
+ (gnus-icalendar-event-from-ical): Compute all timezones.
+
+2014-10-14 Teodor Zlatanov <tzz@lifelogs.com>
+
+ * gnus-start.el (gnus-save-newsrc-file-check-timestamp): New option to
+ check the newsrc.eld file's timestamp before saving it.
+ (gnus-save-newsrc-file): Use it, with a prompt when the newsrc.eld
+ timestamp has changed to be newer.
+
+2014-10-06 Jan Tatarik <jan.tatarik@gmail.com>
+
+ * gnus-icalendar.el (gnus-icalendar-identities):
+ Include message-alternative-emails.
+
+2014-10-05 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * ntlm.el (ntlm-string-make-unibyte, ntlm-secure-hash):
+ New compatibility functions.
+ (ntlm-build-auth-response): Use them.
+
+2014-10-04 Thomas Fitzsimmons <fitzsim@fitzsim.org>
+
+ * ntlm.el (ntlm-build-auth-request):
+ Add NTLM2 Session support. (Bug#15603)
+
+2014-10-04 Alan Schmitt <alan.schmitt@polytechnique.org> (tiny change)
+
+ * nnimap.el (nnimap-process-expiry-targets): Reverse the list of
+ expired messages only when it was built in reverse order.
+
+2014-10-04 Peter Münster <pmlists@free.fr> (tiny change)
+
+ * gnus-delay.el (gnus-delay-send-queue): Remove `gnus-delay-header'
+ last so it can be used in `message-send-hook'.
+
+2014-09-29 Daiki Ueno <ueno@gnu.org>
+
+ * mml.el (mml-parse-1): Error out if unknown mode is specified in
+ <#secure> tag (bug#18513).
+
+2014-09-27 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * parse-time.el: Require cl when compiling.
+
+2014-09-26 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ Use cl-lib as much as possible following the 2014-09-26 change
+ in the Emacs trunk.
+ * parse-time.el: Try requiring cl-lib.
+ (parse-time-incf): Alias to cl-incf or incf.
+ (digit-char-p): Remove.
+ (parse-time-integer): Alias to cl-parse-integer or the one defined.
+ (parse-integer): Rename to parse-time-integer.
+ (parse-time-tokenize, parse-time-rules, parse-time-string)
+ Use parse-time-incf and parse-time-integer.
+
+2014-09-11 Paul Eggert <eggert@cs.ucla.edu>
+
+ * gnus-cloud.el (gnus-cloud-parse-version-1): Fix misspelling
+ of ":delete".
+
2014-08-26 Katsumi Yamaoka <yamaoka@jpl.org>
* gnus-art.el (gnus-article-browse-html-save-cid-content)
* gnus-icalendar.el (gnus-icalendar-event:org-timestamp): Fix
org-timestamp for events ending at midnight.
-2013-11-21 Ivan Shmakov <ivan@siamics.net> (tiny change)
+2013-11-21 Ivan Shmakov <ivan@siamics.net>
* nndoc.el (nndoc-type-alist, nndoc-debbugs-db-type-p): Support debbugs
.log files.
* gnus-group.el: Require gnus-sum and autoload functions to
resolve warnings when gnus-group.el compiled alone.
- (gnus-group-line-format): Documented new %F.
+ (gnus-group-line-format): Document new %F.
(size of Fetched data) group line format; identifies disk space
used by agent and cache.
- (gnus-group-line-format-alist): Defined new F format.
+ (gnus-group-line-format-alist): Define new F format.
(gnus-total-fetched-for): New function.
(gnus-group-delete-group): No longer update
gnus-cache-active-altered as gnus-request-delete-group now keeps
(while (not (eobp))
(cond
((looking-at "^password: \"\\(.+\\)\"$")
- (auth-source-macos-keychain-result-append
- ret
- keychain-generic
- "secret"
- (lexical-let ((v (match-string 1)))
- (lambda () v))))
+ (setq ret (auth-source-macos-keychain-result-append
+ ret
+ keychain-generic
+ "secret"
+ (lexical-let ((v (match-string 1)))
+ (lambda () v)))))
;; TODO: check if this is really the label
;; match 0x00000007 <blob>="AppleID"
((looking-at "^[ ]+0x00000007 <blob>=\"\\(.+\\)\"")
- (auth-source-macos-keychain-result-append
- ret
- keychain-generic
- "label"
- (match-string 1)))
+ (setq ret (auth-source-macos-keychain-result-append
+ ret
+ keychain-generic
+ "label"
+ (match-string 1))))
;; match "crtr"<uint32>="aapl"
;; match "svce"<blob>="AppleID"
((looking-at "^[ ]+\"\\([a-z]+\\)\"[^=]+=\"\\(.+\\)\"")
- (auth-source-macos-keychain-result-append
- ret
- keychain-generic
- (match-string 1)
- (match-string 2))))
- (forward-line)))
+ (setq ret (auth-source-macos-keychain-result-append
+ ret
+ keychain-generic
+ (match-string 1)
+ (match-string 2)))))
+ (forward-line)))
;; return `ret' iff it has the :secret key
(and (plist-get ret :secret) (list ret))))
:type 'string
:group 'mime-security)
+(defvar idna-program)
+
(defcustom gnus-use-idna (and (condition-case nil (require 'idna) (file-error))
(mm-coding-system-p 'utf-8)
+ idna-program
(executable-find idna-program))
"Whether IDNA decoding of headers is used when viewing messages.
This requires GNU Libidn, and by default only enabled if it is found."
(incf i)))
i))
-(defun article-hide-headers (&optional arg delete)
+(defun article-hide-headers (&optional _arg _delete)
"Hide unwanted headers and possibly sort them as well."
(interactive)
;; This function might be inhibited.
(if (and wash-face-p (memq 'face gnus-article-wash-types))
(gnus-delete-images 'face)
(let ((from (message-fetch-field "from"))
- face faces)
+ faces)
(save-current-buffer
(when (and wash-face-p
(gnus-buffer-live-p gnus-original-article-buffer)
(gnus-delete-images 'xface)
;; Display X-Faces.
(let ((from (message-fetch-field "from"))
- x-faces face)
+ x-faces)
(save-current-buffer
(when (and wash-face-p
(gnus-buffer-live-p gnus-original-article-buffer)
"Find CID content in HANDLES and save it in a file in DIRECTORY.
Return file name."
(save-match-data
- (let (file type)
+ (let (file)
(catch 'found
(dolist (handle handles)
(cond
(gnus-summary-show-article)))))
(defun article-hide-list-identifiers ()
- "Remove list identifies from the Subject header.
+ "Remove list identifiers from the Subject header.
The `gnus-list-identifiers' variable specifies what to do."
(interactive)
(let ((inhibit-point-motion-hooks t)
'hidden
nil)))
-(defun gnus-article-show-hidden-text (type &optional dummy)
+(defun gnus-article-show-hidden-text (type &optional _dummy)
"Show all hidden text of type TYPE.
Originally it is hide instead of DUMMY."
(let ((inhibit-read-only t)
gnus-article-date-headers)
t))
-(defun article-date-ut (&optional type highlight date-position)
+(defun article-date-ut (&optional type _highlight date-position)
"Convert DATE date to TYPE in the current article.
The default type is `ut'. See `gnus-article-date-headers' for
possible values."
(let* ((case-fold-search t)
(inhibit-read-only t)
(inhibit-point-motion-hooks t)
- (first t)
(visible-date (mail-fetch-field "Date"))
pos date bface eface)
(save-excursion
(set dir-var (file-name-directory result)))
result))
-(defun gnus-article-archive-name (group)
+(defun gnus-article-archive-name (_group)
"Return the first instance of an \"Archive-name\" in the current buffer."
(let ((case-fold-search t))
(when (re-search-forward "archive-name: *\\([^ \n\t]+\\)[ \t]*$" nil t)
default
(or last-file default))))
-(defun gnus-plain-save-name (newsgroup headers &optional last-file)
+(defun gnus-plain-save-name (newsgroup _headers &optional last-file)
"Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
If variable `gnus-use-long-file-name' is non-nil, it is
~/News/news.group. Otherwise, it is like ~/News/news/group/news."
default-directory))
gnus-article-save-directory)))
-(defun gnus-sender-save-name (newsgroup headers &optional last-file)
+(defun gnus-sender-save-name (_newsgroup headers &optional _last-file)
"Generate file name from sender."
(let ((from (mail-header-from headers)))
(expand-file-name
(substitute-key-definition
'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
+(defvar gnus-article-send-map)
+
(gnus-define-keys (gnus-article-send-map "S" gnus-article-mode-map)
"W" gnus-article-wide-reply-with-original)
(if (featurep 'xemacs)
(forward-line line)
(point)))))))
-(defun gnus-article-prepare (article &optional all-headers header)
+(defvar gnus-tmp-internal-hook)
+
+(defun gnus-article-prepare (article &optional all-headers _header)
"Prepare ARTICLE in article mode buffer.
ARTICLE should either be an article number or a Message-ID.
If ARTICLE is an id, HEADER should be the article headers.
If ALL-HEADERS is non-nil, no headers are hidden."
- (save-excursion
+ (save-excursion ;FIXME: Shouldn't that be save-current-buffer?
;; Make sure we start in a summary buffer.
(unless (derived-mode-p 'gnus-summary-mode)
(set-buffer gnus-summary-buffer))
(setq gnus-summary-buffer (current-buffer))
- (let* ((gnus-article (if header (mail-header-number header) article))
- (summary-buffer (current-buffer))
+ (let* ((summary-buffer (current-buffer))
(gnus-tmp-internal-hook gnus-article-internal-prepare-hook)
(group gnus-newsgroup-name)
result)
(gnus-run-hooks 'gnus-article-prepare-hook)
t))))))
+(defvar gnus-mime-display-attachment-buttons-in-header)
+
;;;###autoload
(defun gnus-article-prepare-display ()
"Make the current buffer look like a nice article."
General format specifiers can also be used. See Info node
`(gnus)Formatting Variables'.")
+(defvar gnus-tmp-type)
+(defvar gnus-tmp-type-long)
+(defvar gnus-tmp-name)
+(defvar gnus-tmp-description)
+(defvar gnus-tmp-id)
+(defvar gnus-tmp-length)
+(defvar gnus-tmp-dots)
+(defvar gnus-tmp-info)
+(defvar gnus-tmp-pressed-details)
+
(defvar gnus-mime-button-line-format-alist
'((?t gnus-tmp-type ?s)
(?T gnus-tmp-type-long ?s)
The current article has a complicated MIME structure, giving up..."))
(let* ((data (get-text-property (point) 'gnus-data))
(id (get-text-property (point) 'gnus-part))
- param
(handles gnus-article-mime-handles))
(unless file
(setq file
(text-property-any (point-min) (point) 'gnus-data handle)))
(setq handle (get-text-property b 'gnus-data))
b))
- start contents charset coding-system)
+ start)
(when handle
(when (= b (prog1
btn
(setq b btn))
(if (and (not arg) (mm-handle-undisplayer handle))
(mm-remove-part handle)
- (mm-with-unibyte-buffer
- (mm-insert-part handle)
- (setq contents
- (or (mm-decompress-buffer (mm-handle-filename handle) nil t)
- (buffer-string))))
(cond
- ((not arg)
- (unless (setq charset (mail-content-type-get
- (mm-handle-type handle) 'charset))
- (unless (setq coding-system
- (mm-with-unibyte-buffer
- (insert contents)
- (mm-find-buffer-file-coding-system)))
- (setq charset gnus-newsgroup-charset))))
+ ((not arg) nil)
((numberp arg)
(if (mm-handle-undisplayer handle)
- (mm-remove-part handle))
- (setq charset
- (or (cdr (assq arg
- gnus-summary-show-article-charset-alist))
- (mm-read-coding-system "Charset: "))))
+ (mm-remove-part handle)))
((mm-handle-undisplayer handle)
(mm-remove-part handle)))
(goto-char start)
(interactive)
(gnus-article-check-buffer)
(let* ((handle (or handle (get-text-property (point) 'gnus-data)))
- (mm-user-display-methods nil)
(mm-inlined-types nil)
(mail-parse-charset gnus-newsgroup-charset)
(mail-parse-ignored-charsets
(when gnus-break-pages
(gnus-narrow-to-page))))
-(defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
+(defun gnus-insert-mime-button (handle id &optional displayed)
(let ((gnus-tmp-name
(or (mm-handle-filename handle)
(mail-content-type-get (mm-handle-type handle) 'url)
""))
+ (gnus-tmp-id id)
(gnus-tmp-type (mm-handle-media-type handle))
(gnus-tmp-description (or (mm-handle-description handle) ""))
(gnus-tmp-dots
"hide" "show")
(aref gnus-down-mouse-3 0))))))
-(defun gnus-widget-press-button (elems el)
+(defun gnus-widget-press-button (elems _el)
(goto-char (widget-get elems :from))
(gnus-article-press-button))
;; may change the point. So we set the window point.
(set-window-point window point)))
(let ((handles ihandles)
- (inhibit-read-only t)
- handle)
+ (inhibit-read-only t))
(cond (handles)
((setq handles (mm-dissect-buffer nil gnus-article-loose-mime))
(when gnus-article-emulate-mime
overlook them if there is a huge body. This option offers you a copy
of all non-inlinable MIME parts as buttons shown in front of an article.
If nil, don't show those extra buttons."
- :version "24.5"
+ :version "25.1"
:group 'gnus-article-mime
:type 'boolean)
(let* ((preferred (or preferred (mm-preferred-alternative handles)))
(ihandles handles)
(point (point))
- handle (inhibit-read-only t) from props begend not-pref)
+ handle (inhibit-read-only t) from begend not-pref)
(save-window-excursion
(save-restriction
(when ibegend
(when image
(gnus-add-image 'shr image))))
+(defun gnus-article-mime-handles (&optional alist id all)
+ (if alist
+ (let ((i 1) newid flat)
+ (dolist (handle alist flat)
+ (setq newid (append id (list i))
+ i (1+ i))
+ (if (stringp (car handle))
+ (setq flat (nconc flat (gnus-article-mime-handles
+ (cdr handle) newid all)))
+ (delq (rassq handle all) all)
+ (setq flat (nconc flat (list (cons newid handle)))))))
+ (let ((flat (list nil)))
+ ;; Assume that elements of `gnus-article-mime-handle-alist'
+ ;; are in the decreasing order, but unnumbered subsidiaries
+ ;; in each element are in the increasing order.
+ (dolist (handle (reverse gnus-article-mime-handle-alist))
+ (if (stringp (cadr handle))
+ (setq flat (nconc flat (gnus-article-mime-handles
+ (cddr handle) (list (car handle)) flat)))
+ (delq (rassq (cdr handle) flat) flat)
+ (setq flat (nconc flat (list (cons (list (car handle))
+ (cdr handle)))))))
+ (setq flat (cdr flat))
+ (mapc (lambda (handle)
+ (if (cdar handle)
+ ;; This is a hidden (i.e. unnumbered) handle.
+ (progn
+ (setcar handle
+ (1+ (caar gnus-article-mime-handle-alist)))
+ (push handle gnus-article-mime-handle-alist))
+ (setcar handle (caar handle))))
+ flat)
+ flat)))
+
(defun gnus-mime-buttonize-attachments-in-header (&optional interactive)
"Show attachments as buttons in the end of the header of an article.
This function toggles the display when called interactively. Note that
in the body. Use `gnus-header-face-alist' to highlight buttons."
(interactive (list t))
(gnus-with-article-buffer
- (gmm-labels
- ;; Function that returns a flattened version of
- ;; `gnus-article-mime-handle-alist'.
- ((flattened-alist
- (&optional alist id all)
- (if alist
- (let ((i 1) newid flat)
- (dolist (handle alist flat)
- (setq newid (append id (list i))
- i (1+ i))
- (if (stringp (car handle))
- (setq flat (nconc flat (flattened-alist (cdr handle)
- newid all)))
- (delq (rassq handle all) all)
- (setq flat (nconc flat (list (cons newid handle)))))))
- (let ((flat (list nil)))
- ;; Assume that elements of `gnus-article-mime-handle-alist'
- ;; are in the decreasing order, but unnumbered subsidiaries
- ;; in each element are in the increasing order.
- (dolist (handle (reverse gnus-article-mime-handle-alist))
- (if (stringp (cadr handle))
- (setq flat (nconc flat (flattened-alist (cddr handle)
- (list (car handle))
- flat)))
- (delq (rassq (cdr handle) flat) flat)
- (setq flat (nconc flat (list (cons (list (car handle))
- (cdr handle)))))))
- (setq flat (cdr flat))
- (mapc (lambda (handle)
- (if (cdar handle)
- ;; This is a hidden (i.e. unnumbered) handle.
- (progn
- (setcar handle
- (1+ (caar gnus-article-mime-handle-alist)))
- (push handle gnus-article-mime-handle-alist))
- (setcar handle (caar handle))))
- flat)
- flat))))
- (let ((case-fold-search t) buttons handle type st)
- (save-excursion
- (save-restriction
- (widen)
- (article-narrow-to-head)
- ;; Header buttons exist?
- (while (and (not buttons)
- (re-search-forward "^attachments?:[\n ]+" nil t))
- (when (get-char-property (match-end 0)
- 'gnus-button-attachment-extra)
- (setq buttons (match-beginning 0))))
- (widen)
+ (let ((case-fold-search t) buttons handle type st)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (article-narrow-to-head)
+ ;; Header buttons exist?
+ (while (and (not buttons)
+ (re-search-forward "^attachments?:[\n ]+" nil t))
+ (when (get-char-property (match-end 0)
+ 'gnus-button-attachment-extra)
+ (setq buttons (match-beginning 0))))
+ (widen)
+ (when buttons
+ ;; Delete header buttons.
+ (delete-region buttons (if (re-search-forward "^[^ ]" nil t)
+ (match-beginning 0)
+ (point-max))))
+ (unless (and interactive buttons)
+ ;; Find buttons.
+ (setq buttons nil)
+ (dolist (button (gnus-article-mime-handles))
+ (setq handle (cdr button)
+ type (mm-handle-media-type handle))
+ (when (or (and (if (gnus-buffer-live-p gnus-summary-buffer)
+ (with-current-buffer gnus-summary-buffer
+ gnus-inhibit-images)
+ gnus-inhibit-images)
+ (string-match "\\`image/" type))
+ (mm-inline-override-p handle)
+ (and (mm-handle-disposition handle)
+ (not (equal (car (mm-handle-disposition handle))
+ "inline"))
+ (not (mm-attachment-override-p handle)))
+ (not (mm-automatic-display-p handle))
+ (not (or (and (mm-inlinable-p handle)
+ (mm-inlined-p handle))
+ (mm-automatic-external-display-p type))))
+ (push button buttons)))
(when buttons
- ;; Delete header buttons.
- (delete-region buttons (if (re-search-forward "^[^ ]" nil t)
- (match-beginning 0)
- (point-max))))
- (unless (and interactive buttons)
- ;; Find buttons.
- (setq buttons nil)
- (dolist (button (flattened-alist))
- (setq handle (cdr button)
- type (mm-handle-media-type handle))
- (when (or (and (if (gnus-buffer-live-p gnus-summary-buffer)
- (with-current-buffer gnus-summary-buffer
- gnus-inhibit-images)
- gnus-inhibit-images)
- (string-match "\\`image/" type))
- (mm-inline-override-p handle)
- (and (mm-handle-disposition handle)
- (not (equal (car (mm-handle-disposition handle))
- "inline"))
- (not (mm-attachment-override-p handle)))
- (not (mm-automatic-display-p handle))
- (not (or (and (mm-inlinable-p handle)
- (mm-inlined-p handle))
- (mm-automatic-external-display-p type))))
- (push button buttons)))
- (when buttons
- ;; Add header buttons.
- (article-goto-body)
- (forward-line -1)
- (narrow-to-region (point) (point))
- (insert "Attachment" (if (cdr buttons) "s" "") ":")
- (dolist (button (nreverse buttons))
- (setq st (point))
- (insert " ")
- (mm-handle-set-undisplayer
- (setq handle (copy-sequence (cdr button))) nil)
- (gnus-insert-mime-button handle (car button))
- (skip-chars-backward "\t\n ")
- (delete-region (point) (point-max))
- (when (> (current-column) (window-width))
- (goto-char st)
- (insert "\n")
- (end-of-line)))
- (insert "\n")
- (dolist (ovl (gnus-overlays-in (point-min) (point)))
- (gnus-overlay-put ovl 'gnus-button-attachment-extra t)
- (gnus-overlay-put ovl 'face nil))
- (let ((gnus-treatment-function-alist
- '((gnus-treat-highlight-headers
- gnus-article-highlight-headers))))
- (gnus-treat-article 'head))))))))))
+ ;; Add header buttons.
+ (article-goto-body)
+ (forward-line -1)
+ (narrow-to-region (point) (point))
+ (insert "Attachment" (if (cdr buttons) "s" "") ":")
+ (dolist (button (nreverse buttons))
+ (setq st (point))
+ (insert " ")
+ (mm-handle-set-undisplayer
+ (setq handle (copy-sequence (cdr button))) nil)
+ (gnus-insert-mime-button handle (car button))
+ (skip-chars-backward "\t\n ")
+ (delete-region (point) (point-max))
+ (when (> (current-column) (window-width))
+ (goto-char st)
+ (insert "\n")
+ (end-of-line)))
+ (insert "\n")
+ (dolist (ovl (gnus-overlays-in (point-min) (point)))
+ (gnus-overlay-put ovl 'gnus-button-attachment-extra t)
+ (gnus-overlay-put ovl 'face nil))
+ (let ((gnus-treatment-function-alist
+ '((gnus-treat-highlight-headers
+ gnus-article-highlight-headers))))
+ (gnus-treat-article 'head)))))))))
;;; Article savers.
(if header-line-format 1 0)
2)))))))
+(defvar scroll-in-place)
+
(defun gnus-article-next-page-1 (lines)
(condition-case ()
(let ((scroll-in-place nil)
(unless (derived-mode-p 'gnus-article-mode)
(error "Command invoked outside of a Gnus article buffer")))
-(defun gnus-article-read-summary-keys (&optional arg key not-restore-window)
+(defvar gnus-pick-mode)
+
+(defun gnus-article-read-summary-keys (&optional _arg key not-restore-window)
"Read a summary buffer key sequence and execute it from the article buffer."
(interactive "P")
(gnus-article-check-buffer)
"An" "Ap" [?A (meta return)] [?A delete]))
(nosave-in-article
'("AS" "\C-d"))
- (up-to-top
- '("n" "Gn" "p" "Gp"))
keys new-sum-point)
(with-current-buffer gnus-article-current-summary
(let (gnus-pick-mode)
(defvar gnus-agent-summary-mode)
(defvar gnus-draft-mode)
(defvar help-xref-stack-item)
+(defvar help-xref-following)
(defun gnus-article-describe-bindings (&optional prefix)
"Show a list of all defined keys, and their definitions.
(let ((func gnus-article-edit-done-function)
(buf (current-buffer))
(start (window-start))
- (p (point))
(winconf gnus-prev-winconf))
(widen) ;; Widen it in case that users narrowed the buffer.
(funcall func arg)
(gnus-article-add-buttons)
(gnus-article-add-buttons-to-head))
-(defun gnus-article-highlight-some (&optional force)
+(defun gnus-article-highlight-some (&optional _force)
"Highlight current article.
This function calls `gnus-article-highlight-headers',
`gnus-article-highlight-signature', and `gnus-article-add-buttons' to
(error "Unknown news URL syntax"))))
(list scheme server port group message-id articles)))
+(defvar nntp-port-number)
+
(defun gnus-button-handle-news (url)
"Fetch a news URL."
- (destructuring-bind (scheme server port group message-id articles)
+ (destructuring-bind (_scheme server port group message-id _articles)
(gnus-parse-news-url url)
(cond
(message-id
(with-current-buffer gnus-summary-buffer
(gnus-summary-refer-article message-id)))
-(defun gnus-button-fetch-group (address &rest ignore)
+(defun gnus-button-fetch-group (address &rest _ignore)
"Fetch GROUP specified by ADDRESS."
(when (string-match "\\`\\(nntp\\|news\\):\\(//\\)?\\(.*\\)\\'"
address)
(setq url (replace-regexp-in-string "\n" " " url))
(when (string-match "mailto:/*\\(.*\\)" url)
(setq url (substring url (match-beginning 1) nil)))
- (let (to args subject func)
- (setq args (gnus-url-parse-query-string
+ (let* ((args (gnus-url-parse-query-string
(if (string-match "^\\?" url)
(substring url 1)
(if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
(concat "to=" (match-string 1 url) "&"
(match-string 2 url))
- (concat "to=" url))))
- subject (cdr-safe (assoc "subject" args)))
+ (concat "to=" url)))))
+ (subject (cdr-safe (assoc "subject" args)))
+ func)
(gnus-msg-mail)
(while args
(setq func (intern-soft (concat "message-goto-" (downcase (caar args)))))
:action 'gnus-button-prev-page
:button-keymap gnus-prev-page-map)))
-(defun gnus-button-next-page (&optional args more-args)
+(defun gnus-button-next-page (&optional _args _more-args)
"Go to the next page."
(interactive)
(let ((win (selected-window)))
(gnus-article-next-page)
(select-window win)))
-(defun gnus-button-prev-page (&optional args more-args)
+(defun gnus-button-prev-page (&optional _args _more-args)
"Go to the prev page."
(interactive)
(let ((win (selected-window)))
:action 'gnus-button-next-page
:button-keymap gnus-next-page-map)))
-(defun gnus-article-button-next-page (arg)
+(defun gnus-article-button-next-page (_arg)
"Go to the next page."
(interactive "P")
(let ((win (selected-window)))
(gnus-article-next-page)
(select-window win)))
-(defun gnus-article-button-prev-page (arg)
+(defun gnus-article-button-prev-page (_arg)
"Go to the prev page."
(interactive "P")
(let ((win (selected-window)))
(defvar gnus-inhibit-article-treatments nil)
-(defun gnus-treat-article (gnus-treat-condition
- &optional part-number total-parts gnus-treat-type)
- (let ((gnus-treat-length (- (point-max) (point-min)))
+;; Dynamic variables.
+(defvar part-number) ;FIXME: Lacks a "gnus-" prefix.
+(defvar total-parts) ;FIXME: Lacks a "gnus-" prefix.
+(defvar gnus-treat-type)
+(defvar gnus-treat-condition)
+(defvar gnus-treat-length)
+
+(defun gnus-treat-article (condition
+ &optional part-num total type)
+ (let ((gnus-treat-condition condition)
+ (part-number part-num)
+ (total-parts total)
+ (gnus-treat-type type)
+ (gnus-treat-length (- (point-max) (point-min)))
(alist gnus-treatment-function-alist)
(article-goto-body-goes-to-point-min-p t)
(treated-type
- (or (not gnus-treat-type)
+ (or (not type)
(catch 'found
(let ((list gnus-article-treat-types))
(while list
- (when (string-match (pop list) gnus-treat-type)
+ (when (string-match (pop list) type)
(throw 'found t)))))))
(highlightp (gnus-visual-p 'article-highlight 'highlight))
- val elem)
+ val)
(gnus-run-hooks 'gnus-part-display-hook)
(dolist (elem alist)
(setq val
(save-restriction
(funcall (cadr elem)))))))
-;; Dynamic variables.
-(defvar part-number)
-(defvar total-parts)
-(defvar gnus-treat-type)
-(defvar gnus-treat-condition)
-(defvar gnus-treat-length)
-
(defun gnus-treat-predicate (val)
(cond
((null val)
(gnus-mime-security-show-details handle)
(gnus-mime-security-verify-or-decrypt handle))))
-(defun gnus-insert-mime-security-button (handle &optional displayed)
+(defun gnus-insert-mime-security-button (handle &optional _displayed)
(let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
(gnus-tmp-type
(concat
:action 'gnus-widget-press-button
:button-keymap gnus-mime-security-button-map
:help-echo
- (lambda (widget)
+ (lambda (_widget)
;; Needed to properly clear the message due to a bug in
;; wid-edit (XEmacs only).
(when (boundp 'help-echo-owns-message)
(interactive)
(save-excursion
(save-window-excursion
- ;; Avoir warnings?
+ ;; Avoid warnings?
;; (message "Saving Gnus bookmarks to file %s..." gnus-bookmark-default-file)
(set-buffer (get-buffer-create " *Gnus bookmarks*"))
(erase-buffer)
(defgroup gnus-cloud nil
"Syncing Gnus data via IMAP."
+ :version "25.1"
:group 'gnus)
(defcustom gnus-cloud-synced-files
(:directory "~/News" :match ".*.SCORE\\'"))
"List of file regexps that should be kept up-to-date via the cloud."
:group 'gnus-cloud
+ ;; FIXME this type does not match the default. Nor does the documentation.
:type '(repeat regexp))
(defvar gnus-cloud-group-name "*Emacs Cloud*")
(let ((spec (ignore-errors (read (current-buffer))))
length)
(when (and (consp spec)
- (memq (plist-get spec :type) '(:file :data :deleta)))
+ (memq (plist-get spec :type) '(:file :data :delete)))
(setq length (plist-get spec :length))
(push (append spec
(list
(setq hour (string-to-number (match-string 1 delay))
minute (string-to-number (match-string 2 delay)))
;; Use current time, except...
- (setq deadline (apply 'vector (decode-time (current-time))))
+ (setq deadline (apply 'vector (decode-time)))
;; ... for minute and hour.
(aset deadline 1 minute)
(aset deadline 2 hour)
(when (gnus-group-entry group)
(gnus-activate-group group)
(add-hook 'message-send-hook
- (lambda () (message-remove-header gnus-delay-header)))
+ (lambda () (message-remove-header gnus-delay-header)) t)
(setq articles (nndraft-articles))
(while (setq article (pop articles))
(gnus-request-head article group)
(defcustom gnus-x-face-omit-files nil
"Regexp to match faces in `gnus-x-face-directory' to be omitted."
- :version "24.5"
+ :version "25.1"
:group 'gnus-fun
:type 'string)
(defcustom gnus-face-directory (expand-file-name "faces" gnus-directory)
"*Directory where Face PNG files are stored."
- :version "24.5"
+ :version "25.1"
:group 'gnus-fun
:type 'directory)
(defcustom gnus-face-omit-files nil
"Regexp to match faces in `gnus-face-directory' to be omitted."
- :version "24.5"
+ :version "25.1"
:group 'gnus-fun
:type 'string)
;;;###autoload
(defun gnus-insert-random-face-header ()
- "Insert a randome Face header from `gnus-face-directory'."
+ "Insert a random Face header from `gnus-face-directory'."
(gnus--insert-random-face-with-type 'gnus-random-face 'Face))
(defface gnus-x-face '((t (:foreground "black" :background "white")))
(defmethod gnus-icalendar-event:start ((event gnus-icalendar-event))
(format-time-string "%Y-%m-%d %H:%M" (gnus-icalendar-event:start-time event)))
-(defun gnus-icalendar-event--decode-datefield (ical field)
- (let* ((date (icalendar--get-event-property ical field))
- (date-props (icalendar--get-event-property-attributes ical field))
- (tz (plist-get date-props 'TZID)))
-
- (date-to-time (timezone-make-date-arpa-standard date nil tz))))
+(defun gnus-icalendar-event--decode-datefield (event field zone-map)
+ (let* ((dtdate (icalendar--get-event-property event field))
+ (dtdate-zone (icalendar--find-time-zone
+ (icalendar--get-event-property-attributes
+ event field) zone-map))
+ (dtdate-dec (icalendar--decode-isodatetime dtdate nil dtdate-zone)))
+ (apply 'encode-time dtdate-dec)))
(defun gnus-icalendar-event--find-attendee (ical name-or-email)
(let* ((event (car (icalendar--all-events ical)))
("REQ-PARTICIPANT" 'required)
("OPT-PARTICIPANT" 'optional)
(_ 'non-participant)))
+ (zone-map (icalendar--convert-all-timezones ical))
(args (list :method method
:organizer organizer
- :start-time (gnus-icalendar-event--decode-datefield event 'DTSTART)
- :end-time (gnus-icalendar-event--decode-datefield event 'DTEND)
+ :start-time (gnus-icalendar-event--decode-datefield event 'DTSTART zone-map)
+ :end-time (gnus-icalendar-event--decode-datefield event 'DTEND zone-map)
:rsvp (string= (plist-get (cadr attendee) 'RSVP) "TRUE")
:participation-type participation-type
:req-participants (car attendee-names)
calendar events if any of your identities matches at least one
RSVP participant.
-Your identity is guessed automatically from the variables `user-full-name',
-`user-mail-address', and `gnus-ignored-from-addresses'.
+Your identity is guessed automatically from the variables
+`user-full-name', `user-mail-address',
+`gnus-ignored-from-addresses' and `message-alternative-emails'.
If you need even more aliases you can define them here. It really
only makes sense to define names or email addresses."
(list user-full-name (regexp-quote user-mail-address)
; NOTE: these can be lists
gnus-ignored-from-addresses ; already regexp-quoted
+ message-alternative-emails ;
(mapcar #'regexp-quote gnus-icalendar-additional-identities)))))
;; TODO: make the template customizable
:body subject
:actions '("read" "Read")
:on-action 'gnus-notifications-action
- :app-icon (or photo-file
- (gnus-funcall-no-warning
- 'image-search-load-path "gnus/gnus.png"))
+ :app-icon (gnus-funcall-no-warning
+ 'image-search-load-path "gnus/gnus.png")
+ :image-path photo-file
:app-name "Gnus"
:category "email.arrived"
:timeout gnus-notifications-timeout)
(make-obsolete-variable 'gnus-registry-max-track-groups nil "23.4")
(make-obsolete-variable 'gnus-registry-entry-caching nil "23.4")
(make-obsolete-variable 'gnus-registry-trim-articles-without-groups nil "23.4")
+(make-obsolete-variable 'gnus-registry-max-pruned-entries nil "24.4")
(defcustom gnus-registry-track-extra '(subject sender recipient)
"Whether the registry should track extra data about a message.
(defcustom gnus-registry-cache-file
(nnheader-concat
(or gnus-dribble-directory gnus-home-directory "~/")
- ".gnus.registry.eioio")
+ ".gnus.registry.eieio")
"File where the Gnus registry will be stored."
:group 'gnus-registry
:type 'file)
:type '(radio (const :format "Unlimited " nil)
(integer :format "Maximum number: %v")))
-(defcustom gnus-registry-max-pruned-entries nil
- "Maximum number of pruned entries in the registry, nil for unlimited."
- :version "24.1"
+(defcustom gnus-registry-prune-factor 0.1
+ "When pruning, try to prune back to this factor less than the maximum size.
+
+In order to prevent constant pruning, we prune back to a number
+somewhat less than the maximum size. This option controls
+exactly how much less. For example, given a maximum size of
+50000 and a prune factor of 0.1, the pruning process will try to
+cut the registry back to \(- 50000 \(* 50000 0.1\)\) -> 45000
+entries. The pruning process is constrained by the presence of
+\"precious\" entries."
+ :version "24.4"
:group 'gnus-registry
- :type '(radio (const :format "Unlimited " nil)
- (integer :format "Maximum number: %v")))
+ :type 'float)
+
+(defcustom gnus-registry-default-sort-function
+ #'gnus-registry-sort-by-creation-time
+ "Sort function to use when pruning the registry.
+
+Entries which sort to the front of the list will be pruned
+first.
+
+This can slow pruning down. Set to nil to perform no sorting."
+ :version "24.4"
+ :group 'gnus-registry
+ :type 'symbol)
+
+(defun gnus-registry-sort-by-creation-time (l r)
+ "Sort older entries to front of list."
+ ;; Pruning starts from the front of the list.
+ (time-less-p
+ (cadr (assq 'creation-time r))
+ (cadr (assq 'creation-time l))))
(defun gnus-registry-fixup-registry (db)
(when db
(oset db :precious
(append gnus-registry-extra-entries-precious
'()))
- (oset db :max-hard
+ (oset db :max-size
(or gnus-registry-max-entries
most-positive-fixnum))
(oset db :prune-factor
- 0.1)
- (oset db :max-soft
- (or gnus-registry-max-pruned-entries
- most-positive-fixnum))
+ (or gnus-registry-prune-factor
+ 0.1))
(oset db :tracked
(append gnus-registry-track-extra
'(mark group keyword)))
"Gnus Registry"
:file (or file gnus-registry-cache-file)
;; these parameters are set in `gnus-registry-fixup-registry'
- :max-hard most-positive-fixnum
- :max-soft most-positive-fixnum
+ :max-size most-positive-fixnum
+ :version registry-db-version
:precious nil
:tracked nil)))
(gnus-message 4 "Remaking the Gnus registry")
(setq gnus-registry-db (gnus-registry-make-db))))
-(defun gnus-registry-read ()
- "Read the registry cache file."
+(defun gnus-registry-load ()
+ "Load the registry from the cache file."
(interactive)
(let ((file gnus-registry-cache-file))
(condition-case nil
- (progn
- (gnus-message 5 "Reading Gnus registry from %s..." file)
- (setq gnus-registry-db
- (gnus-registry-fixup-registry
- (condition-case nil
- (with-no-warnings
- (eieio-persistent-read file 'registry-db))
- ;; Older EIEIO versions do not check the class name.
- ('wrong-number-of-arguments
- (eieio-persistent-read file)))))
- (gnus-message 5 "Reading Gnus registry from %s...done" file))
+ (gnus-registry-read file)
+ (file-error
+ ;; Fix previous mis-naming of the registry file.
+ (let ((old-file-name
+ (concat (file-name-sans-extension
+ gnus-registry-cache-file)
+ ".eioio")))
+ (if (and (file-exists-p old-file-name)
+ (yes-or-no-p
+ (format "Rename registry file from %s to %s? "
+ old-file-name file)))
+ (progn
+ (gnus-registry-read old-file-name)
+ (oset gnus-registry-db :file file)
+ (gnus-message 1 "Registry filename changed to %s" file))
+ (gnus-registry-remake-db t))))
(error
(gnus-message
1
file)
(gnus-registry-remake-db t)))))
+(defun gnus-registry-read (file)
+ "Do the actual reading of the registry persistence file."
+ (gnus-message 5 "Reading Gnus registry from %s..." file)
+ (setq gnus-registry-db
+ (gnus-registry-fixup-registry
+ (condition-case nil
+ (with-no-warnings
+ (eieio-persistent-read file 'registry-db))
+ ;; Older EIEIO versions do not check the class name.
+ ('wrong-number-of-arguments
+ (eieio-persistent-read file)))))
+ (gnus-message 5 "Reading Gnus registry from %s...done" file))
+
(defun gnus-registry-save (&optional file db)
"Save the registry cache file."
(interactive)
(db (or db gnus-registry-db)))
(gnus-message 5 "Saving Gnus registry (%d entries) to %s..."
(registry-size db) file)
- (registry-prune db)
+ (registry-prune
+ db gnus-registry-default-sort-function)
;; TODO: call (gnus-string-remove-all-properties v) on all elements?
(eieio-persistent-save db file)
(gnus-message 5 "Saving Gnus registry (size %d) to %s...done"
"Just like `registry-insert' but tries to prune on error."
(when (registry-full db)
(message "Trying to prune the registry because it's full")
- (registry-prune db))
+ (registry-prune
+ db gnus-registry-default-sort-function))
(registry-insert db id entry)
entry)
(gnus-message 5 "Initializing the registry")
(gnus-registry-install-hooks)
(gnus-registry-install-shortcuts)
- (gnus-registry-read))
+ (gnus-registry-load))
;; FIXME: Why autoload this function?
;;;###autoload
(add-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
(add-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
- (add-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
+ (add-hook 'gnus-read-newsrc-el-hook 'gnus-registry-load)
(add-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids))
(remove-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
(remove-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
- (remove-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
+ (remove-hook 'gnus-read-newsrc-el-hook 'gnus-registry-load)
(remove-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids)
(setq gnus-registry-enabled nil))
:group 'gnus-newsrc
:type 'hook)
+(defcustom gnus-save-newsrc-file-check-timestamp nil
+ "Check the modification time of the newsrc.eld file before saving it.
+When the newsrc.eld file is updated by multiple machines,
+checking the file's modification time is a good way to avoid
+overwriting updated data."
+ :version "25.1"
+ :group 'gnus-newsrc
+ :type 'boolean)
+
(defcustom gnus-save-newsrc-hook nil
"A hook called before saving any of the newsrc files."
:group 'gnus-newsrc
"Check whether a group has been activated or not.
If SCAN, request a scan of that group as well. If METHOD, use
that select method instead of determining the method based on the
-group name. If DONT-CHECK, don't check check whether the group
+group name. If DONT-CHECK, don't check whether the group
actually exists. If DONT-SUB-CHECK or DONT-CHECK, don't let the
backend check whether the group actually exists."
(let ((method (or method (inline (gnus-find-method-for-group group))))
'msdos-long-file-names
(lambda () t))))
+(defvar gnus-save-newsrc-file-last-timestamp nil)
(defun gnus-save-newsrc-file (&optional force)
"Save .newsrc file."
;; Note: We cannot save .newsrc file if all newsgroups are removed
(erase-buffer)
(gnus-message 5 "Saving %s.eld..." gnus-current-startup-file)
+ ;; check timestamp of `gnus-current-startup-file'.eld against
+ ;; `gnus-save-newsrc-file-last-timestamp'
+ (when gnus-save-newsrc-file-check-timestamp
+ (let* ((checkfile (concat gnus-current-startup-file ".eld"))
+ (mtime (nth 5 (file-attributes checkfile))))
+ (when (and gnus-save-newsrc-file-last-timestamp
+ (time-less-p gnus-save-newsrc-file-last-timestamp
+ mtime))
+ (unless (y-or-n-p
+ (format "%s was updated externally after %s, save?"
+ checkfile
+ (format-time-string
+ "%c"
+ gnus-save-newsrc-file-last-timestamp)))
+ (error "Couldn't save %s: updated externally" checkfile)))))
+
(if gnus-save-startup-file-via-temp-buffer
(let ((coding-system-for-write gnus-ding-file-coding-system)
(standard-output (current-buffer)))
(gnus-gnus-to-quick-newsrc-format)
(gnus-run-hooks 'gnus-save-quick-newsrc-hook)
- (save-buffer))
+ (save-buffer)
+ (setq gnus-save-newsrc-file-last-timestamp
+ (nth 5 (file-attributes buffer-file-name))))
(let ((coding-system-for-write gnus-ding-file-coding-system)
(version-control gnus-backup-startup-file)
(startup-file (concat gnus-current-startup-file ".eld"))
;; Replace the existing startup file with the temp file.
(rename-file working-file startup-file t)
- (gnus-set-file-modes startup-file setmodes)))
+ (gnus-set-file-modes startup-file setmodes)
+ (setq gnus-save-newsrc-file-last-timestamp
+ (nth 5 (file-attributes startup-file)))))
(condition-case nil
(delete-file working-file)
(file-error nil)))))
(gnus-group-is-exiting-without-update-p t)
(quit-config (gnus-group-quit-config group)))
(when (or no-questions
+ (gnus-ephemeral-group-p group)
gnus-expert-user
(gnus-y-or-n-p "Discard changes to this group and exit? "))
(gnus-async-halt-prefetch)
((gnus-group-read-ephemeral-group
(setq vgroup (format
"nnvirtual:%s-%s" gnus-newsgroup-name
- (format-time-string "%Y%m%dT%H%M%S" (current-time))))
+ (format-time-string "%Y%m%dT%H%M%S")))
`(nnvirtual ,vgroup (nnvirtual-component-groups ,groups))
t
(cons (current-buffer) 'summary)))
(if (not (and (condition-case nil (require 'idna)
(file-error))
(mm-coding-system-p 'utf-8)
+ (symbol-value 'idna-program)
(executable-find (symbol-value 'idna-program))))
(gnus-message
5 "GNU Libidn not installed properly (`idn' or `idna.el' missing)")
;; Every version of Emacs Gnus supports has built-in float-time.
;; The featurep test silences an irritating compiler warning.
-(eval-and-compile
+(defalias 'gnus-float-time
(if (or (featurep 'emacs)
(fboundp 'float-time))
- (defalias 'gnus-float-time 'float-time)
- (defun gnus-float-time (&optional time)
- "Convert time value TIME to a floating point number.
-TIME defaults to the current time."
- (time-to-seconds (or time (current-time))))))
+ 'float-time 'time-to-seconds))
;;; Keymap macros.
(defun gnus-seconds-today ()
"Return the number of seconds passed today."
- (let ((now (decode-time (current-time))))
+ (let ((now (decode-time)))
(+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600))))
(defun gnus-seconds-month ()
"Return the number of seconds passed this month."
- (let ((now (decode-time (current-time))))
+ (let ((now (decode-time)))
(+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)
(* (- (car (nthcdr 3 now)) 1) 3600 24))))
(defun gnus-seconds-year ()
"Return the number of seconds passed this year."
- (let ((now (decode-time (current-time)))
- (days (format-time-string "%j" (current-time))))
+ (let* ((current (current-time))
+ (now (decode-time current))
+ (days (format-time-string "%j" current)))
(+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)
(* (- (string-to-number days) 1) 3600 24))))
(if (fboundp 'find-image)
(defun gnus-mode-line-buffer-identification (line)
(let ((str (car-safe line))
- (load-path (mm-image-load-path)))
- (if (and (stringp str)
+ (load-path (append (mm-image-load-path) load-path)))
+ (if (and (display-graphic-p)
+ (stringp str)
(string-match "^Gnus:" str))
(progn (add-text-properties
0 5
(1+ (point))
(progn (forward-sexp 1) (1- (point))))))))
(t
- (message "Unknown symbol: %c" c)
(forward-char 1))))
;; If we found no display-name, then we look for comments.
(if display-name
(non-viewer . t)
(type . "application/zip")
("copiousoutput"))
+ ("pdf"
+ (viewer . doc-view-mode)
+ (type . "application/pdf")
+ (test . (eq window-system 'x)))
("pdf"
(viewer . "gv -safer %s")
(type . "application/pdf")
common-mime-info)))))
commands))
+(defun mailcap-view-mime (type)
+ "View the data in the current buffer that has MIME type TYPE.
+`mailcap-mime-data' determines the method to use."
+ (let ((method (mailcap-mime-info type)))
+ (if (stringp method)
+ (shell-command-on-region (point-min) (point-max)
+ ;; Use stdin as the "%s".
+ (format method "-")
+ (current-buffer)
+ t)
+ (funcall method))))
+
(provide 'mailcap)
;;; mailcap.el ends here
"If non-nil, delete non-matching headers when forwarding a message.
Only headers that match this regexp will be included. This
variable should be a regexp or a list of regexps."
- :version "24.5"
+ :version "25.1"
:group 'message-forwarding
:type '(repeat :value-to-internal (lambda (widget value)
(custom-split-regexp-maybe value))
:type '(radio (const :format "%v " nil)
(string :format "FQDN: %v")))
-(defcustom message-use-idna (and (condition-case nil (require 'idna)
- (file-error))
- (mm-coding-system-p 'utf-8)
- (executable-find idna-program)
- (string= (idna-to-ascii "räksmörgås")
- "xn--rksmrgs-5wao1o")
- t)
+(defcustom message-use-idna
+ (and (or (mm-coding-system-p 'utf-8)
+ (condition-case nil
+ (let (mucs-ignore-version-incompatibilities)
+ (require 'un-define))
+ (error)))
+ (condition-case nil (require 'idna) (file-error))
+ idna-program
+ (executable-find idna-program)
+ (string= (idna-to-ascii "räksmörgås") "xn--rksmrgs-5wao1o")
+ t)
"Whether to encode non-ASCII in domain names into ASCII according to IDNA.
GNU Libidn, and in particular the elisp package \"idna.el\" and
the external program \"idn\", must be installed for this
"cat\\|com\\|coop\\|edu\\|gov\\|"
"info\\|int\\|jobs\\|"
"mil\\|mobi\\|museum\\|name\\|net\\|"
- "org\\|pro\\|tel\\|travel\\|uucp\\)")
+ "org\\|pro\\|tel\\|travel\\|uucp\\|"
+ ;; ICANN-era generic top-level domains
+ "academy\\|actor\\|agency\\|airforce\\|archi\\|associates\\|axa\\|"
+ "bar\\|bargains\\|bayern\\|beer\\|berlin\\|best\\|bid\\|bike\\|"
+ "biz\\|black\\|blackfriday\\|blue\\|boutique\\|build\\|builders\\|"
+ "buzz\\|cab\\|camera\\|camp\\|capital\\|cards\\|care\\|career\\|"
+ "careers\\|cash\\|catering\\|center\\|ceo\\|cheap\\|christmas\\|"
+ "church\\|citic\\|cleaning\\|clinic\\|clothing\\|club\\|codes\\|"
+ "coffee\\|college\\|cologne\\|com\\|community\\|company\\|computer\\|"
+ "construction\\|contractors\\|cooking\\|cool\\|country\\|creditcard\\|"
+ "cruises\\|dance\\|dating\\|democrat\\|dental\\|desi\\|design\\|"
+ "diamonds\\|directory\\|discount\\|domains\\|education\\|email\\|"
+ "engineering\\|enterprises\\|equipment\\|estate\\|eus\\|events\\|"
+ "exchange\\|expert\\|exposed\\|fail\\|farm\\|feedback\\|finance\\|"
+ "financial\\|fish\\|fishing\\|fitness\\|flights\\|florist\\|foo\\|"
+ "foundation\\|frogans\\|fund\\|furniture\\|futbol\\|gal\\|"
+ "gallery\\|gift\\|glass\\|globo\\|gmo\\|gop\\|graphics\\|gratis\\|"
+ "gripe\\|guide\\|guitars\\|guru\\|hamburg\\|haus\\|hiphop\\|"
+ "holdings\\|holiday\\|homes\\|horse\\|house\\|immobilien\\|"
+ "industries\\|info\\|ink\\|institute\\|insure\\|international\\|"
+ "investments\\|jetzt\\|juegos\\|kaufen\\|kim\\|kitchen\\|kiwi\\|"
+ "koeln\\|kred\\|land\\|lat\\|latino\\|lease\\|life\\|lighting\\|"
+ "limited\\|limo\\|link\\|loans\\|london\\|luxe\\|luxury\\|"
+ "management\\|mango\\|marketing\\|media\\|meet\\|menu\\|miami\\|"
+ "moda\\|moe\\|monash\\|moscow\\|motorcycles\\|nagoya\\|name\\|"
+ "net\\|neustar\\|ninja\\|nyc\\|okinawa\\|onl\\|org\\|paris\\|"
+ "partners\\|parts\\|photo\\|photography\\|photos\\|pics\\|"
+ "pictures\\|pink\\|plumbing\\|pro\\|productions\\|properties\\|"
+ "pub\\|qpon\\|quebec\\|recipes\\|red\\|reisen\\|ren\\|rentals\\|"
+ "repair\\|report\\|rest\\|reviews\\|rich\\|rocks\\|rodeo\\|"
+ "ruhr\\|ryukyu\\|saarland\\|schule\\|scot\\|services\\|sexy\\|"
+ "shiksha\\|shoes\\|singles\\|social\\|sohu\\|solar\\|solutions\\|"
+ "soy\\|supplies\\|supply\\|support\\|surgery\\|systems\\|tattoo\\|"
+ "tax\\|technology\\|tienda\\|tips\\|today\\|tokyo\\|tools\\|"
+ "town\\|toys\\|trade\\|training\\|university\\|uno\\|vacations\\|"
+ "vegas\\|ventures\\|viajes\\|villas\\|vision\\|vodka\\|vote\\|"
+ "voting\\|voto\\|voyage\\|wang\\|watch\\|webcam\\|wed\\|wien\\|"
+ "wiki\\|works\\|wtc\\|wtf\\|xyz\\|yachts\\|yokohama\\|you\\|"
+ "zone\\)")
;; http://en.wikipedia.org/wiki/List_of_Internet_top-level_domains
;; http://en.wikipedia.org/wiki/GTLD
;; `approved, but not yet in operation': .xxx
((not (string-match
(concat "^[ \t]*"
(regexp-quote new-subject)
- " \t]*$")
+ "[ \t]*$")
old-subject)) ; yes, it really is a new subject
;; delete eventual Re: prefix
(setq old-subject
(goto-char (point-max))
;; Insert the signature.
(unless (bolp)
- (insert "\n"))
+ (newline))
(when message-signature-insert-empty-line
- (insert "\n"))
- (insert "-- \n")
+ (newline))
+ (insert "-- ")
+ (newline)
(if (eq signature t)
(insert-file-contents signature-file)
(insert signature))
(goto-char (point-max))
- (or (bolp) (insert "\n")))))
+ (or (bolp) (newline)))))
(defun message-insert-importance-high ()
"Insert header to mark message as important."
"Make date string for the Expires header. Expiry in DAYS days.
In posting styles use `(\"Expires\" (make-expires-date 30))'."
- (let* ((cur (decode-time (current-time)))
+ (let* ((cur (decode-time))
(nday (+ days (nth 3 cur))))
(setf (nth 3 cur) nday)
(message-make-date (apply 'encode-time cur))))
(unless from
(setq from (mail-fetch-field "from")))
;; FIXME: In some circumstances, this code is running within
- ;; an unibyte macro. mail-extract-address-components
+ ;; a unibyte macro. mail-extract-address-components
;; creates unibyte buffers. This `if', though not a perfect
;; solution, avoids most of them.
(if from
(autoload 'mml-compute-boundary "mml")
+(defun mm-url-encode-multipart-form-data (pairs &optional boundary)
+ "Return PAIRS encoded in multipart/form-data."
+ ;; RFC1867
+ ;; Get a good boundary
+ (unless boundary
+ (setq boundary (mml-compute-boundary '())))
+ (concat
+ ;; Start with the boundary
+ "--" boundary "\r\n"
+ ;; Create name value pairs
+ (mapconcat
+ 'identity
+ ;; Delete any returned items that are empty
+ (delq nil
+ (mapcar (lambda (data)
+ (cond ((equal (car data) "file")
+ ;; For each pair
+ (format
+ ;; Encode the name
+ "Content-Disposition: form-data; name=%S; filename=%S\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: binary\r\n\r\n%s"
+ (cdr (assoc "name" (cdr data))) (cdr (assoc "filename" (cdr data)))
+ (cond ((stringp (cdr (assoc "filedata" (cdr data))))
+ (cdr (assoc "filedata" (cdr data))))
+ ((integerp (cdr (assoc "filedata" (cdr data))))
+ (number-to-string (cdr (assoc "filedata" (cdr data))))))))
+ ((equal (car data) "submit")
+ "Content-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit\r\n")
+ (t
+ (format
+ "Content-Disposition: form-data;name=%S\r\n\r\n%s\r\n"
+ (car data) (concat (mm-url-form-encode-xwfu (cdr data)))
+ ))))
+ pairs))
+ ;; use the boundary as a separator
+ (concat "\r\n--" boundary "\r\n"))
+ ;; put a boundary at the end.
+ "--" boundary "--\r\n"))
+
(defun mm-url-remove-markup ()
"Remove all HTML markup, leaving just plain text."
(goto-char (point-min))
(while (search-forward "<!--" nil t)
(delete-region (match-beginning 0)
- (or (search-forward "-->" nil t)
- (point-max))))
+ (or (search-forward "-->" nil t)
+ (point-max))))
(goto-char (point-min))
(while (re-search-forward "<[^>]+>" nil t)
(replace-match "" t t)))
it if any may malfunction."
(if (featurep 'xemacs)
`(progn ,@forms)
+ (message "Warning: Using brain-dead macro `mm-with-unibyte-current-buffer'!")
(let ((multibyte (make-symbol "multibyte")))
`(let ((,multibyte enable-multibyte-characters))
(when ,multibyte
(set-buffer-multibyte t)))))))
(put 'mm-with-unibyte-current-buffer 'lisp-indent-function 0)
(put 'mm-with-unibyte-current-buffer 'edebug-form-spec '(body))
+(make-obsolete 'mm-with-unibyte-current-buffer nil "25.1")
(defun mm-find-charset-region (b e)
"Return a list of Emacs charsets in the region B to E."
((string= mode "encrypt")
(setq tags (list "encrypt" method)))
((string= mode "signencrypt")
- (setq tags (list "sign" method "encrypt" method))))
+ (setq tags (list "sign" method "encrypt" method)))
+ (t
+ (error "Unknown secure mode %s" mode)))
(eval `(mml-insert-tag ,secure-mode
,@tags
,(if keyfile "keyfile")
"*nnimap*" (current-buffer) nnimap-address
(nnimap-map-port (car ports))
:type nnimap-stream
+ :warn-unless-encrypted t
:return-list t
:shell-command nnimap-shell-program
:capability-command "1 CAPABILITY\r\n"
(setq target nil))
(nnheader-message 7 "Expiring article %s:%d" group article))
(when target
- (push article deleted-articles))))))))
+ (push article deleted-articles))))))
+ (setq deleted-articles (nreverse deleted-articles))))
;; Change back to the current group again.
(nnimap-change-group group server)
- (setq deleted-articles (nreverse deleted-articles))
(nnimap-delete-article (gnus-compress-sequence deleted-articles))
deleted-articles))
(while (and (not (bobp))
(progn
(forward-line -1)
- (looking-at "\\*"))))
+ (looking-at "\\*\\|[0-9]+ OK NOOP"))))
(not (looking-at (format "%d .*\n" sequence)))))
(when messagep
(nnheader-message-maybe
t)
(defun nnweb-google-identity (url)
- "Return an unique identifier based on URL."
+ "Return a unique identifier based on URL."
(if (string-match "selm=\\([^ &>]+\\)" url)
(match-string 1 url)
url))
(request-msgType (concat (make-string 1 1) (make-string 3 0)))
;0x01 0x00 0x00 0x00
(request-flags (concat (make-string 1 7) (make-string 1 178)
- (make-string 2 0)))
- ;0x07 0xb2 0x00 0x00
+ (make-string 1 8) (make-string 1 0)))
+ ;0x07 0xb2 0x08 0x00
lu ld off-d off-u)
(when (string-match "@" user)
(unless domain
(defmacro ntlm-string-as-unibyte (string)
(if (fboundp 'string-as-unibyte)
`(string-as-unibyte ,string)
+ string))
+ (defmacro ntlm-string-make-unibyte (string)
+ (if (fboundp 'string-make-unibyte)
+ `(string-make-unibyte ,string)
string)))
+(eval-and-compile
+ (autoload 'sha1 "sha1")
+ (if (fboundp 'secure-hash)
+ (defalias 'ntlm-secure-hash 'secure-hash)
+ (defun ntlm-secure-hash (algorithm object &optional start end binary)
+ "Return the secure hash of OBJECT, a buffer or string.
+ALGORITHM is a symbol specifying the hash to use: md5, sha1.
+
+The two optional arguments START and END are positions specifying for
+which part of OBJECT to compute the hash. If nil or omitted, uses the
+whole OBJECT.
+
+If BINARY is non-nil, returns a string in binary form."
+ (cond ((eq algorithm 'md5)
+ (if binary
+ (let* ((hex (md5 object start end))
+ (len (length hex))
+ (beg 0)
+ rest)
+ (while (< beg len)
+ (push (ntlm-string-make-unibyte
+ (char-to-string
+ (string-to-number
+ (substring hex beg (setq beg (+ beg 2)))
+ 16)))
+ rest))
+ (apply #'concat (nreverse rest)))
+ (md5 object start end)))
+ ((eq algorithm 'sha1)
+ (sha1 object start end binary))
+ (t
+ (error "(ntlm-secure-hash) Unsupported algorithm: %s"
+ algorithm))))))
+
(defun ntlm-build-auth-response (challenge user password-hashes)
"Return the response string to a challenge string CHALLENGE given by
the NTLM based server for the user USER and the password hash list
(setq domain (substring user (1+ (match-beginning 0))))
(setq user (substring user 0 (match-beginning 0))))
- ;; generate response data
- (setq lmRespData
- (ntlm-smb-owf-encrypt (car password-hashes) challengeData))
- (setq ntRespData
- (ntlm-smb-owf-encrypt (cadr password-hashes) challengeData))
+ ;; check if "negotiate NTLM2 key" flag is set in type 2 message
+ (if (not (zerop (logand (aref flags 2) 8)))
+ (let (randomString
+ sessionHash)
+ ;; generate NTLM2 session response data
+ (setq randomString (ntlm-string-make-unibyte
+ (concat
+ (make-string 1 (random 256))
+ (make-string 1 (random 256))
+ (make-string 1 (random 256))
+ (make-string 1 (random 256))
+ (make-string 1 (random 256))
+ (make-string 1 (random 256))
+ (make-string 1 (random 256))
+ (make-string 1 (random 256)))))
+ (setq sessionHash (ntlm-secure-hash
+ 'md5 (concat challengeData randomString)
+ nil nil t))
+ (setq sessionHash (substring sessionHash 0 8))
+
+ (setq lmRespData (concat randomString (make-string 16 0)))
+ (setq ntRespData (ntlm-smb-owf-encrypt
+ (cadr password-hashes) sessionHash)))
+ (progn
+ ;; generate response data
+ (setq lmRespData
+ (ntlm-smb-owf-encrypt (car password-hashes) challengeData))
+ (setq ntRespData
+ (ntlm-smb-owf-encrypt (cadr password-hashes) challengeData))))
;; get offsets to fields to pack the response struct in a string
(setq lu (length user))
;;; Code:
-(eval-when-compile (require 'cl)) ;and ah ain't kiddin' 'bout it
-
(eval-and-compile
- (when (featurep 'xemacs)
- (defvar parse-time-syntax (make-vector 256 nil))))
-(defvar parse-time-digits (make-vector 256 nil))
+ (ignore-errors (require 'cl-lib)))
+(eval-when-compile
+ (require 'cl) ;and ah ain't kiddin' 'bout it
+ (defalias 'parse-time-incf (if (featurep 'cl-lib) 'cl-incf 'incf)))
;; Byte-compiler warnings
(defvar parse-time-elt)
(defvar parse-time-val)
-(unless (aref parse-time-digits ?0)
- (loop for i from ?0 to ?9
- do (aset parse-time-digits i (- i ?0))))
-
-(when (featurep 'xemacs)
- (unless (aref parse-time-syntax ?0)
- (loop for i from ?0 to ?9
- do (aset parse-time-syntax i ?0))
- (loop for i from ?A to ?Z
- do (aset parse-time-syntax i ?A))
- (loop for i from ?a to ?z
- do (aset parse-time-syntax i ?a))
- (aset parse-time-syntax ?+ 1)
- (aset parse-time-syntax ?- -1)
- (aset parse-time-syntax ?: ?d)))
-
-(defsubst digit-char-p (char)
- (aref parse-time-digits char))
-
(eval-and-compile
(if (featurep 'xemacs)
- (defsubst parse-time-string-chars (char)
- (and (< char (length parse-time-syntax))
- (aref parse-time-syntax char)))
+ (progn
+ (defvar parse-time-syntax (make-vector 256 nil))
+ (loop for i from ?0 to ?9
+ do (aset parse-time-syntax i ?0))
+ (loop for i from ?A to ?Z
+ do (aset parse-time-syntax i ?A))
+ (loop for i from ?a to ?z
+ do (aset parse-time-syntax i ?a))
+ (aset parse-time-syntax ?+ 1)
+ (aset parse-time-syntax ?- -1)
+ (aset parse-time-syntax ?: ?d)
+ (defsubst parse-time-string-chars (char)
+ (and (< char (length parse-time-syntax))
+ (aref parse-time-syntax char))))
(defsubst parse-time-string-chars (char)
(save-match-data
(let (case-fold-search str)
((string-match "[[:lower:]]" str) ?a)
((string-match "[[:digit:]]" str) ?0)))))))
-(put 'parse-error 'error-conditions '(parse-error error))
-(put 'parse-error 'error-message "Parsing error")
-
-(defsubst parse-integer (string &optional start end)
- "[CL] Parse and return the integer in STRING, or nil if none."
- (let ((integer 0)
- (digit 0)
- (index (or start 0))
- (end (or end (length string))))
- (when (< index end)
- (let ((sign (aref string index)))
- (if (or (eq sign ?+) (eq sign ?-))
- (setq sign (parse-time-string-chars sign)
- index (1+ index))
- (setq sign 1))
- (while (and (< index end)
- (setq digit (digit-char-p (aref string index))))
- (setq integer (+ (* integer 10) digit)
- index (1+ index)))
- (if (/= index end)
- (signal 'parse-error `("not an integer"
- ,(substring string (or start 0) end)))
- (* sign integer))))))
+(eval-and-compile
+ (if (fboundp 'cl-parse-integer)
+ (defalias 'parse-time-integer 'cl-parse-integer)
+ (defvar parse-time-digits (make-vector 256 nil))
+ (loop for i from ?0 to ?9
+ do (aset parse-time-digits i (- i ?0)))
+ (defun parse-time-integer (string &rest keys)
+ "[CL] Parse and return the integer in STRING, or nil if none."
+ (let* ((start (plist-get keys :start))
+ (end (or (plist-get keys :end) (length string)))
+ (integer 0)
+ (digit 0)
+ (index (or start 0)))
+ (when (< index end)
+ (let ((sign (aref string index)))
+ (if (or (eq sign ?+) (eq sign ?-))
+ (setq sign (parse-time-string-chars sign)
+ index (1+ index))
+ (setq sign 1))
+ (while (and (< index end)
+ (setq digit (aref parse-time-digits
+ (aref string index))))
+ (setq integer (+ (* integer 10) digit)
+ index (1+ index)))
+ (if (/= index end)
+ (error "Not an integer string: `%s'" string)
+ (* sign integer))))))))
(defun parse-time-tokenize (string)
"Tokenize STRING into substrings."
(while (< index end)
(while (and (< index end) ;Skip invalid characters.
(not (setq c (parse-time-string-chars (aref string index)))))
- (incf index))
+ (parse-time-incf index))
(setq start index all-digits (eq c ?0))
- (while (and (< (incf index) end) ;Scan valid characters.
+ (while (and (< (parse-time-incf index) end) ;Scan valid characters.
(setq c (parse-time-string-chars (aref string index))))
(setq all-digits (and all-digits (eq c ?0))))
(if (<= index end)
- (push (if all-digits (parse-integer string start index)
+ (push (if all-digits (parse-time-integer string
+ :start start :end index)
(substring string start index))
list)))
(nreverse list)))
(= 5 (length parse-time-elt))
(or (= (aref parse-time-elt 0) ?+)
(= (aref parse-time-elt 0) ?-))))
- ,#'(lambda () (* 60 (+ (parse-integer parse-time-elt 3 5)
- (* 60 (parse-integer parse-time-elt 1 3)))
+ ,#'(lambda () (* 60 (+ (parse-time-integer parse-time-elt :start 3 :end 5)
+ (* 60 (parse-time-integer parse-time-elt
+ :start 1 :end 3)))
(if (= (aref parse-time-elt 0) ?-) -1 1))))
((5 4 3)
,#'(lambda () (and (stringp parse-time-elt)
(let ((new-val (if rule
(let ((this (pop rule)))
(if (vectorp this)
- (parse-integer
+ (parse-time-integer
parse-time-elt
- (aref this 0) (aref this 1))
+ :start (aref this 0)
+ :end (aref this 1))
(funcall this)))
parse-time-val)))
(rplaca (nthcdr (pop slots) time) new-val))))))))
'tls)
(t
(or pop3-stream-type 'network)))
+ :warn-unless-encrypted t
:capability-command "CAPA\r\n"
:end-of-command "^\\(-ERR\\|+OK\\).*\n"
:end-of-capability "^\\.\r?\n\\|^-ERR"
;; This library provides a general-purpose EIEIO-based registry
;; database with persistence, initialized with these fields:
-;; version: a float, 0.1 currently (don't change it)
+;; version: a float
-;; max-hard: an integer, default 5000000
+;; max-size: an integer, default most-positive-fixnum
-;; max-soft: an integer, default 50000
+;; prune-factor: a float between 0 and 1, default 0.1
;; precious: a list of symbols
;; Note that whether a field has one or many pieces of data, the data
;; is always a list of values.
-;; The user decides which fields are "precious", F2 for example. At
-;; PRUNE TIME (when the :prune-function is called), the registry will
-;; trim any entries without the F2 field until the size is :max-soft
-;; or less. No entries with the F2 field will be removed at PRUNE
-;; TIME.
+;; The user decides which fields are "precious", F2 for example. When
+;; the registry is pruned, any entries without the F2 field will be
+;; removed until the size is :max-size * :prune-factor _less_ than the
+;; maximum database size. No entries with the F2 field will be removed
+;; at PRUNE TIME, which means it may not be possible to prune back all
+;; the way to the target size.
-;; When an entry is inserted, the registry will reject new entries
-;; if they bring it over the max-hard limit, even if they have the F2
+;; When an entry is inserted, the registry will reject new entries if
+;; they bring it over the :max-size limit, even if they have the F2
;; field.
;; The user decides which fields are "tracked", F1 for example. Any
(error
"eieio not found in `load-path' or gnus-fallback-lib/ directory.")))
+(eval-when-compile
+ (unless (fboundp 'cl-remf)
+ (defalias 'cl-remf 'remf)
+ (defalias 'cl-loop 'loop)
+ (defalias 'cl-subseq 'subseq)))
+
+;; The version number needs to be kept outside of the class definition
+;; itself. The persistent-save process does *not* write to file any
+;; slot values that are equal to the default :initform value. If a
+;; database object is at the most recent version, therefore, its
+;; version number will not be written to file. That makes it
+;; difficult to know when a database needs to be upgraded.
+(defvar registry-db-version 0.2
+ "The current version of the registry format.")
+
(defclass registry-db (eieio-persistent)
((version :initarg :version
- :initform 0.1
- :type float
- :custom float
+ :initform nil
+ :type (or null float)
:documentation "The registry version.")
- (max-hard :initarg :max-hard
- :initform 5000000
+ (max-size :initarg :max-size
+ ;; :initform most-positive-fixnum ;; see below
:type integer
:custom integer
- :documentation "Never accept more than this many elements.")
- (max-soft :initarg :max-soft
- :initform 50000
- :type integer
- :custom integer
- :documentation "Prune as much as possible to get to this size.")
+ :documentation "The maximum number of registry entries.")
(prune-factor
:initarg :prune-factor
:initform 0.1
:type float
:custom float
- :documentation "At the max-hard limit, prune size * this entries.")
+ :documentation "Prune to \(:max-size * :prune-factor\) less
+ than the :max-size limit. Should be a float between 0 and 1.")
(tracked :initarg :tracked
:initform nil
:type t
(data :initarg :data
:type hash-table
:documentation "The data hashtable.")))
+;; Do this separately, since defclass doesn't allow expressions in :initform.
+(oset-default registry-db max-size most-positive-fixnum)
+
+(defmethod initialize-instance :BEFORE ((this registry-db) slots)
+ "Check whether a registry object needs to be upgraded."
+ ;; Hardcoded upgrade routines. Version 0.1 to 0.2 requires the
+ ;; :max-soft slot to disappear, and the :max-hard slot to be renamed
+ ;; :max-size.
+ (let ((current-version
+ (and (plist-member slots :version)
+ (plist-get slots :version))))
+ (when (or (null current-version)
+ (eql current-version 0.1))
+ (setq slots
+ (plist-put slots :max-size (plist-get slots :max-hard)))
+ (setq slots
+ (plist-put slots :version registry-db-version))
+ (cl-remf slots :max-hard)
+ (cl-remf slots :max-soft))))
(defmethod initialize-instance :AFTER ((this registry-db) slots)
"Set value of data slot of THIS after initialization."
(defmethod registry-full ((db registry-db))
"Checks if registry-db THIS is full."
(>= (registry-size db)
- (oref db :max-hard)))
+ (oref db :max-size)))
(defmethod registry-insert ((db registry-db) key entry)
"Insert ENTRY under KEY into the registry-db THIS.
(assert (not (registry-full db))
nil
- "registry max-hard size limit reached")
+ "registry max-size limit reached")
;; store the entry
(puthash key entry (oref db :data))
(registry-lookup-secondary-value db tr val value-keys))))
(oref db :data))))))
-(defmethod registry-prune ((db registry-db) &optional sortfun)
- "Prunes the registry-db object THIS.
-Removes only entries without the :precious keys if it can,
-then removes oldest entries first.
-Returns the number of deleted entries.
-If SORTFUN is given, tries to keep entries that sort *higher*.
-SORTFUN is passed only the two keys so it must look them up directly."
- (dolist (collector '(registry-prune-soft-candidates
- registry-prune-hard-candidates))
- (let* ((size (registry-size db))
- (collected (funcall collector db))
- (limit (nth 0 collected))
- (candidates (nth 1 collected))
- ;; sort the candidates if SORTFUN was given
- (candidates (if sortfun (sort candidates sortfun) candidates))
- (candidates-count (length candidates))
- ;; are we over max-soft?
- (prune-needed (> size limit)))
-
- ;; while we have more candidates than we need to remove...
- (while (and (> candidates-count (- size limit)) candidates)
- (decf candidates-count)
- (setq candidates (cdr candidates)))
-
- (registry-delete db candidates nil)
- (length candidates))))
-
-(defmethod registry-prune-soft-candidates ((db registry-db))
- "Collects pruning candidates from the registry-db object THIS.
-Proposes only entries without the :precious keys."
+(defmethod registry-prune ((db registry-db) &optional sortfunc)
+ "Prunes the registry-db object DB.
+
+Attempts to prune the number of entries down to \(*
+:max-size :prune-factor\) less than the max-size limit, so
+pruning doesn't need to happen on every save. Removes only
+entries without the :precious keys, so it may not be possible to
+reach the target limit.
+
+Entries to be pruned are first sorted using SORTFUNC. Entries
+from the front of the list are deleted first.
+
+Returns the number of deleted entries."
+ (let ((size (registry-size db))
+ (target-size (- (oref db :max-size)
+ (* (oref db :max-size)
+ (oref db :prune-factor))))
+ candidates)
+ (if (> size target-size)
+ (progn
+ (setq candidates
+ (registry-collect-prune-candidates
+ db (- size target-size) sortfunc))
+ (length (registry-delete db candidates nil)))
+ 0)))
+
+(defmethod registry-collect-prune-candidates ((db registry-db) limit sortfunc)
+ "Collects pruning candidates from the registry-db object DB.
+
+Proposes only entries without the :precious keys, and attempts to
+return LIMIT such candidates. If SORTFUNC is provided, sort
+entries first and return candidates from beginning of list."
(let* ((precious (oref db :precious))
(precious-p (lambda (entry-key)
(cdr (memq (car entry-key) precious))))
(data (oref db :data))
- (limit (oref db :max-soft))
- (candidates (loop for k being the hash-keys of data
- using (hash-values v)
- when (notany precious-p v)
- collect k)))
- (list limit candidates)))
-
-(defmethod registry-prune-hard-candidates ((db registry-db))
- "Collects pruning candidates from the registry-db object THIS.
-Proposes any entries over the max-hard limit minus size * prune-factor."
- (let* ((data (oref db :data))
- ;; prune to (size * prune-factor) below the max-hard limit so
- ;; we're not pruning all the time
- (limit (max 0 (- (oref db :max-hard)
- (* (registry-size db) (oref db :prune-factor)))))
- (candidates (loop for k being the hash-keys of data
- collect k)))
- (list limit candidates)))
+ (candidates (cl-loop for k being the hash-keys of data
+ using (hash-values v)
+ when (notany precious-p v)
+ collect (cons k v))))
+ ;; We want the full entries for sorting, but should only return a
+ ;; list of entry keys.
+ (when sortfunc
+ (setq candidates (sort candidates sortfunc)))
+ (delq nil (cl-subseq (mapcar #'car candidates) 0 limit))))
(provide 'registry)
;;; registry.el ends here
(should-not (registry--match :member entry '((hello)))))
(message "Done with matching testing."))
-(defun gnustest-registry-make-testable-db (n &optional name file)
+(defun gnustest-registry-sort-function (l r)
+ "Sort lower values of sort-field earlier."
+ (< (cadr (assq 'sort-field l))
+ (cadr (assq 'sort-field r))))
+
+(defun gnustest-registry-make-testable-db (n &optional prune-factor name file)
(let* ((db (registry-db
(or name "Testing")
:file (or file "unused")
- :max-hard n
- :max-soft 0 ; keep nothing not precious
+ :max-size n
+ :prune-factor (or prune-factor 0.1)
:precious '(extra more-extra)
:tracked '(sender subject groups))))
(dotimes (i n)
(registry-insert db i `((sender "me")
(subject "about you")
- (more-extra) ; empty data key should be pruned
- ;; first 5 entries will NOT have this extra data
- ,@(when (< 5 i) (list (list 'extra "more data")))
+ (more-extra) ; Empty data key should be pruned.
+ ;; First 5 entries will NOT have this extra data.
+ ,@(when (< 4 i) (list (list 'extra "more data")))
+ (sort-field ,(- n i))
(groups ,(number-to-string i)))))
db))
(should (= n (length (registry-search db :all t))))
(message "Secondary search after delete")
(should (= n (length (registry-lookup-secondary-value db 'sender "me"))))
- ;; (message "Pruning")
- ;; (let* ((tokeep (registry-search db :member '((extra "more data"))))
- ;; (count (- n (length tokeep)))
- ;; (pruned (registry-prune db))
- ;; (prune-count (length pruned)))
- ;; (message "Expecting to prune %d entries and pruned %d"
- ;; count prune-count)
- ;; (should (and (= count 5)
- ;; (= count prune-count))))
(message "Done with usage testing.")))
+(ert-deftest gnustest-registry-pruning-test ()
+ "Check that precious entries are never pruned."
+ (let ((dbs (list
+ ;; Can prune fully without touching precious entries.
+ (gnustest-registry-make-testable-db 10 0.1)
+ ;; Pruning limited by precious entries.
+ (gnustest-registry-make-testable-db 10 0.6))))
+ (dolist (db dbs)
+ (message "Pruning")
+ (let* ((size (registry-size db))
+ (limit (- (oref db :max-size)
+ (* (oref db :max-size)
+ (oref db :prune-factor))))
+ (keepers (registry-search db :member '((extra "more data"))))
+ (expected-prune-count (min (- size (length keepers))
+ (- size limit)))
+ (actual-prune-count (registry-prune db)))
+ (ert-info
+ ((format "Expected to prune %d entries but pruned %d"
+ expected-prune-count actual-prune-count)
+ :prefix "Error: ")
+ (should (= expected-prune-count actual-prune-count)))))))
+
+(ert-deftest gnustest-registry-pruning-sort-test ()
+ "Check that entries are sorted properly before pruning."
+ (let ((db (gnustest-registry-make-testable-db 10 0.4))
+ ;; These entries have the highest 'sort-field values. Pruning
+ ;; sorts by lowest values first, then prunes from the front of
+ ;; the list, so these entries survive
+ (expected-survivors '(5 6 7 8 9 0))
+ actual-survivors disjunct)
+ (registry-prune
+ db #'gnustest-registry-sort-function)
+ (maphash (lambda (k v) (push k actual-survivors))
+ (oref db :data))
+ (setq disjunct (cl-set-exclusive-or
+ expected-survivors
+ actual-survivors))
+ (ert-info
+ ((format "Incorrect pruning: %s" disjunct)
+ :prefix "Error: ")
+ (should (null disjunct)))))
+
(ert-deftest gnustest-registry-persistence-test ()
(let* ((n 100)
(tempfile (make-temp-file "registry-persistence-"))
(name "persistence tester")
- (db (gnustest-registry-make-testable-db n name tempfile))
+ (db (gnustest-registry-make-testable-db n nil name tempfile))
size back)
(message "Saving to %s" tempfile)
(eieio-persistent-save db)
(should (= (registry-size back) n))
(should (= (registry-size back) (registry-size db)))
(delete-file tempfile)
- (message "Pruning Gnus registry to 0 by setting :max-soft")
- (oset db :max-soft 0)
+ (message "Pruning Gnus registry to 0 by setting :max-size")
+ (oset db :max-size 0)
(registry-prune db)
(should (= (registry-size db) 0)))
(message "Done with Gnus registry usage testing."))
;; value equal to HIGH * 2^16 + LOW + USEC * 10^-6 + PSEC * 10^-12
;; seconds, where missing components are treated as zero. HIGH can be
;; negative, either because the value is a time difference, or because
-;; the machine supports negative time stamps that fall before the epoch.
-;; The macro `with-decoded-time-value' and the function
-;; `encode-time-value' make it easier to deal with these formats.
-;; See `time-subtract' for an example of how to use them.
+;; it represents a time stamp before the epoch. Typically, there are
+;; more time values than the underlying system time type supports,
+;; but the reverse can also be true.
;;; Code:
Each element of the list VARLIST is a list of the form
\(HIGH-SYMBOL LOW-SYMBOL MICRO-SYMBOL [PICO-SYMBOL [TYPE-SYMBOL]] TIME-VALUE).
-The time value TIME-VALUE is decoded and the result it bound to
+The time value TIME-VALUE is decoded and the result is bound to
the symbols HIGH-SYMBOL, LOW-SYMBOL and MICRO-SYMBOL.
The optional PICO-SYMBOL is bound to the picoseconds part.
(pop elt)))
(time-value (car elt))
(gensym (make-symbol "time")))
- `(let* ,(append `((,gensym ,time-value)
+ `(let* ,(append `((,gensym (or ,time-value (current-time)))
+ (,gensym
+ (cond
+ ((integerp ,gensym)
+ (list (ash ,gensym -16)
+ (logand ,gensym 65535)))
+ ((floatp ,gensym)
+ (let* ((usec (* 1000000 (mod ,gensym 1)))
+ (ps (round (* 1000000 (mod usec 1))))
+ (us (floor usec))
+ (lo (floor (mod ,gensym 65536)))
+ (hi (floor ,gensym 65536)))
+ (if (eq ps 1000000)
+ (progn
+ (setq ps 0)
+ (setq us (1+ us))
+ (if (eq us 1000000)
+ (progn
+ (setq us 0)
+ (setq lo (1+ lo))
+ (if (eq lo 65536)
+ (progn
+ (setq lo 0)
+ (setq hi (1+ hi))))))))
+ (list hi lo us ps)))
+ (t ,gensym)))
(,high (pop ,gensym))
,low ,micro)
(when pico `(,pico))
((eq type 3) (list high low micro pico))
((null type) (encode-time-value high low micro 0 pico))))
+(when (and (fboundp 'time-add) (subrp (symbol-function 'time-add)))
+ (make-obsolete 'encode-time-value nil "25.1")
+ (make-obsolete 'with-decoded-time-value nil "25.1"))
+
(autoload 'parse-time-string "parse-time")
(autoload 'timezone-make-date-arpa-standard "timezone")
(or (featurep 'emacs)
(and (fboundp 'float-time)
(subrp (symbol-function 'float-time)))
- (defun time-to-seconds (time)
- "Convert time value TIME to a floating point number."
- (with-decoded-time-value ((high low micro pico type time))
- (+ (* 1.0 high 65536)
+ (defun time-to-seconds (&optional time)
+ "Convert optional value TIME to a floating point number.
+TIME defaults to the current time."
+ (with-decoded-time-value ((high low micro pico type
+ (or time (current-time))))
+ (+ (* high 65536.0)
low
(/ (+ (* micro 1e6) pico) 1e12))))))
;;;###autoload
(defun seconds-to-time (seconds)
- "Convert SECONDS (a floating point number) to a time value."
- (let* ((usec (* 1000000 (mod seconds 1)))
- (ps (round (* 1000000 (mod usec 1))))
- (us (floor usec))
- (lo (floor (mod seconds 65536)))
- (hi (floor seconds 65536)))
- (if (eq ps 1000000)
- (progn
- (setq ps 0)
- (setq us (1+ us))
- (if (eq us 1000000)
- (progn
- (setq us 0)
- (setq lo (1+ lo))
- (if (eq lo 65536)
- (progn
- (setq lo 0)
- (setq hi (1+ hi))))))))
- (list hi lo us ps)))
-
-;;;###autoload
-(defun time-less-p (t1 t2)
- "Return non-nil if time value T1 is earlier than time value T2."
- (with-decoded-time-value ((high1 low1 micro1 pico1 type1 t1)
- (high2 low2 micro2 pico2 type2 t2))
- (or (< high1 high2)
- (and (= high1 high2)
- (or (< low1 low2)
- (and (= low1 low2)
- (or (< micro1 micro2)
- (and (= micro1 micro2)
- (< pico1 pico2)))))))))
+ "Convert SECONDS to a time value."
+ (time-add 0 seconds))
;;;###autoload
(defun days-to-time (days)
"Convert DAYS into a time value."
- (let* ((seconds (* 1.0 days 60 60 24))
- (high (condition-case nil (floor (/ seconds 65536))
- (range-error most-positive-fixnum))))
- (list high (condition-case nil (floor (- seconds (* 1.0 high 65536)))
- (range-error 65535)))))
+ (let ((time (condition-case nil (seconds-to-time (* 86400.0 days))
+ (range-error (list most-positive-fixnum 65535)))))
+ (if (integerp days)
+ (setcdr (cdr time) nil))
+ time))
;;;###autoload
(defun time-since (time)
(when (stringp time)
;; Convert date strings to internal time.
(setq time (date-to-time time)))
- (time-subtract (current-time) time))
+ (time-subtract nil time))
;;;###autoload
(defalias 'subtract-time 'time-subtract)
-;;;###autoload
-(defun time-subtract (t1 t2)
- "Subtract two time values, T1 minus T2.
+;; These autoloads do nothing in Emacs 25, where the functions are builtin.
+;;;###autoload(autoload 'time-add "time-date")
+;;;###autoload(autoload 'time-subtract "time-date")
+;;;###autoload(autoload 'time-less-p "time-date")
+
+(eval-and-compile
+ (when (not (and (fboundp 'time-add) (subrp (symbol-function 'time-add))))
+
+ (defun time-add (t1 t2)
+ "Add two time values T1 and T2. One should represent a time difference."
+ (with-decoded-time-value ((high low micro pico type t1)
+ (high2 low2 micro2 pico2 type2 t2))
+ (setq high (+ high high2)
+ low (+ low low2)
+ micro (+ micro micro2)
+ pico (+ pico pico2)
+ type (max type type2))
+ (when (>= pico 1000000)
+ (setq micro (1+ micro)
+ pico (- pico 1000000)))
+ (when (>= micro 1000000)
+ (setq low (1+ low)
+ micro (- micro 1000000)))
+ (when (>= low 65536)
+ (setq high (1+ high)
+ low (- low 65536)))
+ (encode-time-value high low micro pico type)))
+
+ (defun time-subtract (t1 t2)
+ "Subtract two time values, T1 minus T2.
Return the difference in the format of a time value."
- (with-decoded-time-value ((high low micro pico type t1)
- (high2 low2 micro2 pico2 type2 t2))
- (setq high (- high high2)
- low (- low low2)
- micro (- micro micro2)
- pico (- pico pico2)
- type (max type type2))
- (when (< pico 0)
- (setq micro (1- micro)
- pico (+ pico 1000000)))
- (when (< micro 0)
- (setq low (1- low)
- micro (+ micro 1000000)))
- (when (< low 0)
- (setq high (1- high)
- low (+ low 65536)))
- (encode-time-value high low micro pico type)))
-
-;;;###autoload
-(defun time-add (t1 t2)
- "Add two time values T1 and T2. One should represent a time difference."
- (with-decoded-time-value ((high low micro pico type t1)
- (high2 low2 micro2 pico2 type2 t2))
- (setq high (+ high high2)
- low (+ low low2)
- micro (+ micro micro2)
- pico (+ pico pico2)
- type (max type type2))
- (when (>= pico 1000000)
- (setq micro (1+ micro)
- pico (- pico 1000000)))
- (when (>= micro 1000000)
- (setq low (1+ low)
- micro (- micro 1000000)))
- (when (>= low 65536)
- (setq high (1+ high)
- low (- low 65536)))
- (encode-time-value high low micro pico type)))
+ (with-decoded-time-value ((high low micro pico type t1)
+ (high2 low2 micro2 pico2 type2 t2))
+ (setq high (- high high2)
+ low (- low low2)
+ micro (- micro micro2)
+ pico (- pico pico2)
+ type (max type type2))
+ (when (< pico 0)
+ (setq micro (1- micro)
+ pico (+ pico 1000000)))
+ (when (< micro 0)
+ (setq low (1- low)
+ micro (+ micro 1000000)))
+ (when (< low 0)
+ (setq high (1- high)
+ low (+ low 65536)))
+ (encode-time-value high low micro pico type)))
+
+ (defun time-less-p (t1 t2)
+ "Return non-nil if time value T1 is earlier than time value T2."
+ (with-decoded-time-value ((high1 low1 micro1 pico1 type1 t1)
+ (high2 low2 micro2 pico2 type2 t2))
+ (or (< high1 high2)
+ (and (= high1 high2)
+ (or (< low1 low2)
+ (and (= low1 low2)
+ (or (< micro1 micro2)
+ (and (= micro1 micro2)
+ (< pico1 pico2)))))))))))
;;;###autoload
(defun date-to-day (date)
(not (zerop (% year 100))))
(zerop (% year 400))))
-;;;###autoload
-(defun time-to-day-in-year (time)
- "Return the day number within the year corresponding to TIME."
- (let* ((tim (decode-time time))
- (month (nth 4 tim))
+(defun time-date--day-in-year (tim)
+ "Return the day number within the year corresponding to the decoded time TIM."
+ (let* ((month (nth 4 tim))
(day (nth 3 tim))
(year (nth 5 tim))
(day-of-year (+ day (* 31 (1- month)))))
(setq day-of-year (1+ day-of-year))))
day-of-year))
+;;;###autoload
+(defun time-to-day-in-year (time)
+ "Return the day number within the year corresponding to TIME."
+ (time-date--day-in-year (decode-time time)))
+
;;;###autoload
(defun time-to-days (time)
"The number of days between the Gregorian date 0001-12-31bce and TIME.
The Gregorian date Sunday, December 31, 1bce is imaginary."
(let* ((tim (decode-time time))
(year (nth 5 tim)))
- (+ (time-to-day-in-year time) ; Days this year
+ (+ (time-date--day-in-year tim) ; Days this year
(* 365 (1- year)) ; + Days in prior years
(/ (1- year) 4) ; + Julian leap years
(- (/ (1- year) 100)) ; - century years
+2014-12-18 Eric Abrahamsen <eric@ericabrahamsen.net>
+
+ * gnus.texi (Gnus Registry Setup): Explain pruning changes. Mention
+ gnus-registry-prune-factor. Explain sorting changes and
+ gnus-registry-default-sort-function. Correct file extension.
+
+2014-11-10 Katsumi Yamaoka <yamaoka@jpl.org>
+
+ * gnus.texi (Top): Add missing `HTML' menu.
+ (HTML): Fix xref to FAQ 4-16.
+
+2014-11-07 Tassilo Horn <tsdh@gnu.org>
+
+ * gnus.texi (HTML): Update section so that it mentions shr and w3m.
+ Also link the full EWW manual that explains more on shr, too.
+
+ * gnus-faq.texi (FAQ 4 - Reading messages, FAQ 4-16): Add Q&A on how to
+ increase contrast when displaying HTML mail with shr.
+
+2014-11-02 Teodor Zlatanov <tzz@lifelogs.com>
+
+ * auth.texi (Help for users): Explain quoting rules better.
+
+2014-10-25 Eric S. Raymond <esr@thyrsus.com>
+
+ * gnus-coding.texi: Neutralized language specific to a repository type.
+
2014-07-31 Tassilo Horn <tsdh@gnu.org>
* gnus.texi (Group Parameters): Document that `gcc-self' may also be a
@code{auth-source-search} queries. You can also use @code{login} and
@code{account}.
-Spaces are always OK as far as auth-source is concerned (but other
-programs may not like them). Just put the data in quotes, escaping
-quotes as you'd expect with @samp{\}.
+You can use spaces inside a password or other token by surrounding the
+token with either single or double quotes.
-All these are optional. You could just say (but we don't recommend
-it, we're just showing that it's possible)
+You can use single quotes inside a password or other token by
+surrounding it with double quotes, e.g. @code{"he'llo"}. Similarly you
+can use double quotes inside a password or other token by surrounding
+it with single quotes, e.g. @code{'he"llo'}. You can't mix both (so a
+password or other token can't have both single and double quotes).
+
+All this is optional. You could just say (but we don't recommend it,
+we're just showing that it's possible)
@example
password @var{mypassword}
new @file{encrypt.el}), you should probably make the change in the Emacs
tree, and it will show up in the Gnus tree a few days later.
-If you don't have Emacs bzr access (or it's inconvenient), you can
-change such a file in the v5-10 branch, and it should propagate to Emacs
-bzr---however, it will get some extra scrutiny (by Miles) to see if the
-changes are possibly controversial and need discussion on the mailing
-list. Many changes are obvious bug-fixes however, so often there won't
-be any problem.
+If you don't have Emacs repository access (or it's inconvenient), you
+can change such a file in the v5-10 branch, and it should propagate to
+the Emacs repository---however, it will get some extra scrutiny (by
+Miles) to see if the changes are possibly controversial and need
+discussion on the mailing list. Many changes are obvious bug-fixes
+however, so often there won't be any problem.
@item
If it's to a Gnus file, and it's important enough that it should be part
of Emacs and the v5-10 branch, then you can make the change on the v5-10
-branch, and it will go into Emacs bzr and the Gnus git trunk (a few days
+branch, and it will go into Emacs and the Gnus git trunk (a few days
later). The most prominent examples for such changes are bug-fixed
including improvements on the documentation.
* FAQ 4-14:: I don't like the way the Summary buffer looks, how to
tweak it?
* FAQ 4-15:: How to split incoming mails in several groups?
+* FAQ 4-16:: How can I ensure more contrast when viewing HTML mail?
@end menu
@node FAQ 4-1
@end example
@noindent
+@node FAQ 4-16
+@subsubheading Question 4.16
+
+How can I ensure more contrast when viewing HTML mail?
+
+@subsubheading Answer
+
+Gnus' built-in simple HTML renderer (you use it if the value of
+@code{mm-text-html-renderer} is @code{shr}) uses the colors which are
+declared in the HTML mail. However, it adjusts them in order to
+prevent situations like dark gray text on black background. In case
+the results still have a too low contrast for you, increase the values
+of the variables @code{shr-color-visible-distance-min} and
+@code{shr-color-visible-luminance-min}.
+
@node FAQ 5 - Composing messages
@subsection Composing messages
@subsubheading Answer
-The message-ID is an unique identifier for messages you
+The message-ID is a unique identifier for messages you
send. To make it unique, Gnus need to know which machine
name to put after the "@@". If the name of the machine
where Gnus is running isn't suitable (it probably isn't
* Hiding Headers:: Deciding what headers should be displayed.
* Using MIME:: Pushing articles through @acronym{MIME} before reading them.
+* HTML:: Reading @acronym{HTML} messages.
* Customizing Articles:: Tailoring the look of the articles.
* Article Keymap:: Keystrokes available in the article buffer.
* Misc Article:: Other stuff.
@section @acronym{HTML}
@cindex @acronym{HTML}
-If you have @code{w3m} installed on your system, Gnus can display
-@acronym{HTML} articles in the article buffer. There are many Gnus
-add-ons for doing this, using various approaches, but there's one
-(sort of) built-in method that's used by default.
+Gnus can display @acronym{HTML} articles nicely formatted in the
+article buffer. There are many methods for doing that, but two of
+them are kind of default methods.
+
+If your Emacs copy has been built with libxml2 support, then Gnus uses
+Emacs' built-in, plain elisp Simple HTML Renderer @code{shr}
+@footnote{@code{shr} displays colors as declared in the @acronym{HTML}
+article but tries to adjust them in order to be readable. If you
+prefer more contrast, @xref{FAQ 4-16}.} which is also used by Emacs'
+browser EWW (@pxref{EWW, ,EWW, emacs, The Emacs Manual}).
+
+If your Emacs copy lacks libxml2 support but you have @code{w3m}
+installed on your system, Gnus uses that to render @acronym{HTML} mail
+and display the results in the article buffer (@code{gnus-w3m}).
-For a complete overview, consult @xref{Display Customization,
-,Display Customization, emacs-mime, The Emacs MIME Manual}. This
-section only describes the default method.
+For a complete overview, consult @xref{Display Customization, ,Display
+Customization, emacs-mime, The Emacs MIME Manual}. This section only
+describes the default method.
@table @code
@item mm-text-html-renderer
@vindex mm-text-html-renderer
-If set to @code{gnus-article-html}, Gnus will use the built-in method,
-that's based on @code{w3m}.
+If set to @code{shr}, Gnus uses its own simple @acronym{HTML}
+renderer. If set to @code{gnus-w3m}, it uses @code{w3m}.
@item gnus-blocked-images
@vindex gnus-blocked-images
@defvar gnus-registry-max-entries
The number (an integer or @code{nil} for unlimited) of entries the
-registry will keep.
+registry will keep. If the registry has reached or exceeded this
+size, it will reject insertion of new entries.
+@end defvar
+
+@defvar gnus-registry-prune-factor
+This option (a float between 0 and 1) controls how much the registry
+is cut back during pruning. In order to prevent constant pruning, the
+registry will be pruned back to less than
+@code{gnus-registry-max-entries}. This option controls exactly how
+much less: the target is calculated as the maximum number of entries
+minus the maximum number times this factor. The default is 0.1:
+i.e. if your registry is limited to 50000 entries, pruning will try to
+cut back to 45000 entries. Entries with keys marked as precious will
+not be pruned.
@end defvar
-@defvar gnus-registry-max-pruned-entries
-The maximum number (an integer or @code{nil} for unlimited) of entries
-the registry will keep after pruning.
+@defvar gnus-registry-default-sort-function
+This option specifies how registry entries are sorted during pruning.
+If a function is given, it should sort least valuable entries first,
+as pruning starts from the beginning of the list. The default value
+is @code{gnus-registry-sort-by-creation-time}, which proposes the
+oldest entries for pruning. Set to nil to perform no sorting, which
+will speed up the pruning process.
@end defvar
@defvar gnus-registry-cache-file
The file where the registry will be stored between Gnus sessions. By
-default the file name is @code{.gnus.registry.eioio} in the same
+default the file name is @code{.gnus.registry.eieio} in the same
directory as your @code{.newsrc.eld}.
@end defvar