STRLCPY(3) BSD Programmer's Manual STRLCPY(3)
strlcpy, strlcat - size-bounded string copying and concatenation
#include <string.h> size_t strlcpy(char *dst, const char *src, size_t dstsize); size_t strlcat(char *dst, const char *src, size_t dstsize);
The strlcpy() and strlcat() functions copy and concatenate strings with the same input parameters and output results as snprintf(3). They are designed to be safer, more consistent and less error-prone replacements for the easily misused functions strncpy(3) and strncat(3). strlcpy() and strlcat() take the full size of the destination buffer and guarantee NUL termination if there is room. Note that room for the NUL should be included in dstsize. strlcpy() copies up to dstsize - 1 characters from the string src to dst, NUL-terminating the result if dstsize is not 0. strlcat() appends the string src to the end of dst. It will append at most dstsize - strlen(dst) - 1 characters. It will then NUL-terminate, unless dstsize is 0 or the original dst string was longer than dstsize (in practice, this should not happen, as it means that either dstsize is incorrect or dst was not a proper string). If the src and dst strings overlap, the behaviour is undefined.
Besides quibbles over the return type (size_t versus int) and signal handler safety (snprintf(3) is not entirely safe on some systems), the following two are equivalent: n = strlcpy(dst, src, len); n = snprintf(dst, len, "%s", src); Similarily, these are equivalent: n = strlcat(dst, src, len); n = snprintf(dst, len, "%s%s", dst, src); Like snprintf(3), the strlcpy() and strlcat() functions return the total length of the string they tried to create, not including the trailing NUL. For strlcpy() that means the length of src. For strlcat() that means the initial length of dst plus the length of src. If the return value is >= dstsize, the output string has been truncated. It is the caller's responsibility to handle this.
The following code fragment illustrates the simple case: char *s, *p, buf[BUFSIZ]; ... (void)strlcpy(buf, s, sizeof(buf)); (void)strlcat(buf, p, sizeof(buf)); To detect truncation, perhaps while building a pathname, something like the following might be used: char *dir, *file, fn[PATH_MAX]; ... if (strlcpy(fn, dir, sizeof(fn)) >= sizeof(fn)) goto toolong; if (strlcat(fn, file, sizeof(fn)) >= sizeof(fn)) goto toolong; Since it is known how many characters were copied the first time, things can be sped up a bit by using a copy instead of an append: char *dir, *file, fn[PATH_MAX]; size_t n; ... if ((n = strlcpy(fn, dir, sizeof(fn))) >= sizeof(fn)) goto toolong; if (strlcpy(fn + n, file, sizeof(fn) - n) >= sizeof(fn) - n) goto toolong; However, one may question the validity of such optimizations, as their complexity defeats the whole purpose of strlcpy() and strlcat() - as a matter of fact, the first version within this manual page got it wrong.
snprintf(3), strncat(3), strncpy(3), wcslcpy(3)
strlcpy() and strlcat() first appeared in OpenBSD 2.4. Overlapping strings were made into undefined behaviour in 2013; it was legitimate before. Mac OSX or standalone fortify-headers (usually with musl libc) terminate programs passing too large dstsize or overlapping dst and src arguments with SIGILL; unfortunately, neither of these has the decency to warn about this at compile time.
Todd C. Miller <millert@openbsd.org> created strlcpy() and strlcat(). MirBSD #10-current March 17, 2021 1