emacs-diffs
[Top][All Lists]
Advanced

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

master b4543df: Extend directory-append to take an arbitrary number of c


From: Lars Ingebrigtsen
Subject: master b4543df: Extend directory-append to take an arbitrary number of components
Date: Sat, 24 Jul 2021 11:22:53 -0400 (EDT)

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

    Extend directory-append to take an arbitrary number of components
    
    * doc/lispref/files.texi (Directory Names): Document it.
    * lisp/emacs-lisp/shortdoc.el (file-name): Add new example.
    
    * src/fileio.c (Fdirectory_append): Change the function to take an
    arbitrary number of components.
---
 doc/lispref/files.texi      |  13 ++---
 lisp/emacs-lisp/shortdoc.el |   1 +
 src/fileio.c                | 116 ++++++++++++++++++++++++++++++--------------
 test/src/fileio-tests.el    |   8 ++-
 4 files changed, 94 insertions(+), 44 deletions(-)

diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index ec8a252..a624c2e 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -2343,9 +2343,10 @@ entirely of directory separators.
 @end example
 @end defun
 
-@defun directory-append directory filename
-Combine @var{filename} with @var{directory} by optionally putting a
-slash in the middle.
+@defun directory-append directory &rest components
+Concatenate @var{components} to @var{directory}, inserting a slash
+before the components if @var{directory} or the preceding component
+didn't end with a slash.
 
 @example
 @group
@@ -2354,9 +2355,9 @@ slash in the middle.
 @end group
 @end example
 
-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.
+This is almost the same as using @code{concat}, but @var{dirname} (and
+the non-final components) may or may not end with slash characters,
+and this function will not double those characters.
 @end defun
 
   To convert a directory name to its abbreviation, use this
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 7506d75..c507ad7 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -276,6 +276,7 @@ There can be any number of :example/:result elements."
   (directory-append
    :eval (directory-append "/tmp/" "foo")
    :eval (directory-append "/tmp" "foo")
+   :eval (directory-append "/tmp" "foo" "bar/" "zot")
    :eval (directory-append "/tmp" "~"))
   (expand-file-name
    :eval (expand-file-name "foo" "/tmp/")
diff --git a/src/fileio.c b/src/fileio.c
index 277da48..a4f0838 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -749,48 +749,90 @@ 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)
+DEFUN ("directory-append", Fdirectory_append, Sdirectory_append, 1, MANY, 0,
+       doc: /* Append COMPONENTS to DIRECTORY and return the resulting string.
+COMPONENTS must be a list of strings.  DIRECTORY or the non-final
+elements in COMPONENTS may or may not end with a slash -- if they don't
+end with a slash, a slash will be inserted before contatenating.
+usage: (record DIRECTORY &rest COMPONENTS) */)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
-  USE_SAFE_ALLOCA;
-  char *p;
-
-  CHECK_STRING (file);
-  CHECK_STRING (directory);
+  ptrdiff_t chars = 0, bytes = 0, multibytes = 0;
+  Lisp_Object *elements = args;
+  Lisp_Object result;
+  ptrdiff_t i;
+
+  /* First go through the list to check the types and see whether
+     they're all of the same multibytedness. */
+  for (i = 0; i < nargs; i++)
+    {
+      Lisp_Object arg = args[i];
+      CHECK_STRING (arg);
+      if (SCHARS (arg) == 0)
+       xsignal1 (Qfile_error, build_string ("Empty file name"));
+      /* Multibyte and non-ASCII. */
+      if (STRING_MULTIBYTE (arg) && SCHARS (arg) != SBYTES (arg))
+       multibytes++;
+      /* We're not adding a slash to the final part. */
+      if (i == nargs - 1
+         || IS_DIRECTORY_SEP (*(SSDATA (arg) + SBYTES (arg) - 1)))
+       {
+         bytes += SBYTES (arg);
+         chars += SCHARS (arg);
+       }
+      else
+       {
+         bytes += SBYTES (arg) + 1;
+         chars += SCHARS (arg) + 1;
+       }
+    }
 
-  if (SCHARS (file) == 0)
-    xsignal1 (Qfile_error, build_string ("Empty file name"));
+  /* Convert if needed. */
+  if (multibytes != 0 && multibytes != nargs)
+    {
+      elements = xmalloc (nargs * sizeof *elements);
+      bytes = 0;
+      for (i = 0; i < nargs; i++)
+       {
+         Lisp_Object arg = args[i];
+         if (STRING_MULTIBYTE (arg))
+           elements[i] = arg;
+         else
+           elements[i] = make_multibyte_string (SSDATA (arg), SCHARS (arg),
+                                                SCHARS (arg));
+         arg = elements[i];
+         /* We have to recompute the number of bytes. */
+         if (i == nargs - 1
+             || IS_DIRECTORY_SEP (*(SSDATA (arg) + SBYTES (arg) - 1)))
+           bytes += SBYTES (arg);
+         else
+           bytes += SBYTES (arg) + 1;
+       }
+    }
 
-  if (SCHARS (directory) == 0)
-    return file;
+  /* Allocate an empty string. */
+  if (multibytes == 0)
+    result = make_uninit_string (chars);
+  else
+    result = make_uninit_multibyte_string (chars, bytes);
+  /* Null-terminate the string. */
+  *(SSDATA (result) + SBYTES (result)) = 0;
 
-  /* Make the strings the same multibytedness. */
-  if (STRING_MULTIBYTE (file) != STRING_MULTIBYTE (directory))
+  /* Copy over the data. */
+  char *p = SSDATA (result);
+  for (i = 0; i < nargs; i++)
     {
-      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 ();
+      Lisp_Object arg = elements[i];
+      memcpy (p, SSDATA (arg), SBYTES (arg));
+      p += SBYTES (arg);
+      /* The last element shouldn't have a slash added at the end. */
+      if (i < nargs -1 && !IS_DIRECTORY_SEP (*(p - 1)))
+       *p++ = DIRECTORY_SEP;
+    }
+
+  if (multibytes != 0 && multibytes != nargs)
+    xfree (elements);
+
   return result;
 }
 
diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el
index 80afeae..702659f 100644
--- a/test/src/fileio-tests.el
+++ b/test/src/fileio-tests.el
@@ -162,10 +162,16 @@ Also check that an encoding error can appear in a 
symlink."
 
 (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" "zot") "foo/bar/zot"))
   (should (equal (directory-append "foo/" "bar") "foo/bar"))
   (should (equal (directory-append "foo//" "bar") "foo//bar"))
+  (should (equal (directory-append "foo/" "bar/" "zot") "foo/bar/zot"))
+  (should (equal (directory-append "fóo" "bar") "fóo/bar"))
+  (should (equal (directory-append "foo" "bár") "foo/bár"))
+  (should (equal (directory-append "fóo" "bár") "fóo/bár"))
   (should-error (directory-append "foo" ""))
-  (should (equal (directory-append "" "bar") "bar"))
+  (should-error (directory-append "" "bar"))
   (should-error (directory-append "" "")))
 
 ;;; fileio-tests.el ends here



reply via email to

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