Functions that returns a pointer into a string passed argument, such as strchr, ought to return - a 'char *' if the argument is a 'char *', - a 'const char *' if the argument is a 'const char *'.
This is now even mandated in ISO C 23 ยง 7.26.5.1. As C functions, these take a 'const char *' as argument and return a 'char *' (because C doesn't have function overloading). But the effect is that buggy code like this does not produce a warning with "gcc -Wall": ============================================= #include <string.h> int main () { const char *s = "hello world"; char *p = strchr (s, ' '); *p = 'x'; return 0; } ============================================= And yet it crashes at runtime. In order to make this code produce a warning, one needs to define strchr as a macro: #if __STDC_VERSION__ >= 202311 # define strchr(s,c) (typeof(s)) strchr ((s), (c)) #elif (__GNUC__ + (__GNUC_MINOR__ >= 9) > 4) || (__clang_major__ >= 3) || defined __ICC || defined __TINYC__ # define strchr(s,c) _Generic ((s), char const *: (char const *) strchr ((s), (c)), default: strchr ((s), (c))) #endif This uses the 'typeof' keyword from ISO C 23, or the '_Generic' keyword for older compilers. For C++, such a macro approach #if __cplusplus >= 201103L # define strchr(s,c) const_cast<decltype(s)>(strchr ((s), (c))) #endif should better be avoided, because it would not work when the first or second argument contains template parameters that include a comma outside of parentheses. So, for C++, the solution should be something with templates. If someone has spare time available for this, please go ahead. For the beginning, let me install this for 3 <string.h> functions. This could be generalized to more functions: strchr strrchr strchrnul mbschr mbsrchr memchr memrchr rawmemchr strstr strcasestr memmem strpbrk mbspbrk 2025-02-09 Bruno Haible <br...@clisp.org> mbsstr, mbscasestr, mbspcasecmp: Use const-improved function macros. * lib/string.in.h (mbsstr, mbspcasecmp, mbscasestr): Define as macros that cast the result to 'const char *' when the first argument is a 'const char *'. * lib/mbsstr.c: Define _GL_NO_CONST_GENERICS. * lib/mbscasestr.c: Likewise. * lib/mbspcasecmp.c: Likewise. diff --git a/lib/mbscasestr.c b/lib/mbscasestr.c index 7a186e0f98..843229666f 100644 --- a/lib/mbscasestr.c +++ b/lib/mbscasestr.c @@ -15,6 +15,9 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/* Don't use the const-improved function macros in this compilation unit. */ +#define _GL_NO_CONST_GENERICS + #include <config.h> /* Specification. */ diff --git a/lib/mbspcasecmp.c b/lib/mbspcasecmp.c index f57337f01e..0717cd893a 100644 --- a/lib/mbspcasecmp.c +++ b/lib/mbspcasecmp.c @@ -15,6 +15,9 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/* Don't use the const-improved function macros in this compilation unit. */ +#define _GL_NO_CONST_GENERICS + #include <config.h> /* Specification. */ diff --git a/lib/mbsstr.c b/lib/mbsstr.c index 579bcd054d..8e8eb73d55 100644 --- a/lib/mbsstr.c +++ b/lib/mbsstr.c @@ -15,6 +15,9 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/* Don't use the const-improved function macros in this compilation unit. */ +#define _GL_NO_CONST_GENERICS + #include <config.h> /* Specification. */ diff --git a/lib/string.in.h b/lib/string.in.h index ce48829900..6705967c59 100644 --- a/lib/string.in.h +++ b/lib/string.in.h @@ -1178,6 +1178,19 @@ _GL_CXXALIASWARN (mbsrchr); _GL_EXTERN_C char * mbsstr (const char *haystack, const char *needle) _GL_ATTRIBUTE_PURE _GL_ARG_NONNULL ((1, 2)); +/* Don't silently convert a 'const char *' to a 'char *'. Programmers want + compiler warnings for 'const' related mistakes. */ +# if !(defined _GL_NO_CONST_GENERICS || defined __cplusplus) +# if __STDC_VERSION__ >= 202311 +# define mbsstr(h,n) (typeof(h)) mbsstr ((h), (n)) +# elif ((__GNUC__ + (__GNUC_MINOR__ >= 9) > 4) || (__clang_major__ >= 3) \ + || defined __ICC || defined __TINYC__) +# define mbsstr(h,n) \ + _Generic ((h), \ + char const *: (char const *) mbsstr ((h), (n)), \ + default : mbsstr ((h), (n))) +# endif +# endif #endif #if @GNULIB_MBSCASECMP@ @@ -1219,6 +1232,19 @@ _GL_EXTERN_C int mbsncasecmp (const char *s1, const char *s2, size_t n) _GL_EXTERN_C char * mbspcasecmp (const char *string, const char *prefix) _GL_ATTRIBUTE_PURE _GL_ARG_NONNULL ((1, 2)); +/* Don't silently convert a 'const char *' to a 'char *'. Programmers want + compiler warnings for 'const' related mistakes. */ +# if !(defined _GL_NO_CONST_GENERICS || defined __cplusplus) +# if __STDC_VERSION__ >= 202311 +# define mbspcasecmp(s,p) (typeof(s)) mbspcasecmp ((s), (p)) +# elif ((__GNUC__ + (__GNUC_MINOR__ >= 9) > 4) || (__clang_major__ >= 3) \ + || defined __ICC || defined __TINYC__) +# define mbspcasecmp(s,p) \ + _Generic ((s), \ + char const *: (char const *) mbspcasecmp ((s), (p)), \ + default : mbspcasecmp ((s), (p))) +# endif +# endif #endif #if @GNULIB_MBSCASESTR@ @@ -1230,6 +1256,19 @@ _GL_EXTERN_C char * mbspcasecmp (const char *string, const char *prefix) _GL_EXTERN_C char * mbscasestr (const char *haystack, const char *needle) _GL_ATTRIBUTE_PURE _GL_ARG_NONNULL ((1, 2)); +/* Don't silently convert a 'const char *' to a 'char *'. Programmers want + compiler warnings for 'const' related mistakes. */ +# if !(defined _GL_NO_CONST_GENERICS || defined __cplusplus) +# if __STDC_VERSION__ >= 202311 +# define mbscasestr(h,n) (typeof(h)) mbscasestr ((h), (n)) +# elif ((__GNUC__ + (__GNUC_MINOR__ >= 9) > 4) || (__clang_major__ >= 3) \ + || defined __ICC || defined __TINYC__) +# define mbscasestr(h,n) \ + _Generic ((h), \ + char const *: (char const *) mbscasestr ((h), (n)), \ + default : mbscasestr ((h), (n))) +# endif +# endif #endif #if @GNULIB_MBSCSPN@