Hi, Todd and Theo! I wrote this function this week, based on strlcpy(3) and strecopy()[1], which are the best string copy functions I've seen so far. But I came to improvements that bring one function that I think is better than both of those.
I posted the function for review on codidact.com[2]. I also discussed it a bit on the groff@ mailing list[3]. See this manual page I wrote for it. Since you designed strlcpy(3BSD) and strlcat(3BSD), I'm interested in knowing your opinion on it. If you like it, feel free to add it to OpenBSD. ;-) Cheers, Alex [1]: <https://www.symas.com/post/the-sad-state-of-c-strings> [2]: <https://software.codidact.com/posts/285946/285952#answer-285952> [3]: <https://lists.gnu.org/archive/html/groff/2022-02/msg00050.html> --- stpecpy(3) Linux Programmer’s Manual stpecpy(3) NAME stpecpy - bounded string copy and concatenation SYNOPSIS #include <string.h> char *_Nonnull stpecpy(char *_Nonnull dest, const char *_Nonnull restrict src, char *_Nonnull end); DESCRIPTION stpecpy() copies the string pointed to by src (including the terminating null byte ('\0')) to the array pointed to by dest. The strings may not overlap. This function doesn’t overflow the destination buffer, and requires a pointer to the end of it for that purpose. end is a pointer to the last element in the dest buffer. It should be calculated in the following way: char dest[SIZE]; char *end; end = &dest[SIZE - 1]; stpecpy(dest, "Hello world", end); This function only operates on true "C" strings, which means that src must be terminated by a null byte, and dest is guaranteed to be terminated with a null byte af‐ ter the call (as long as the buffer has a size larger than 0). This function is designed to be a safer, more consistent, less error prone, and simpler replacement for other string copy and concatenation functions. Compared to other string copy and concatenation functions strlcat(3BSD) *cat() functions have an inherent performance problem: each *cat() call has to find again the teminating null byte in the dest string. Read about Shlemiel the painter’s algorithm ⟨https://www.joelonsoftware.com/2001/12/11/back- to-basics/⟩. strlcpy(3BSD) strscpy(9) memccpy(3) One can use strlcpy(3BSD), strscpy(9), or memccpy(3) to concatenate, without strlcat(3BSD), but it’s error prone. They require the user to recalculate the remaining buffer size for every call. stpcpy(3) It can’t detect overflow. However, in case the programmer knows overflow can’t occur, then it’s simpler to use it. stpncpy(3) strncpy(3) strncat(3) They don’t work with "C" strings. The resulting string is not guaranteed to be terminated with a null byte. They also write unnecessary zeros af‐ ter the terminating byte. strcpy(3) It doesn’t detect overflow, as stpcpy(3), plus it can’t be chained; requires strcat(3). strcat(3) It can’t detect overflow, as strcpy(3), plus it has performance problems, as strlcat(3BSD). snprintf(3) It has considerable overhead, which can be orders of magnitude slower. RETURN VALUE stpecpy() returns a pointer to the terminating null byte in dest, except if the string has been truncated, in which case it returns end + 1. EXAMPLES Trivial (not optimized) implementation: char *_Nonnull stpecpy(char *_Nonnull dst, char *_Nonnull restrict src, char *_Nonnull end) { for (/* void */; dst <= end; dst++, src++) { *dst = *src; if (*dst == '\0') return dst; } /* truncation detected */ *end = '\0'; return dst; } Example of usage The following program produces this output: $ ./a.out Trunc: 9: Hello wor 9: Hello foo Trunc: 9: Hello baa 2: HW Source code: int main(void) { ptrdiff_t size = 10; char buf[size]; char *end; ptrdiff_t len; end = &buf[size - 1]; len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf; if (len == size) { len--; printf("Trunc: "); } printf("%ti: %s\n", len, buf); len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf; if (len == size) { len--; printf("Trunc: "); } printf("%ti: %s\n", len, buf); len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf; if (len == size) { len--; printf("Trunc: "); } printf("%ti: %s\n", len, buf); len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf; if (len == size) { len--; printf("Trunc: "); } printf("%ti: %s\n", len, buf); } SEE ALSO snprintf(3), strncat(3), strncpy(3), stpsecpy(3) alx 2022-02-13 stpecpy(3) -- Alejandro Colomar Linux man-pages comaintainer; https://www.kernel.org/doc/man-pages/ http://www.alejandro-colomar.es/
