Allow empty elements in directory-append

* doc/lispref/files.texi (Directory Names): Document it.
* src/fileio.c (Fdirectory_append): Allow empty elements.
This commit is contained in:
Lars Ingebrigtsen 2021-07-25 08:00:50 +02:00
parent 03f844249c
commit aa9cba6587
3 changed files with 42 additions and 17 deletions

View file

@ -2355,7 +2355,9 @@ didn't end with a slash.
@end group
@end example
A zero-length directory or component is not allowed.
A @var{directory} or components that are @code{nil} or the empty
string are ignored---they are filtered out first and do not affect the
results in any way.
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,

View file

@ -751,14 +751,14 @@ For that reason, you should normally use `make-temp-file' instead. */)
DEFUN ("directory-append", Fdirectory_append, Sdirectory_append, 1, MANY, 0,
doc: /* Append COMPONENTS to DIRECTORY and return the resulting string.
COMPONENTS must be strings.
Elements in COMPONENTS must be a string or nil.
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)
{
ptrdiff_t chars = 0, bytes = 0, multibytes = 0;
ptrdiff_t chars = 0, bytes = 0, multibytes = 0, eargs = 0;
Lisp_Object *elements = args;
Lisp_Object result;
ptrdiff_t i;
@ -768,9 +768,13 @@ usage: (record DIRECTORY &rest COMPONENTS) */)
for (i = 0; i < nargs; i++)
{
Lisp_Object arg = args[i];
/* Skip empty and nil elements. */
if (NILP (arg))
continue;
CHECK_STRING (arg);
if (SCHARS (arg) == 0)
xsignal1 (Qfile_error, build_string ("Empty file name"));
continue;
eargs++;
/* Multibyte and non-ASCII. */
if (STRING_MULTIBYTE (arg) && SCHARS (arg) != SBYTES (arg))
multibytes++;
@ -789,25 +793,41 @@ usage: (record DIRECTORY &rest COMPONENTS) */)
}
/* Convert if needed. */
if (multibytes != 0 && multibytes != nargs)
if ((multibytes != 0 && multibytes != nargs)
|| eargs != nargs)
{
elements = xmalloc (nargs * sizeof *elements);
int j = 0;
elements = xmalloc (eargs * sizeof *elements);
bytes = 0;
chars = 0;
/* Filter out nil/"". */
for (i = 0; i < nargs; i++)
{
Lisp_Object arg = args[i];
if (!NILP (arg) && SCHARS (arg) != 0)
elements[j++] = arg;
}
for (i = 0; i < eargs; i++)
{
Lisp_Object arg = elements[i];
/* Use multibyte or all-ASCII strings as is. */
if (STRING_MULTIBYTE (arg) || string_ascii_p (arg))
elements[i] = arg;
else
if (!STRING_MULTIBYTE (arg) && !string_ascii_p (arg))
elements[i] = Fstring_to_multibyte (arg);
arg = elements[i];
/* We have to recompute the number of bytes. */
if (i == nargs - 1
if (i == eargs - 1
|| IS_DIRECTORY_SEP (*(SSDATA (arg) + SBYTES (arg) - 1)))
bytes += SBYTES (arg);
{
bytes += SBYTES (arg);
chars += SCHARS (arg);
}
else
bytes += SBYTES (arg) + 1;
{
bytes += SBYTES (arg) + 1;
chars += SCHARS (arg) + 1;
}
}
}
@ -821,13 +841,13 @@ usage: (record DIRECTORY &rest COMPONENTS) */)
/* Copy over the data. */
char *p = SSDATA (result);
for (i = 0; i < nargs; i++)
for (i = 0; i < eargs; i++)
{
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)))
if (i < eargs - 1 && !IS_DIRECTORY_SEP (*(p - 1)))
*p++ = DIRECTORY_SEP;
}

View file

@ -175,8 +175,11 @@ Also check that an encoding error can appear in a symlink."
(aset string 2 255)
(should (not (multibyte-string-p string)))
(should (equal (directory-append "fóo" string) "fóo/aa\377aa")))
(should-error (directory-append "foo" ""))
(should-error (directory-append "" "bar"))
(should-error (directory-append "" "")))
(should (equal (directory-append "foo") "foo"))
(should (equal (directory-append "foo/") "foo/"))
(should (equal (directory-append "foo" "") "foo"))
(should (equal (directory-append "foo" "" "" "" nil) "foo"))
(should (equal (directory-append "" "bar") "bar"))
(should (equal (directory-append "" "") "")))
;;; fileio-tests.el ends here