From 2e6ed253ce485698df649904bd9e5254a3f4bf94 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Wed, 10 Nov 2021 00:26:32 +0100 Subject: [PATCH] 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(+) 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 -- 2.39.5