@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
@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
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;
}
(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