On 2026-02-25T23:28:22+0100, Alejandro Colomar wrote:
> Hi Bruno,
>
> On 2026-02-25T08:09:46+0100, Bruno Haible wrote:
> > Hi Alejandro,
> >
> > > > Oh wait. Should we have called the new function stpnul() instead of
> > > > strnul()?
> > > > Because it returns a pointer.
> > >
> > > ... We have other
> > > existing APIs that only have a variant that return an offset pointer,
> > > and their name is str*(), strchr(3) being the canonical example.
> > > stp*() would only be necessary where there's the two variants with the
> > > same name.
> >
> > OK for keeping strnul then.
> >
> > Among the str* functions that return a pointer (strchr, strrchr, strdup,
> > strpbrk, strstr), there is in particular strpbrk, which can be defined as
> >
> > char *
> > strpbrk (const char *s, const char *char_set)
> > {
> > size_t n = strcspn (s, char_set);
> > return s[n] ? (char *) &s[n] : NULL;
> > }
> >
> > So, as I understand it,
> > - stpcspn would be a variant of strpbrk,
> > - but stpspn is not such a variant.
>
> Your message prompted me to do some work in the manual pages, to clarify
> the incestuous relationship between string APIs. :)
>
> > * If we add only the stpspn function, we'll have tricky-to-remember
> > differences between stpspn and strpbrk.
>
> I've added the following text in strcspn(3):
>
> It is equivalent to
>
> (strpbrk(s, reject) ?: strnul(s)) ‐ s
>
> stpspn(3) has no equivalence to anything else, and can only be
> implemented with loops and byte operations.
>
> Similarly, strpbrk(3) has no equivalence to anything else, and can only
> be implemented with a loop.
>
> > * Whereas if we add both the stpspn and stpcspn functions, we'll have
> > two functions stpcspn, strpbrk that are merely variants of each other.
> >
> > Neither of which is super desirable.
>
> Here's a diff of the most relevant changes. I've documented the
> equivalences of functions to others, wherever possible. This should
> help clarify the tricky-to-remember differences you mentioned in the
> first point.
>
Here's a table of several string functions, and their equivalences (when
they're trivial):
Complex functions
=================
strpbrk(3) // C89
strspn(3) // C89
stprspn()
strnul(s) - strrspn(s, accept)
stprcspn()
strnul(s) - strrcspn(s, reject)
strsuffix()
strdup(3) // C23
aprintf()
strtok(3) // C89
strsep(3) // GNU, BSD
I ignored stuff that depends on the locale, such as strcoll(3).
Simple functions
================
Search
strchr(3) // C89
memchr(s, c, strlen(s) + 1)
strpbrk(s, (char [2]){c, '\0'})
strrchr(3) // C89
memrchr(s, c, strlen(s) + 1)
strnul(3) // GNU
s + strlen(s)
strchr(s, '\0')
strlen(3) // C89
strnul(s) ‐ s
strchrnul(3) // GNU, BSD
strchr(s, c) ?: strnul(s)
s + strcspn(s, (char [2]){c, '\0'})
stpspn()
s + strspn(s, accept)
strcspn(3) // C89
stpcspn(s, reject)
stpcspn()
strpbrk(s, reject) ?: strnul(s)
strrspn()
strnul(s) - stprspn(s, accept)
strrcspn()
strnul(s) - stprcspn(s, reject)
strstr(3) // C89
memmem(haystack, strlen(haystack), needle, strlen(needle))
strprefix()
strncmp(s, prefix, strlen(prefix)) ? NULL : s + strlen(prefix)
Comparison
strcmp(3) // C89
memcmp(s1, s2, MIN(strlen(s1),strlen(s2))+1)
streq(3) // GNU
strcmp(s1, s2) == 0
Copy
stpcpy(3) // POSIX.1-2008
memset(mempcpy(dst, src, strlen(src)), '\0', 1)
strcpy(dst, src) + strlen(dst)
strcpy(3) // C89
stpcpy(dst, src), dst
strcat(3) // C89
stpcpy(strnul(dst), src), dst
Dup
strdupa(3) // GNU
strcpy(alloca(strlen(s) + 1), s)
Separate
stpsep()
strpbrk(s, delim) ? stpcpy(strpbrk(s, delim), "") + 1 : NULL
strsep(&s, delim), s
--
<https://www.alejandro-colomar.es>
signature.asc
Description: PGP signature
