Hi Crystal,

On 2026-02-19T14:12:26+0000, Crystal Kolipe via Mutt-dev wrote:
> On Thu, Feb 19, 2026 at 02:32:45PM +0100, Alejandro Colomar via Mutt-dev 
> wrote:
> > However, I wouldn't worry
> > about performance in systems that don't implement strchrnul(3); those
> > are rare, and the difference would be small-ish.  Let the systems
> > implement strchrnul(3) if they care about performance.
> 
> I could certainly write a native strchrnul() for OpenBSD, but I doubt there
> would be interest there in accepting a such function for which obvious
> work-arounds and replacements already exist.
> 
> We recently had some breakage in MATE due to glic extensions to strftime() not
> being implemented.  I floated a proof of concept minimal implementation that
> fixed the specific issue but interest in it was effectively zero.
> 
> In reality, strchrnul() in third party code is being locally replaced with
> alternative constructs such as:
> 
> foo = strchrnul(bar, 'X');
> 
> foo = foo + strcspn(bar, "X");

Yeah, that's the usual approach when it's a literal.  The problem is
when it's a variable, in which case you need a local array.

BTW, this code is buggy, and I understand this is an email you wrote
quickly and not something you'd be more careful about in real code, but
writing
        foo = foo + strcspn(bar, "X");
instead of
        foo = bar + strcspn(bar, "X");
is indeed quite possible, and the compiler wouldn't even notice.  I hate
having to type variables more than once, and that's something macros
force you to do correctly (because double evaluation is a serious
concern).  In some sense, macros force you to write code more carefully,
because the danger is more evident.

In the case of the strspn(3) family, I write for myself a set of
wrappers that return a pointer instead of a length.  That avoids having
to type the name twice, which as we've seen is error prone.

        #define stpspn(s, accept)                                     \
        ({                                                            \
                __auto_type  s_ = s;                                  \
                                                                      \
                s_ + strspn(s_, accept);                              \
        })

which would be used as:

        foo = stpspn(bar, "X");
instead of
        foo = bar + strspn(bar, "X");

We have this macro in shadow-utils, where we use it often.
<https://github.com/shadow-maint/shadow/blob/master/lib/string/strspn/stpspn.h>

I don't have a stpcspn() variant, because that's something we don't use,
as we use strchrnul(3).

We use stpspn() for example for skipping white space.  This is a common
idiom:

        p = stpspn(p, " \t");

You might be able to convince OpenBSD people to use such macros, as they
reduce moving parts, and thus chances of bugs.  They should make it
easier to review such code.

I think these stp*() variants should be part of the standard C library,
precisely because they're trivial to implement, and they prevent bugs
that are too easy to produce.

While these APIs were rejected by the C Committee (some members of the
committee will just vote against anything that has to do with strings;
others will vote against anything that is trivial to implement), you can
find an N-document that specifies them (authored by me), in case you
want to point to a formal specification of the APIs:
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3663.txt>


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>

Attachment: signature.asc
Description: PGP signature

Reply via email to