Hi Kees, On Mon, Jul 14, 2025 at 10:19:39PM -0700, Kees Cook wrote: > On Fri, Jul 11, 2025 at 10:58:56AM -0700, Linus Torvalds wrote: > > struct seq_buf s; > > seq_buf_init(&s, buf, szie); > > And because some folks didn't like this "declaration that requires a > function call", we even added: > > DECLARE_SEQ_BUF(s, 32); > > to do it in 1 line. :P > > I would love to see more string handling replaced with seq_buf. The thing is, it's not as easy as the fixes I'm proposing, and sprintf_end() solves a lot of UB in a minimal diff that you can dumbly apply. And transitioning from sprintf_end() to seq_buf will still be a possibility --probably even easier, because the code is simpler than with s[c]nprintf()--. Another thing, and this is my opinion, is that I'm not fond of APIs that keep an internal state. With sprintf_end(), the state is minimal and external: the state is the 'p' pointer to where you're going to write. That way, the programmer knows exactly where the writes occur, and can reason about it without having to read the implementation and keep a model of the state in its head. With a struct-based approach, you hide the state inside the structure, which means it's not so easy to reason about how an action will affect the string, at first glance; you need an expert in the API to know how to use it. With sprintf_end(), either one is stupid/careless enough to get the parameters wrong, or the function necessarily works well, *and is simple to fully understand*. And considering that we have ENDOF(), it's hard to understand how one could get it wrong: p = buf; e = ENDOF(buf); p = sprintf_end(p, e, ...); p = sprintf_end(p, e, ...); p = sprintf_end(p, e, ...); p = sprintf_end(p, e, ...); Admittedly, ENDOF() doesn't compile if buf is not an array, so in those cases, there's a chance of a paranoic programmer slapping a -1 just in case, but that doesn't hurt: p = buf; e = buf + size; // Someone might accidentally -1 that? I'm working on extending the _Countof() operator so that it can be applied to array parameters to functions, so that it can be used to count arrays that are not arrays: void f(size_t n, char buf[n]) { p = buf; e = buf + _Countof(buf); // _Countof(buf) will evaluate to n. ... } Which will significantly enhance the usability of sprintf_end(). I want to implement this for GCC next year (there are a few things that need to be improved first to be able to do that), and also propose it for standardization. For a similar comparison of stateful vs stateless functions, there are strtok(3) and strsep(3), which apart from minor differences (strtok(3) collapses adjacent delimiters) are more or less the same. But I'd use strsep(3) over strtok(3), even if just because strtok(3) keeps an internal state, so I always need to be very careful of reading the documentation to remind myself of what happens to the state after each call. strsep(3) is dead simple: you call it, and it updates the pointer you passed; nothing is kept secretly from the programmer. Have a lovely day! Alex --