]> git.eshelyaron.com Git - emacs.git/commitdiff
Extend directory-append to take an arbitrary number of components
authorLars Ingebrigtsen <larsi@gnus.org>
Sat, 24 Jul 2021 15:22:43 +0000 (17:22 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Sat, 24 Jul 2021 15:22:43 +0000 (17:22 +0200)
* 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
lisp/emacs-lisp/shortdoc.el
src/fileio.c
test/src/fileio-tests.el

index ec8a2525ed9d89e3e3ccbfce2ef0c10e879adf73..a624c2eb937cc3e78b0026a42fa2847e73cb09ac 100644 (file)
@@ -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
index 7506d756d198512e7881ef3223396f6548c72531..c507ad7c812ed41511ad3ab23c61445426a4f169 100644 (file)
@@ -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/")
index 277da48315e12d204ebe3f32ff962396fec44064..a4f08383776a4c023c1fbdd5967a6df6fd3930b9 100644 (file)
@@ -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;
 }
 
index 80afeae41ba71f9ea3b853ea86f6566c4e3c5038..702659fa3951b08a8cbc9dd93ab18c2a103ca51d 100644 (file)
@@ -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