emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 5431a58 1/2: Add new function `directory-append'


From: Lars Ingebrigtsen
Subject: master 5431a58 1/2: Add new function `directory-append'
Date: Sat, 24 Jul 2021 07:37:18 -0400 (EDT)

branch: master
commit 5431a58e86d3f2579c1edf1dc8d7074de73ac694
Author: Lars Ingebrigtsen <larsi@gnus.org>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Add new function `directory-append'
    
    * doc/lispref/files.texi (Directory Names): Document it, and
    remove the concat-based file concatenation description.
    * lisp/emacs-lisp/shortdoc.el (file-name): Add.  And add more
    expand-file-name examples.
    
    * src/fileio.c (Fdirectory_append): New function.
---
 doc/lispref/files.texi      | 50 ++++++++++-----------------------------------
 etc/NEWS                    |  4 ++++
 lisp/emacs-lisp/shortdoc.el |  9 +++++++-
 src/fileio.c                | 46 +++++++++++++++++++++++++++++++++++++++++
 test/src/fileio-tests.el    |  8 ++++++++
 5 files changed, 77 insertions(+), 40 deletions(-)

diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index c7e5537..ac49c5a 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -2343,49 +2343,21 @@ entirely of directory separators.
 @end example
 @end defun
 
-  Given a directory name, you can combine it with a relative file name
-using @code{concat}:
+@defun directory-append filename directory
+Combine @var{filename} with @var{directory} by optionally putting a
+slash in the middle.
 
 @example
-(concat @var{dirname} @var{relfile})
-@end example
-
-@noindent
-Be sure to verify that the file name is relative before doing that.
-If you use an absolute file name, the results could be syntactically
-invalid or refer to the wrong file.
-
-  If you want to use a directory file name in making such a
-combination, you must first convert it to a directory name using
-@code{file-name-as-directory}:
-
-@example
-(concat (file-name-as-directory @var{dirfile}) @var{relfile})
-@end example
-
-@noindent
-Don't try concatenating a slash by hand, as in
-
-@example
-;;; @r{Wrong!}
-(concat @var{dirfile} "/" @var{relfile})
-@end example
-
-@noindent
-because this is not portable.  Always use
-@code{file-name-as-directory}.
-
-  To avoid the issues mentioned above, or if the @var{dirname} value
-might be @code{nil} (for example, from an element of @code{load-path}),
-use:
-
-@example
-(expand-file-name @var{relfile} @var{dirname})
+@group
+(directory-append "/tmp" "foo")
+     @result{} "/tmp/foo"
+@end group
 @end example
 
-However, @code{expand-file-name} expands leading @samp{~} in
-@var{relfile}, which may not be what you want.  @xref{File Name
-Expansion}.
+This is almost the same as using @code{concat}, but @var{dirname} may
+or may not end with a slash character, and this function will not
+double that character.
+@end defun
 
   To convert a directory name to its abbreviation, use this
 function:
diff --git a/etc/NEWS b/etc/NEWS
index e4b0809..a10c580 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3121,6 +3121,10 @@ The former is now declared obsolete.
 * Lisp Changes in Emacs 28.1
 
 +++
+*** New function 'directory-append'.
+This appends a file name to a directory name and returns the result.
+
++++
 *** New function 'split-string-shell-command'.
 This splits a shell command string into separate components,
 respecting quoting with single ('like this') and double ("like this")
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 22439f4..7506d75 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -273,8 +273,15 @@ There can be any number of :example/:result elements."
    :eval (file-relative-name "/tmp/foo" "/tmp"))
   (make-temp-name
    :eval (make-temp-name "/tmp/foo-"))
+  (directory-append
+   :eval (directory-append "/tmp/" "foo")
+   :eval (directory-append "/tmp" "foo")
+   :eval (directory-append "/tmp" "~"))
   (expand-file-name
-   :eval (expand-file-name "foo" "/tmp/"))
+   :eval (expand-file-name "foo" "/tmp/")
+   :eval (expand-file-name "foo" "/tmp///")
+   :eval (expand-file-name "foo" "/tmp/foo/.././")
+   :eval (expand-file-name "~" "/tmp/"))
   (substitute-in-file-name
    :eval (substitute-in-file-name "$HOME/foo"))
   "Directory Functions"
diff --git a/src/fileio.c b/src/fileio.c
index 04c9d7d..277da48 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -749,6 +749,51 @@ For that reason, you should normally use `make-temp-file' 
instead.  */)
                                   empty_unibyte_string, Qnil);
 }
 
+DEFUN ("directory-append", Fdirectory_append, Sdirectory_append, 2, 2, 0,
+       doc: /* Return FILE (a string) appended to DIRECTORY (a string).
+DIRECTORY may or may not end with a slash -- the return value from
+this function will be the same.  */)
+  (Lisp_Object directory, Lisp_Object file)
+{
+  USE_SAFE_ALLOCA;
+  char *p;
+
+  CHECK_STRING (file);
+  CHECK_STRING (directory);
+
+  if (SCHARS (file) == 0)
+    xsignal1 (Qfile_error, build_string ("Empty file name"));
+
+  if (SCHARS (directory) == 0)
+    return file;
+
+  /* Make the strings the same multibytedness. */
+  if (STRING_MULTIBYTE (file) != STRING_MULTIBYTE (directory))
+    {
+      if (STRING_MULTIBYTE (file))
+       directory = make_multibyte_string (SSDATA (directory),
+                                          SCHARS (directory),
+                                          SCHARS (directory));
+      else
+       file = make_multibyte_string (SSDATA (file),
+                                     SCHARS (file),
+                                     SCHARS (file));
+    }
+
+  /* Allocate enough extra space in case we need to put a slash in
+     there. */
+  p = SAFE_ALLOCA (SBYTES (file) + SBYTES (directory) + 2);
+  ptrdiff_t offset = SBYTES (directory);
+  memcpy (p, SSDATA (directory), offset);
+  if (! IS_DIRECTORY_SEP (p[offset - 1]))
+    p[offset++] = DIRECTORY_SEP;
+  memcpy (p + offset, SSDATA (file), SBYTES (file));
+  p[offset + SBYTES (file)] = 0;
+  Lisp_Object result = build_string (p);
+  SAFE_FREE ();
+  return result;
+}
+
 /* NAME must be a string.  */
 static bool
 file_name_absolute_no_tilde_p (Lisp_Object name)
@@ -6488,6 +6533,7 @@ This includes interactive calls to `delete-file' and
   defsubr (&Sdirectory_file_name);
   defsubr (&Smake_temp_file_internal);
   defsubr (&Smake_temp_name);
+  defsubr (&Sdirectory_append);
   defsubr (&Sexpand_file_name);
   defsubr (&Ssubstitute_in_file_name);
   defsubr (&Scopy_file);
diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el
index b989c97..80afeae 100644
--- a/test/src/fileio-tests.el
+++ b/test/src/fileio-tests.el
@@ -160,4 +160,12 @@ Also check that an encoding error can appear in a symlink."
   (should-error (file-exists-p "/foo\0bar")
                 :type 'wrong-type-argument))
 
+(ert-deftest fileio-tests/directory-append ()
+  (should (equal (directory-append "foo" "bar") "foo/bar"))
+  (should (equal (directory-append "foo/" "bar") "foo/bar"))
+  (should (equal (directory-append "foo//" "bar") "foo//bar"))
+  (should-error (directory-append "foo" ""))
+  (should (equal (directory-append "" "bar") "bar"))
+  (should-error (directory-append "" "")))
+
 ;;; fileio-tests.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]