Function: file-name-concat
file-name-concat is a function defined in fileio.c.
Signature
(file-name-concat DIRECTORY &rest COMPONENTS)
Documentation
Append COMPONENTS to DIRECTORY and return the resulting string.
Each element 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 concatenating.
In most cases, one or more calls to expand-file-name are better
suited for the job than this function. Use this function only if
some of the special expansions done by expand-file-name get in
the way of what your program needs to do.
Other relevant functions are documented in the file-name group.
Probably introduced at or before Emacs version 28.1.
Shortdoc
;; file-name
(file-name-concat "/tmp/" "foo")
=> "/tmp/foo"
(file-name-concat "/tmp" "foo")
=> "/tmp/foo"
(file-name-concat "/tmp" "foo" "bar/" "zot")
=> "/tmp/foo/bar/zot"
(file-name-concat "/tmp" "~")
=> "/tmp/~"
Aliases
Source Code
// Defined in /usr/src/emacs/src/fileio.c
{
ptrdiff_t chars = 0, bytes = 0, multibytes = 0, eargs = 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 multibyteness. */
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)
continue;
eargs++;
/* 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;
}
}
/* Convert if needed. */
if ((multibytes != 0 && multibytes != nargs)
|| eargs != nargs)
{
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] = Fstring_to_multibyte (arg);
arg = elements[i];
/* We have to recompute the number of bytes. */
if (i == eargs - 1
|| IS_DIRECTORY_SEP (*(SSDATA (arg) + SBYTES (arg) - 1)))
{
bytes += SBYTES (arg);
chars += SCHARS (arg);
}
else
{
bytes += SBYTES (arg) + 1;
chars += SCHARS (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 < 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 < eargs - 1 && !IS_DIRECTORY_SEP (*(p - 1)))
*p++ = DIRECTORY_SEP;
}
if (elements != args)
xfree (elements);
return result;
}