From 13fdded2c19823d9216b391d0636345029cf3e81 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Fri, 20 Aug 2021 16:20:51 +0200 Subject: Allow preserving symlinks with file-precious-flag set * doc/lispref/files.texi (Saving Buffers): Document it. * lisp/files.el (file-preserve-symlinks-on-save): New user option (bug#18125). (basic-save-buffer-2): Use it. --- doc/lispref/files.texi | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 266501d46d0..12c06111374 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -503,6 +503,13 @@ all hard links between the file you save and other file names. Some modes give this variable a non-@code{nil} buffer-local value in particular buffers. + +@vindex file-preserve-symlinks-on-save +If this option is non-@code{nil} and you're visiting files via a +symbolic link, Emacs break the symbolic link and write the buffer to a +file with the same name as the symbolic link. To instead write to the +file the symbolic link points to (and thereby preserving the link), +set @code{file-preserve-symlinks-on-save} to @code{t}. @end defopt @defopt require-final-newline -- cgit v1.2.3 From beb54dc1b2b00cb4541b82acf6ead8a8075c3011 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 21 Aug 2021 10:58:24 +0300 Subject: Improve documentation of 'file-preserve-symlinks-on-save' * lisp/files.el (file-precious-flag): Mention 'file-preserve-symlinks-on-save' in the doc string. * doc/lispref/files.texi (Saving Buffers): Move the description of 'file-preserve-symlinks-on-save' from here... * doc/emacs/files.texi (Customize Save): ...to here. Improve wording. * etc/NEWS: Fix wording of 'file-preserve-symlinks-on-save' entry. --- doc/emacs/files.texi | 11 +++++++++++ doc/lispref/files.texi | 7 ------- etc/NEWS | 4 ++-- lisp/files.el | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi index 207c951a875..9aae0e9a0b3 100644 --- a/doc/emacs/files.texi +++ b/doc/emacs/files.texi @@ -742,6 +742,17 @@ always supposed to end in newlines. Such major modes set the variable setting the latter variable, you can control how these modes handle final newlines. +@vindex file-preserve-symlinks-on-save +If this option is non-@code{nil} and you're visiting a file via a +symbolic link, Emacs will break the symbolic link upon saving the +buffer, and will write the buffer to a file with the same name as the +symbolic link, if the value of @code{file-precious-flag} is +non-@code{nil} (@pxref{Saving Buffers, file-precious-flag,, elisp, The +Emacs Lisp Reference Manual}). If you want Emacs to save the buffer +to the file the symbolic link points to (thereby preserving the link) +in these cases, customize the variable +@code{file-preserve-symlinks-on-save} to @code{t}. + @vindex write-region-inhibit-fsync Normally, when a program writes a file, the operating system briefly caches the file's data in main memory before committing the data to diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 12c06111374..266501d46d0 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -503,13 +503,6 @@ all hard links between the file you save and other file names. Some modes give this variable a non-@code{nil} buffer-local value in particular buffers. - -@vindex file-preserve-symlinks-on-save -If this option is non-@code{nil} and you're visiting files via a -symbolic link, Emacs break the symbolic link and write the buffer to a -file with the same name as the symbolic link. To instead write to the -file the symbolic link points to (and thereby preserving the link), -set @code{file-preserve-symlinks-on-save} to @code{t}. @end defopt @defopt require-final-newline diff --git a/etc/NEWS b/etc/NEWS index cdc70d6fef0..ec7e54e2dde 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2462,8 +2462,8 @@ images are marked. +++ *** New user option 'file-preserve-symlinks-on-save'. -This controls what Emacs does when saving buffers visited via a -symbolic link, and 'file-precious-flag' is non-nil. +This controls what Emacs does when saving buffers that visit files via +symbolic links, and 'file-precious-flag' is non-nil. +++ *** New user option 'copy-directory-create-symlink'. diff --git a/lisp/files.el b/lisp/files.el index 6a617feca49..f0baa4fac6d 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -257,7 +257,7 @@ This feature is advisory: for example, if the directory in which the file is being saved is not writable, Emacs may ignore a non-nil value of `file-precious-flag' and write directly into the file. -See also: `break-hardlink-on-save'." +See also: `break-hardlink-on-save' and `file-preserve-symlinks-on-save'." :type 'boolean :group 'backup) -- cgit v1.2.3 From 6a6de68dafd27238577e92a7a79e97f3f1a6e381 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Wed, 1 Sep 2021 12:13:19 +0200 Subject: Add new macro `with-existing-directory' * doc/lispref/files.texi (Testing Accessibility): Document it. * lisp/subr.el (with-existing-directory): New macro (bug#32004). --- doc/lispref/files.texi | 10 ++++++++++ etc/NEWS | 5 +++++ lisp/subr.el | 13 +++++++++++++ test/lisp/subr-tests.el | 8 ++++++++ 4 files changed, 36 insertions(+) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 266501d46d0..d1045704527 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -936,6 +936,16 @@ file in @file{/foo/} will give an error: @end example @end defun +@defmac with-existing-directory body@dots{} +This macro ensures that @code{default-directory} is bound to an +existing directory before executing @var{body}. If +@code{default-directory} already exists, that's preferred, and +otherwise some other directory is used. This macro can be useful, for +instance, when calling an external command that requires that it's +running in a directory that exists. The chosen directory is not +guaranteed to be writable. +@end defmac + @defun access-file filename string If you can read @var{filename} this function returns @code{nil}; otherwise it signals an error diff --git a/etc/NEWS b/etc/NEWS index 0e1edd648d0..6f6b8e108fe 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3422,6 +3422,11 @@ The former is now declared obsolete. * Lisp Changes in Emacs 28.1 ++++ +*** New macro 'with-existing-directory'. +This macro binds 'default-directory' to some other existing directory +if 'default-directory' doesn't exist, and then executes the body forms. + +++ *** New function 'file-name-concat'. This appends file name components to a directory name and returns the diff --git a/lisp/subr.el b/lisp/subr.el index 0a31ef2b29f..7426dcce50f 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -4593,6 +4593,19 @@ MODES is as for `set-default-file-modes'." ,@body) (set-default-file-modes ,umask))))) +(defmacro with-existing-directory (&rest body) + "Execute BODY with `default-directory' bound to an existing directory. +If `default-directory' is already an existing directory, it's not changed." + (declare (indent 0) (debug t)) + `(let ((default-directory (seq-find (lambda (dir) + (and dir + (file-exists-p dir))) + (list default-directory + (expand-file-name "~/") + (getenv "TMPDIR") + "/tmp/") + "/"))) + ,@body)) ;;; Matching and match data. diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 21b8a27858e..c1f8225ec8a 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -740,5 +740,13 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350." 1)) (should (equal (buffer-string) "new bar zot foobar")))) +(ert-deftest test-with-existing-directory () + (let ((dir (make-temp-name "/tmp/not-exist-"))) + (let ((default-directory dir)) + (should-not (file-exists-p default-directory))) + (with-existing-directory + (should-not (equal dir default-directory)) + (should (file-exists-p default-directory))))) + (provide 'subr-tests) ;;; subr-tests.el ends here -- cgit v1.2.3 From 252a769b11689d13796db6f76c2935374796b079 Mon Sep 17 00:00:00 2001 From: Stephen Gildea Date: Fri, 10 Sep 2021 20:21:34 -0700 Subject: ; * doc/lispref/files.texi (Changing Files): Fix xref to file-modes. 'file-modes' is in node "Testing Accessibility", not "File Attributes". --- doc/lispref/files.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 4235c4900a3..3b1f2d5cb80 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1824,7 +1824,7 @@ Interactively, @var{mode} is read from the minibuffer using @code{read-file-modes} (see below), which lets the user type in either an integer or a string representing the permissions symbolically. -@xref{File Attributes}, for the function @code{file-modes}, which +@xref{Testing Accessibility}, for the function @code{file-modes}, which returns the permissions of a file. @end deffn -- cgit v1.2.3 From 3cc77aa976f356f2496bd8471cf68b44d26c22a7 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Sun, 3 Oct 2021 18:13:23 +0000 Subject: Clarify (elisp) insert-file-contents with BEG or END not on character boundary * doc/lispref/files.texi (Reading from files): When the argument BEG or END to insert-file-contents are at a byte position not at a character boundary, clarify that raw bytes get inserted, and how to handle this awkwardness in Lisp. Also clarify that insert-file-contents-literally is intended to insert raw bytes into the buffer. Fix the outdated example that states it inserts 500 characters, when it actually inserts 500 bytes. --- doc/lispref/files.texi | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 2dc808e6945..e73f53b040d 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -563,7 +563,17 @@ In this case, @var{visit} must be @code{nil}. For example, @end example @noindent -inserts the first 500 characters of a file. +inserts the characters coded by the first 500 bytes of a file. + +If @var{beg} or @var{end} fails to be at a character boundary, Emacs's +character code conversion will insert one or more raw-text characters +(@pxref{Coding System Basics}) into the buffer. If you want to read +part of a file this way, you are recommended to bind +@code{coding-system-for-read} to a suitable value around the call to +this function (@pxref{Specifying Coding Systems}), and to write Lisp +code which will check for raw-text characters at the boundaries, read +the rest of these characters from the file, and convert them back to +valid characters. If the argument @var{replace} is non-@code{nil}, it means to replace the contents of the buffer (actually, just the accessible portion) with the @@ -577,10 +587,11 @@ with @code{insert-file-contents}, as long as @var{replace} and @end defun @defun insert-file-contents-literally filename &optional visit beg end replace -This function works like @code{insert-file-contents} except that it -does not run @code{after-insert-file-functions}, and does not do -format decoding, character code conversion, automatic uncompression, -and so on. +This function works like @code{insert-file-contents} except that each +byte in the file is handled separately, being converted into a +raw-text character if needed. It does not run +@code{after-insert-file-functions}, and does not do format decoding, +character code conversion, automatic uncompression, and so on. @end defun If you want to pass a file name to another process so that another -- cgit v1.2.3 From fc32a3bd958797ad4392a97b66e52bff420ac399 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 3 Oct 2021 21:30:34 +0300 Subject: ; * doc/lispref/files.texi (Reading from Files): Fix wording. --- doc/lispref/files.texi | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index e73f53b040d..1e05153f3c0 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -565,15 +565,15 @@ In this case, @var{visit} must be @code{nil}. For example, @noindent inserts the characters coded by the first 500 bytes of a file. -If @var{beg} or @var{end} fails to be at a character boundary, Emacs's -character code conversion will insert one or more raw-text characters -(@pxref{Coding System Basics}) into the buffer. If you want to read -part of a file this way, you are recommended to bind -@code{coding-system-for-read} to a suitable value around the call to -this function (@pxref{Specifying Coding Systems}), and to write Lisp -code which will check for raw-text characters at the boundaries, read -the rest of these characters from the file, and convert them back to -valid characters. +If @var{beg} or @var{end} happens to be in the middle of a character's +multibyte sequence, Emacs's character code conversion will insert one +or more eight-bit characters (a.k.a.@: ``raw bytes'') +(@pxref{Character Sets}) into the buffer. If you want to read part of +a file this way, we recommend to bind @code{coding-system-for-read} to +a suitable value around the call to this function (@pxref{Specifying +Coding Systems}), and to write Lisp code which will check for raw +bytes at the boundaries, read the entire sequence of these bytes, and +convert them back to valid characters. If the argument @var{replace} is non-@code{nil}, it means to replace the contents of the buffer (actually, just the accessible portion) with the @@ -588,8 +588,8 @@ with @code{insert-file-contents}, as long as @var{replace} and @defun insert-file-contents-literally filename &optional visit beg end replace This function works like @code{insert-file-contents} except that each -byte in the file is handled separately, being converted into a -raw-text character if needed. It does not run +byte in the file is handled separately, being converted into an +eight-bit character if needed. It does not run @code{after-insert-file-functions}, and does not do format decoding, character code conversion, automatic uncompression, and so on. @end defun -- cgit v1.2.3 From 9b70b1aad9fb817f914d8643118c4d5b47a1378c Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Tue, 19 Oct 2021 17:18:05 -0400 Subject: * doc/lispref/files.texi (File Name Components): Mention GNU "path" convention --- doc/lispref/files.texi | 3 +++ 1 file changed, 3 insertions(+) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 1e05153f3c0..59e18b32c2e 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -2083,6 +2083,9 @@ directory. Therefore, Emacs considers a file name as having two main parts: the @dfn{directory name} part, and the @dfn{nondirectory} part (or @dfn{file name within the directory}). Either part may be empty. Concatenating these two parts reproduces the original file name. +@footnote{Emacs follows the GNU convention to use the term @emph{file name} +instead of the term @emph{pathname}. We use the term @emph{path} only for +search paths, which are lists of directory names.} On most systems, the directory part is everything up to and including the last slash (backslash is also allowed in input on MS-DOS or -- cgit v1.2.3 From daea9b3b44a4dc09017c3d2d5b8eaa4c68cdd2b0 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 1 Nov 2021 14:51:57 +0100 Subject: 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(-) (limited to 'doc/lispref/files.texi') 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) -- cgit v1.2.3 From dfc89318ceddaa6801746f53c400cacb9685aabe Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Mon, 1 Nov 2021 17:09:52 +0200 Subject: Improve documentation of 'file-has-changed-p' * etc/NEWS: * doc/lispref/files.texi (File Attributes): Improve wording of the documentation of 'file-has-changed-p'. --- doc/lispref/files.texi | 14 ++++++++------ etc/NEWS | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 250f7a3f9f5..b7420fc678f 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1315,12 +1315,14 @@ on the 19th, @file{aug-20} was written on the 20th, and the file @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). +This function returns non-@code{nil} if the time stamp of +@var{filename} has changed since the last call. When called for the +first time for some @var{filename}, it records the last modification +time of the file and returns non-@code{nil}. Thereafter, when called +for the same @var{filename}, it compares the current time stamp with +the recorded one and returns non-@code{nil} only if they are +different. This is useful when a Lisp program wants to re-read a file +whenever it changes. @end defun @defun file-attributes filename &optional id-format diff --git a/etc/NEWS b/etc/NEWS index 114441f1b6a..73f76076d46 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -369,8 +369,8 @@ Use 'exif-parse-file' and 'exif-field' instead. +++ *** 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). +files at run-time, and allows Lisp programs to re-parse files only +when they have changed. --- *** New function 'font-has-char-p'. -- cgit v1.2.3 From 53a9a2de456b2db8d126bbc71becd5ae86a1f172 Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Tue, 2 Nov 2021 15:50:31 +0100 Subject: Improve file-has-changed-p * doc/lispref/files.texi: Update the documentation. * lisp/net/mailcap.el: Add a second argument to the call to file-has-changed-p (bug#51523). --- doc/lispref/files.texi | 14 ++++++++------ lisp/files.el | 28 ++++++++++++++++------------ lisp/net/mailcap.el | 4 +++- 3 files changed, 27 insertions(+), 19 deletions(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index b7420fc678f..84da6eba2fc 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1314,15 +1314,17 @@ on the 19th, @file{aug-20} was written on the 20th, and the file @end example @end defun -@defun file-has-changed-p filename +@defun file-has-changed-p filename tag This function returns non-@code{nil} if the time stamp of @var{filename} has changed since the last call. When called for the first time for some @var{filename}, it records the last modification -time of the file and returns non-@code{nil}. Thereafter, when called -for the same @var{filename}, it compares the current time stamp with -the recorded one and returns non-@code{nil} only if they are -different. This is useful when a Lisp program wants to re-read a file -whenever it changes. +time ans size of the file and returns non-@code{nil}. Thereafter, +when called for the same @var{filename}, it compares the current time +stamp and size with the recorded one and returns non-@code{nil} only +if they are different. This is useful when a Lisp program wants to +re-read a file whenever it changes. With an optional argument +@var{tag}, the size and modification time comparisons are limited to +calls with the same tag. @end defun @defun file-attributes filename &optional id-format diff --git a/lisp/files.el b/lisp/files.el index 5e7be3844eb..d7dfa9399e2 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6181,21 +6181,25 @@ 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) +(defvar file-has-changed-p--hash-table (make-hash-table :test #'equal) "Internal variable used by `file-has-changed-p'.") -(defun file-has-changed-p (file) +(defun file-has-changed-p (file &optional tag) "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)))) +The size and modification time of FILE is compared to the size +and 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. +The optional argument TAG can be used to limit the comparison to +invocations with identical tags; it can for example be the symbol +of the calling function." + (let* ((fileattr (file-attributes file 'integer)) + (attr (cons (file-attribute-size fileattr) + (file-attribute-modification-time fileattr))) + (sym (concat (symbol-name tag) "@" file)) + (cachedattr (gethash sym file-has-changed-p--hash-table))) + (when (not (equal attr cachedattr)) + (puthash sym attr 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. diff --git a/lisp/net/mailcap.el b/lisp/net/mailcap.el index 4dedd38c22c..e40cf2a336d 100644 --- a/lisp/net/mailcap.el +++ b/lisp/net/mailcap.el @@ -447,7 +447,9 @@ 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))))) - (when (seq-some (lambda (f) (file-has-changed-p (car f))) path) + (when (seq-some (lambda (f) + (file-has-changed-p (car f) 'mail-parse-mailcaps)) + path) ;; The ~/.mailcap entries will end up first in the resulting data. (dolist (spec (reverse (if (stringp path) -- cgit v1.2.3 From 4f851c2357cb8a09024432f2c6e061907311ce43 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 2 Nov 2021 17:09:05 +0200 Subject: ; Improve documentation of a recent change * lisp/files.el (file-has-changed-p): TAG must be a symbol. * doc/lispref/files.texi (File Attributes): Fix typos. --- doc/lispref/files.texi | 13 +++++++------ lisp/files.el | 14 +++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 84da6eba2fc..24f059ea4c6 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1318,13 +1318,14 @@ on the 19th, @file{aug-20} was written on the 20th, and the file This function returns non-@code{nil} if the time stamp of @var{filename} has changed since the last call. When called for the first time for some @var{filename}, it records the last modification -time ans size of the file and returns non-@code{nil}. Thereafter, +time and size of the file, and returns non-@code{nil}. Thereafter, when called for the same @var{filename}, it compares the current time -stamp and size with the recorded one and returns non-@code{nil} only -if they are different. This is useful when a Lisp program wants to -re-read a file whenever it changes. With an optional argument -@var{tag}, the size and modification time comparisons are limited to -calls with the same tag. +stamp and size with the recorded ones, and returns non-@code{nil} only +if either the time stamp or the size (or both) are different. This is +useful when a Lisp program wants to re-read a file whenever it +changes. With an optional argument @var{tag}, which must be a symbol, +the size and modification time comparisons are limited to calls with +the same tag. @end defun @defun file-attributes filename &optional id-format diff --git a/lisp/files.el b/lisp/files.el index d7dfa9399e2..173198a4246 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6186,13 +6186,13 @@ Return nil if DIR is not an existing directory." (defun file-has-changed-p (file &optional tag) "Return non-nil if FILE has changed. -The size and modification time of FILE is compared to the size -and 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. -The optional argument TAG can be used to limit the comparison to -invocations with identical tags; it can for example be the symbol -of the calling function." +The size and modification time of FILE are compared to the size +and modification time of tghe same FILE during a previous +invocation of `file-has-changed-p'. Thus, the first invocation +of `file-has-changed-p' always returns non-nil. +The optional argument TAG, which must be a symbol, can be used to +limit the comparison to invocations with identical tags; it can be +the symbol of the calling function, for example." (let* ((fileattr (file-attributes file 'integer)) (attr (cons (file-attribute-size fileattr) (file-attribute-modification-time fileattr))) -- cgit v1.2.3 From a08a9a9aead5c03a9e0eba3b2e6f5117283cde96 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Fri, 5 Nov 2021 20:46:35 +0100 Subject: Improve `file-has-changed-p' * doc/lispref/files.texi (File Attributes): Be precise when filename does not exist in `file-has-changed-p'. * lisp/files.el (file-has-changed-p): Suppress remote file caches. Handle the case FILE does not exists. --- doc/lispref/files.texi | 16 ++++++++-------- lisp/files.el | 14 +++++++++----- 2 files changed, 17 insertions(+), 13 deletions(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 24f059ea4c6..ddc1d05c1ca 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1318,14 +1318,14 @@ on the 19th, @file{aug-20} was written on the 20th, and the file This function returns non-@code{nil} if the time stamp of @var{filename} has changed since the last call. When called for the first time for some @var{filename}, it records the last modification -time and size of the file, and returns non-@code{nil}. Thereafter, -when called for the same @var{filename}, it compares the current time -stamp and size with the recorded ones, and returns non-@code{nil} only -if either the time stamp or the size (or both) are different. This is -useful when a Lisp program wants to re-read a file whenever it -changes. With an optional argument @var{tag}, which must be a symbol, -the size and modification time comparisons are limited to calls with -the same tag. +time and size of the file, and returns non-@code{nil} when +@var{filename} exists. Thereafter, when called for the same +@var{filename}, it compares the current time stamp and size with the +recorded ones, and returns non-@code{nil} only if either the time +stamp or the size (or both) are different. This is useful when a Lisp +program wants to re-read a file whenever it changes. With an optional +argument @var{tag}, which must be a symbol, the size and modification +time comparisons are limited to calls with the same tag. @end defun @defun file-attributes filename &optional id-format diff --git a/lisp/files.el b/lisp/files.el index 173198a4246..3af97303268 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6187,15 +6187,19 @@ Return nil if DIR is not an existing directory." (defun file-has-changed-p (file &optional tag) "Return non-nil if FILE has changed. The size and modification time of FILE are compared to the size -and modification time of tghe same FILE during a previous +and modification time of the same FILE during a previous invocation of `file-has-changed-p'. Thus, the first invocation -of `file-has-changed-p' always returns non-nil. +of `file-has-changed-p' always returns non-nil when FILE exists. The optional argument TAG, which must be a symbol, can be used to limit the comparison to invocations with identical tags; it can be the symbol of the calling function, for example." - (let* ((fileattr (file-attributes file 'integer)) - (attr (cons (file-attribute-size fileattr) - (file-attribute-modification-time fileattr))) + (let* (;; FIXME: Shall we use `file-truename'? + (file (directory-file-name file)) + (remote-file-name-inhibit-cache t) + (fileattr (file-attributes file 'integer)) + (attr (and fileattr + (cons (file-attribute-size fileattr) + (file-attribute-modification-time fileattr)))) (sym (concat (symbol-name tag) "@" file)) (cachedattr (gethash sym file-has-changed-p--hash-table))) (when (not (equal attr cachedattr)) -- cgit v1.2.3 From 2e6ed253ce485698df649904bd9e5254a3f4bf94 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Wed, 10 Nov 2021 00:26:32 +0100 Subject: Add new function 'file-name-split' * doc/lispref/files.texi (File Name Components): Document it. * lisp/files.el (file-name-split): New function (bug#50572). * lisp/emacs-lisp/shortdoc.el (file-name): Mention it. --- doc/lispref/files.texi | 13 +++++++++++++ etc/NEWS | 4 ++++ lisp/emacs-lisp/shortdoc.el | 3 +++ lisp/files.el | 23 +++++++++++++++++++++++ test/lisp/files-tests.el | 6 ++++++ 5 files changed, 49 insertions(+) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index ddc1d05c1ca..dd058b12158 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -2244,6 +2244,19 @@ and @code{file-name-nondirectory}. For example, @end example @end defun +@defun file-name-split filename +This function splits a file name into its components, and can be +thought of as the inverse of @code{string-joing} with the appropriate +directory separator. For example, + +@example +(file-name-split "/tmp/foo.txt") + @result{} ("" "tmp" "foo.txt") +(string-join (file-name-split "/tmp/foo.txt") "/") + @result{} "/tmp/foo.txt" +@end example +@end defun + @node Relative File Names @subsection Absolute and Relative File Names @cindex absolute file name diff --git a/etc/NEWS b/etc/NEWS index 807f31fa33e..3cad0995ac5 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -612,6 +612,10 @@ Use 'exif-parse-file' and 'exif-field' instead. * Lisp Changes in Emacs 29.1 ++++ +*** New function 'file-name-split'. +This returns a list of all the components of a file name. + +++ *** New macro 'with-undo-amalgamate' It records a particular sequence of operations as a single undo step diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index c3d6c742940..a9f548b104e 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -281,6 +281,9 @@ There can be any number of :example/:result elements." :eval (file-name-base "/tmp/foo.txt")) (file-relative-name :eval (file-relative-name "/tmp/foo" "/tmp")) + (file-name-split + :eval (file-name-split "/tmp/foo") + :eval (file-name-split "foo/bar")) (make-temp-name :eval (make-temp-name "/tmp/foo-")) (file-name-concat diff --git a/lisp/files.el b/lisp/files.el index 0bc7a92fbea..c694df38268 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -5051,6 +5051,29 @@ See also `file-name-sans-extension'." (file-name-sans-extension (file-name-nondirectory (or filename (buffer-file-name))))) +(defun file-name-split (filename) + "Return a list of all the components of FILENAME. +On most systems, this will be true: + + (equal (string-join (file-name-split filename) \"/\") filename)" + (let ((components nil)) + ;; If this is a directory file name, then we have a null file name + ;; at the end. + (when (directory-name-p filename) + (push "" components) + (setq filename (directory-file-name filename))) + ;; Loop, chopping off components. + (while (length> filename 0) + (push (file-name-nondirectory filename) components) + (let ((dir (file-name-directory filename))) + (setq filename (and dir (directory-file-name dir))) + ;; If there's nothing left to peel off, we're at the root and + ;; we can stop. + (when (equal dir filename) + (push "" components) + (setq filename nil)))) + components)) + (defcustom make-backup-file-name-function #'make-backup-file-name--default-function "A function that `make-backup-file-name' uses to create backup file names. diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index c6d7c19279b..1e20317739a 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el @@ -1800,6 +1800,12 @@ Prompt users for any modified buffer with `buffer-offer-save' non-nil." ;; `save-some-buffers-default-predicate' (i.e. the 2nd element) is ignored. (nil save-some-buffers-root ,nb-might-save)))))) +(defun test-file-name-split () + (should (equal (file-name-split "foo/bar") '("foo" "bar"))) + (should (equal (file-name-split "/foo/bar") '("" "foo" "bar"))) + (should (equal (file-name-split "/foo/bar/zot") '("" "foo" "bar" "zot"))) + (should (equal (file-name-split "/foo/bar/") '("" "foo" "bar" ""))) + (should (equal (file-name-split "foo/bar/") '("foo" "bar" "")))) (provide 'files-tests) ;;; files-tests.el ends here -- cgit v1.2.3 From 5a58b2f58c2b8411d593f6ea5a157c20a1176aaf Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Wed, 10 Nov 2021 08:56:03 +0100 Subject: ; * doc/lispref/files.texi: Fix typo. --- doc/lispref/files.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'doc/lispref/files.texi') diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index dd058b12158..d93770a0d2f 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -2246,7 +2246,7 @@ and @code{file-name-nondirectory}. For example, @defun file-name-split filename This function splits a file name into its components, and can be -thought of as the inverse of @code{string-joing} with the appropriate +thought of as the inverse of @code{string-join} with the appropriate directory separator. For example, @example -- cgit v1.2.3