Re: strpbrk stops expand.c from compiling on Ubuntu 26-04

Andrew C Aitchison via Exim-dev <[email protected]> Wed, 13 May 2026 15:27:26 +0100 (BST)
Newsgroups gmane.mail.exim.devel
Message-ID <[email protected]>
On Sat, 2 May 2026, Jeremy Harris via Exim-dev wrote:

> On 2026/05/02 9:50 PM, Andrew C Aitchison via Exim-dev wrote:
>> Do we have an Ubuntu 26-04 (Resolute Raccoon) build animal ?
>
> We don't have any animals at all that claim to be Ubuntu.
> As usual, volunteers would be welcome.

I will look into that; I had been put off by
https://buildfarm.exim.org/wiki/Installation.html
   We’re not discouraging you from joining the Exim BuildFarm if you’re
   Debian or Debian derivative, but merely want to acknowledge the
   excellent job the Debian project already does with it.
but I should have taken more not of "We’re not discouraging you" :-)

>> The full definition of strpbrk in /usr/include/string.h
>> (which *has* changed in this Ubuntu) now conditionally has
>> separate declarations for const and non-const:
>> 
>> grep -3 strpbrk /usr/include/string.h | sed -e 's/^/  /'
>>    #ifdef __CORRECT_ISO_CPP_STRING_H_PROTO
>>    extern "C++"
>>    {
>>    extern char *strpbrk (char *__s, const char *__accept)
>>         __THROW __asm ("strpbrk") __attribute_pure__ __nonnull ((1, 2));
>>    extern const char *strpbrk (const char *__s, const char *__accept)
>>         __THROW __asm ("strpbrk") __attribute_pure__ __nonnull ((1, 2));
>>
>>    # ifdef __OPTIMIZE__
>>    __extern_always_inline char *
>>    strpbrk (char *__s, const char *__accept) __THROW
>>    {
>>      return __builtin_strpbrk (__s, __accept);
>>    }
>>
>>    __extern_always_inline const char *
>>    strpbrk (const char *__s, const char *__accept) __THROW
>>    {
>>      return __builtin_strpbrk (__s, __accept);
>>    }
>>    # endif
>>    }
>>    #else
>>    extern char *strpbrk (const char *__s, const char *__accept)
>>         __THROW __attribute_pure__ __nonnull ((1, 2));
>>    # if __GLIBC_USE (ISOC23) && defined __glibc_const_generic && !defined 
>> _LIBC
>>    #  define strpbrk(S, ACCEPT)                                  \
>>      __glibc_const_generic (S, const char *, strpbrk (S, ACCEPT))
>>    # endif
>>    #endif
>>    /* Find the first occurrence of NEEDLE in HAYSTACK.  */
>> 
>> Does this look different from other platforms, in a way that might cause
>> the compiler to object ?
>
> Fedora 43 /usr/include/string.h looks pretty much the same.  I have no clue
> if that stuff affects C, or is C++ only.
>
>> clang -E expand.c turns line 4638 into
>>     for(char * cp; cp = _Generic (0 ? ((const char *) item) : (void *) 1, 
>> const void *: (const char *) (strpbrk ((const char *) item, tok)), default: 
>> strpbrk ((const char *) item, tok)); item = (unsigned char *) cp) 
>
>
> ... and what I don't see in (the string.h section) is any mention of a 
> Generic.
>
> Though, a known generic we could use here to operate cleanly on a
> (non-const) string [ "item" not being const ] happens to be really
> what is wanted.  I wonder when the definition of strbrk() changed?
> The manpage here only describes it as
>
>     char *strpbrk(const char *s, const char *accept);
> STANDARDS
>       C11, POSIX.1-2008.

If I understand correctly you can pass a non-const to an argument
declared as const, as long as there is no alternative
definition?declaration with a non-const argument.
Did we actually mean to pass 'CCS item' to strpbrk at expand.c:4638
- two lines later expand.c passes 'CS item' to string_catn ?

The attached as a patch works on my compilers and doesn't change the
results of a full
   ./runtest --keep --continue
Does it work on older systems ?

>> cc1: all warnings being treated as errors 
>
> It's quite likely I don't do that.


-- 
Andrew C. Aitchison                      Kendal, UK
                    [email protected]
strpbrk.patch (text/x-diff, 576 B)
diff --git a/src/src/expand.c b/src/src/expand.c
index e954ee724..231e0edd5 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -4635,7 +4635,7 @@ while ((item = string_nextinlist(&list, &sep, buffer, LISTNAMED_BUF_SIZE)))
     char tok[3];
     tok[0] = sep; tok[1] = ':'; tok[2] = 0;
 
-    for(char * cp; cp = strpbrk(CCS item, tok); item = US cp)
+    for(char * cp; cp = strpbrk(CS item, tok); item = US cp)
       {
       yield = string_catn(yield, item, cp - CS item);
       if (*cp++ == ':')	/* colon in a non-colon-sep list item, needs doubling */