Hi David, > Date: 2026-11-05 20:53:21+0000 > From: David Laight > > On Thu, 5 Nov 2026 12:49:59 +0100 > Alejandro Colomar wrote: > > > > Personally I think that while there are issues with strlcat() > > > (mostly to do with its return value), and that there are often > > > much better (and faster - so sprintf is out) ways of getting > > > the same result, the deprecation really needs to propose a > > > sensible alternative (not inlined strlen() + strscpy()). > > > > One alternative would be to add strscat(), with the obvious internal > > strlen(3). This would be a drop-in replacement, and wouldn't have the > > issues of strlcat(3) --mainly, the DoS vulnerability which is > > consequence of the return value, and also the more difficult error > > handling of doing '>=sizeof(...)' than '<0'--. > > I've had thoughts of a more general function - but I've not tried to > write it. > > int str_append(char *dest, size_t dest_sz, size_t dest_off, const char *src, size_t src_max_len); > > Where dest_sz is usually sizeof(*dest) and the header can substitute that for > a constant zero. > dest_off would similarly default to strlen(dest), if -ve the function would > return that value immediately. > src_max_len would be used for an effective strnlen(src, src_max_len). > > The idea would be that calls like: > char tgt[100]; > len = str_append(tgt, 0, strscpy(tgt, src_1), src_2, 0); > would append src_1 and src_2 in tgt returning -ve on overflow. > (Further chaining would then work.) > > But, as I said, I've not tried to write it or see what it would look like in use. I think it's too complex for what it does. See how stpecpy() works: char tgt[100]; char *p, *e; e = tgt + _Countof(tgt); p = tgt; p = stpecpy(p, e, src_1); p = stpecpy(p, e, src_2); p = stpecpy(p, e, src_3); p = stpecpy(p, e, src_4); p = stpecpy(p, e, src_5); ... p = stpecpy(p, e, src_9); if (p == NULL) goto truncated; // 'p - tgt' is the length of the string. // In other words, 'p' points to the '\0'. It copies all the strings into tgt, with truncation. If any of the 5 calls resulted in truncation, it will be reported as a NULL. You can also do the chain in a single line: p = stpecpy(stpecpy(stpecpy(p, e, src_1), e, src_2), e, src_3); But IMO it reads better one per line. Then, strscpy(9) is trivially implementable as: ssize_t strscpy(char *dst, const char *src, size_t dsize) { char *p; BUG_ON(dsize == 0); p = stpecpy(dst, dst + dsize, src); return p ? p - dst : -E2BIG; } And a hypothetical strscat() would be: ssize_t strscat(char *dst, const char *src, size_t dsize) { char *p; BUG_ON(dsize == 0); p = stpecpy(dst + strlen(dst), dst + dsize, src); return p ? p - dst : -E2BIG; } I have been working literally years on this API, and believe it has reached perfection. It includes some feedback I got from Doug McIlroy about the design, which improved the usability and safety. Have a lovely night! Alex > > David --