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.
This commit is contained in:
parent
8cd66a3170
commit
b4543dfa9e
4 changed files with 93 additions and 43 deletions
|
@ -2343,9 +2343,10 @@ entirely of directory separators.
|
||||||
@end example
|
@end example
|
||||||
@end defun
|
@end defun
|
||||||
|
|
||||||
@defun directory-append directory filename
|
@defun directory-append directory &rest components
|
||||||
Combine @var{filename} with @var{directory} by optionally putting a
|
Concatenate @var{components} to @var{directory}, inserting a slash
|
||||||
slash in the middle.
|
before the components if @var{directory} or the preceding component
|
||||||
|
didn't end with a slash.
|
||||||
|
|
||||||
@example
|
@example
|
||||||
@group
|
@group
|
||||||
|
@ -2354,9 +2355,9 @@ slash in the middle.
|
||||||
@end group
|
@end group
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
This is almost the same as using @code{concat}, but @var{dirname} may
|
This is almost the same as using @code{concat}, but @var{dirname} (and
|
||||||
or may not end with a slash character, and this function will not
|
the non-final components) may or may not end with slash characters,
|
||||||
double that character.
|
and this function will not double those characters.
|
||||||
@end defun
|
@end defun
|
||||||
|
|
||||||
To convert a directory name to its abbreviation, use this
|
To convert a directory name to its abbreviation, use this
|
||||||
|
|
|
@ -276,6 +276,7 @@ There can be any number of :example/:result elements."
|
||||||
(directory-append
|
(directory-append
|
||||||
:eval (directory-append "/tmp/" "foo")
|
:eval (directory-append "/tmp/" "foo")
|
||||||
:eval (directory-append "/tmp" "foo")
|
:eval (directory-append "/tmp" "foo")
|
||||||
|
:eval (directory-append "/tmp" "foo" "bar/" "zot")
|
||||||
:eval (directory-append "/tmp" "~"))
|
:eval (directory-append "/tmp" "~"))
|
||||||
(expand-file-name
|
(expand-file-name
|
||||||
:eval (expand-file-name "foo" "/tmp/")
|
:eval (expand-file-name "foo" "/tmp/")
|
||||||
|
|
114
src/fileio.c
114
src/fileio.c
|
@ -749,48 +749,90 @@ For that reason, you should normally use `make-temp-file' instead. */)
|
||||||
empty_unibyte_string, Qnil);
|
empty_unibyte_string, Qnil);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN ("directory-append", Fdirectory_append, Sdirectory_append, 2, 2, 0,
|
DEFUN ("directory-append", Fdirectory_append, Sdirectory_append, 1, MANY, 0,
|
||||||
doc: /* Return FILE (a string) appended to DIRECTORY (a string).
|
doc: /* Append COMPONENTS to DIRECTORY and return the resulting string.
|
||||||
DIRECTORY may or may not end with a slash -- the return value from
|
COMPONENTS must be a list of strings. DIRECTORY or the non-final
|
||||||
this function will be the same. */)
|
elements in COMPONENTS may or may not end with a slash -- if they don't
|
||||||
(Lisp_Object directory, Lisp_Object file)
|
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;
|
ptrdiff_t chars = 0, bytes = 0, multibytes = 0;
|
||||||
char *p;
|
Lisp_Object *elements = args;
|
||||||
|
Lisp_Object result;
|
||||||
|
ptrdiff_t i;
|
||||||
|
|
||||||
CHECK_STRING (file);
|
/* First go through the list to check the types and see whether
|
||||||
CHECK_STRING (directory);
|
they're all of the same multibytedness. */
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
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))
|
Lisp_Object arg = args[i];
|
||||||
directory = make_multibyte_string (SSDATA (directory),
|
CHECK_STRING (arg);
|
||||||
SCHARS (directory),
|
if (SCHARS (arg) == 0)
|
||||||
SCHARS (directory));
|
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
|
else
|
||||||
file = make_multibyte_string (SSDATA (file),
|
{
|
||||||
SCHARS (file),
|
bytes += SBYTES (arg) + 1;
|
||||||
SCHARS (file));
|
chars += SCHARS (arg) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate enough extra space in case we need to put a slash in
|
/* Convert if needed. */
|
||||||
there. */
|
if (multibytes != 0 && multibytes != nargs)
|
||||||
p = SAFE_ALLOCA (SBYTES (file) + SBYTES (directory) + 2);
|
{
|
||||||
ptrdiff_t offset = SBYTES (directory);
|
elements = xmalloc (nargs * sizeof *elements);
|
||||||
memcpy (p, SSDATA (directory), offset);
|
bytes = 0;
|
||||||
if (! IS_DIRECTORY_SEP (p[offset - 1]))
|
for (i = 0; i < nargs; i++)
|
||||||
p[offset++] = DIRECTORY_SEP;
|
{
|
||||||
memcpy (p + offset, SSDATA (file), SBYTES (file));
|
Lisp_Object arg = args[i];
|
||||||
p[offset + SBYTES (file)] = 0;
|
if (STRING_MULTIBYTE (arg))
|
||||||
Lisp_Object result = build_string (p);
|
elements[i] = arg;
|
||||||
SAFE_FREE ();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* Copy over the data. */
|
||||||
|
char *p = SSDATA (result);
|
||||||
|
for (i = 0; i < nargs; 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)))
|
||||||
|
*p++ = DIRECTORY_SEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multibytes != 0 && multibytes != nargs)
|
||||||
|
xfree (elements);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,10 +162,16 @@ Also check that an encoding error can appear in a symlink."
|
||||||
|
|
||||||
(ert-deftest fileio-tests/directory-append ()
|
(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 (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") "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-error (directory-append "foo" ""))
|
||||||
(should (equal (directory-append "" "bar") "bar"))
|
(should-error (directory-append "" "bar"))
|
||||||
(should-error (directory-append "" "")))
|
(should-error (directory-append "" "")))
|
||||||
|
|
||||||
;;; fileio-tests.el ends here
|
;;; fileio-tests.el ends here
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue