From daea9b3b44a4dc09017c3d2d5b8eaa4c68cdd2b0 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 1 Nov 2021 14:51:57 +0100 Subject: [PATCH] Read mailcaps again only when necessary * doc/lispref/files.texi (File Attributes): Document it. * lisp/files.el (file-has-changed-p): New function. (file-has-changed-p--hash-table): Internal variable used by the new function (bug#51523). * lisp/emacs-lisp/shortdoc.el (file): Mention it. * lisp/net/mailcap.el (mailcap-parse-mailcaps): Read mailcaps again only when at least one of the mailcap files has changed. Fixes bug#51523. --- doc/lispref/files.texi | 9 +++++++++ etc/NEWS | 5 +++++ lisp/emacs-lisp/shortdoc.el | 3 +++ lisp/files.el | 16 ++++++++++++++++ lisp/net/mailcap.el | 25 +++++++++++++------------ 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 59e18b32c2e..250f7a3f9f5 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1314,6 +1314,15 @@ on the 19th, @file{aug-20} was written on the 20th, and the file @end example @end defun +@defun file-has-changed-p filename +This convenience function is useful when, for instance, parsing files +run-time, and you typically want to re-read a file when it has +changed. This function returns non-@code{nil} the first time it's +called on @var{filename} in an Emacs session, but will return +@code{nil} on subsequent calls in that session (unless the file +changes its modification time). +@end defun + @defun file-attributes filename &optional id-format @anchor{Definition of file-attributes} This function returns a list of attributes of file @var{filename}. If diff --git a/etc/NEWS b/etc/NEWS index 57c64f7cadb..62cfb79a2ab 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -360,6 +360,11 @@ Use 'exif-parse-file' and 'exif-field' instead. * Lisp Changes in Emacs 29.1 +*** New function 'file-has-changed-p'. +This convenience function is useful when writing code that parses +files run-time, and allows you to easily re-parse files when they have +changed (but not otherwise). + --- *** New function 'font-has-char-p'. This can be used to check whether a specific font has a glyph for a diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index daf362dd88b..c3d6c742940 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -358,6 +358,9 @@ There can be any number of :example/:result elements." (file-newer-than-file-p :no-eval (file-newer-than-file-p "/tmp/foo" "/tmp/bar") :eg-result nil) + (file-has-changed-p + :no-eval (file-has-changed-p "/tmp/foo") + :eg-result t) (file-equal-p :no-eval (file-equal-p "/tmp/foo" "/tmp/bar") :eg-result nil) diff --git a/lisp/files.el b/lisp/files.el index 1e65d0ce835..5e7be3844eb 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6181,6 +6181,22 @@ Return nil if DIR is not an existing directory." (unless mismatch (file-equal-p root dir))))))) +(defvar file-has-changed-p--hash-table (make-hash-table) + "Internal variable used by `file-has-changed-p'.") + +(defun file-has-changed-p (file) + "Return non-nil if FILE has changed. +The modification time of FILE is compared to the modification +time of FILE during a previous invocation of `file-has-changed-p'. +Therefore the first invocation of `file-has-changed-p' always +returns non-nil." + (let* ((attr (file-attributes file 'integer)) + (mtime (file-attribute-modification-time attr)) + (saved-mtime (gethash (intern file) + file-has-changed-p--hash-table))) + (when (not (equal mtime saved-mtime)) + (puthash (intern file) mtime file-has-changed-p--hash-table)))) + (defun copy-directory (directory newname &optional keep-time parents copy-contents) "Copy DIRECTORY to NEWNAME. Both args must be strings. This function always sets the file modes of the output files to match diff --git a/lisp/net/mailcap.el b/lisp/net/mailcap.el index 83d0eeef9f1..4dedd38c22c 100644 --- a/lisp/net/mailcap.el +++ b/lisp/net/mailcap.el @@ -447,18 +447,19 @@ MAILCAPS if set; otherwise (on Unix) use the path from RFC 1524, plus ("/etc/mailcap" system) ("/usr/etc/mailcap" system) ("/usr/local/etc/mailcap" system))))) - ;; The ~/.mailcap entries will end up first in the resulting data. - (dolist (spec (reverse - (if (stringp path) - (split-string path path-separator t) - path))) - (let ((source (and (consp spec) (cadr spec))) - (file-name (if (stringp spec) - spec - (car spec)))) - (when (and (file-readable-p file-name) - (file-regular-p file-name)) - (mailcap-parse-mailcap file-name source)))) + (when (seq-some (lambda (f) (file-has-changed-p (car f))) path) + ;; The ~/.mailcap entries will end up first in the resulting data. + (dolist (spec (reverse + (if (stringp path) + (split-string path path-separator t) + path))) + (let ((source (and (consp spec) (cadr spec))) + (file-name (if (stringp spec) + spec + (car spec)))) + (when (and (file-readable-p file-name) + (file-regular-p file-name)) + (mailcap-parse-mailcap file-name source))))) (setq mailcap-parsed-p t))) (defun mailcap-parse-mailcap (fname &optional source) -- 2.39.5