Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Date: Wed, 31 May 2023 16:00:26 +0200
From: Jens Gustedt <Jens.Gustedt@...ia.fr>
To: musl@...ts.openwall.com
Subject: [C23 const 2/2] C23: change string.h and wchar.h interfaces to macros that respects the const contract

This adds a macro interfaces to those string functions that search for
a string position. This has an additional cast of the return value to
`void const*` for the case that the argument to the call was also
const-qualified. Nothing changes for the correspondin function itself,
only the identifier has to be protected with (), such that the macro
does not expand for the function declaration or definition.

The implementation of this macro might be a bit unusual for musl. It
serves the purpose of better error tracking if users call the macro
with the wrong argument.

(0) Programming _Generic is hard. It needs that all choices are
syntactically and semantically valid. Otherwise the users drowns in
warnings and errors.

(1) Compilers nowadays track on which line in a macro definition an
error appears. For _Generic it helps a lot if the compiler presents
the exact choice which leads to a diagnostic.

(2) All object pointer values with unqualified or const-qualified
target are valid for the memchr function. Therefore we use a trick
that maps all const-qualified targets to void const*.

(3) All other functions have to accept either pointers to character
types (char or wchar_t) or pointers to void (where there is an
implicit conversion to the character pointer). Therefore we cannot use
the trick in (2), this would map any const-qualified pointer to that
case. Instead, we list the two possible const-qualifed cases
explicitly.

(4) The case of wrongly qualified pointer arguments would lead to a
misleading diagnostic without the casts.
---
 include/string.h     | 54 ++++++++++++++++++++++++++++++++++++++-----
 include/wchar.h      | 55 +++++++++++++++++++++++++++++++++++++++-----
 src/include/string.h |  6 +++++
 src/include/wchar.h  |  6 +++++
 src/string/memchr.c  |  2 +-
 src/string/strchr.c  |  2 +-
 src/string/strpbrk.c |  2 +-
 src/string/strrchr.c |  2 +-
 src/string/strstr.c  |  2 +-
 src/string/wcschr.c  |  2 +-
 src/string/wcspbrk.c |  2 +-
 src/string/wcsrchr.c |  2 +-
 src/string/wcsstr.c  |  2 +-
 src/string/wmemchr.c |  2 +-
 14 files changed, 119 insertions(+), 22 deletions(-)

diff --git a/include/string.h b/include/string.h
index 7df7a402..61768cf7 100644
--- a/include/string.h
+++ b/include/string.h
@@ -28,7 +28,54 @@ void *memcpy (void *__restrict, const void *__restrict, size_t);
 void *memmove (void *, const void *, size_t);
 void *memset (void *, int, size_t);
 int memcmp (const void *, const void *, size_t);
-void *memchr (const void *, int, size_t);
+
+void *(memchr) (const void *, int, size_t);
+char *(strchr) (const char *, int);
+char *(strrchr) (const char *, int);
+char *(strpbrk) (const char *, const char *);
+char *(strstr) (const char *, const char *);
+#if __STDC_VERSION__ > 201112L
+# define memchr(S, C, N)                                                \
+  _Generic(                                                             \
+           /* ensure conversion to a void pointer */                    \
+           1 ? (S) : (void*)1,                                          \
+           void const*: (void const*)memchr((void const*)(S), (C), (N)), \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     memchr((S), (C), (N))                           \
+)
+# define strchr(S, C)                                                   \
+  _Generic(                                                             \
+           (S),                                                         \
+           void const*: (char const*)strchr((char const*)(S), (C)),     \
+           char const*: (char const*)strchr((char const*)(S), (C)),     \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     strchr((S), (C))                                \
+)
+# define strrchr(S, C)                                                  \
+  _Generic(                                                             \
+           (S),                                                         \
+           void const*: (char const*)strrchr((char const*)(S), (C)),    \
+           char const*: (char const*)strrchr((char const*)(S), (C)),    \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     strrchr((S), (C))                               \
+)
+# define strpbrk(S, A)                                                  \
+  _Generic(                                                             \
+           (S),                                                         \
+           void const*: (char const*)strpbrk((char const*)(S), (A)),    \
+           char const*: (char const*)strpbrk((char const*)(S), (A)),    \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     strpbrk((S), (A))                               \
+)
+# define strstr(H, N)                                                   \
+  _Generic(                                                             \
+           (H),                                                         \
+           void const*: (char const*)strstr((char const*)(H), (N)),     \
+           char const*: (char const*)strstr((char const*)(H), (N)),     \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     strstr((H), (N))                                \
+)
+#endif
 
 char *strcpy (char *__restrict, const char *__restrict);
 char *strncpy (char *__restrict, const char *__restrict, size_t);
@@ -42,13 +89,8 @@ int strncmp (const char *, const char *, size_t);
 int strcoll (const char *, const char *);
 size_t strxfrm (char *__restrict, const char *__restrict, size_t);
 
-char *strchr (const char *, int);
-char *strrchr (const char *, int);
-
 size_t strcspn (const char *, const char *);
 size_t strspn (const char *, const char *);
-char *strpbrk (const char *, const char *);
-char *strstr (const char *, const char *);
 char *strtok (char *__restrict, const char *__restrict);
 
 size_t strlen (const char *);
diff --git a/include/wchar.h b/include/wchar.h
index ed5d774d..194f7f8f 100644
--- a/include/wchar.h
+++ b/include/wchar.h
@@ -61,21 +61,64 @@ int wcsncmp (const wchar_t *, const wchar_t *, size_t);
 int wcscoll(const wchar_t *, const wchar_t *);
 size_t wcsxfrm (wchar_t *__restrict, const wchar_t *__restrict, size_t);
 
-wchar_t *wcschr (const wchar_t *, wchar_t);
-wchar_t *wcsrchr (const wchar_t *, wchar_t);
-
 size_t wcscspn (const wchar_t *, const wchar_t *);
 size_t wcsspn (const wchar_t *, const wchar_t *);
-wchar_t *wcspbrk (const wchar_t *, const wchar_t *);
 
 wchar_t *wcstok (wchar_t *__restrict, const wchar_t *__restrict, wchar_t **__restrict);
 
 size_t wcslen (const wchar_t *);
 
-wchar_t *wcsstr (const wchar_t *__restrict, const wchar_t *__restrict);
 wchar_t *wcswcs (const wchar_t *, const wchar_t *);
 
-wchar_t *wmemchr (const wchar_t *, wchar_t, size_t);
+wchar_t *(wmemchr) (const wchar_t *, wchar_t, size_t);
+wchar_t *(wcschr) (const wchar_t *, wchar_t);
+wchar_t *(wcsrchr) (const wchar_t *, wchar_t);
+wchar_t *(wcspbrk) (const wchar_t *, const wchar_t *);
+wchar_t *(wcsstr) (const wchar_t *__restrict, const wchar_t *__restrict);
+#if __STDC_VERSION__ > 201112L
+# define wmemchr(S, C, N)                                               \
+  _Generic(                                                             \
+           (S),                                                         \
+           void const*: (wchar_t const*)wmemchr((wchar_t const*)(S), (C), (N)), \
+           wchar_t const*: (wchar_t const*)wmemchr((wchar_t const*)(S), (C), (N)), \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     wmemchr((S), (C), (N))                          \
+)
+# define wcschr(S, C)                                                   \
+  _Generic(                                                             \
+           (S),                                                         \
+           void const*: (wchar_t const*)wcschr((wchar_t const*)(S), (C)), \
+           wchar_t const*: (wchar_t const*)wcschr((wchar_t const*)(S), (C)), \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     wcschr((S), (C))                                \
+)
+# define wcsrchr(S, C)                                                  \
+  _Generic(                                                             \
+           (S),                                                         \
+           void const*: (wchar_t const*)wcsrchr((wchar_t const*)(S), (C)), \
+           wchar_t const*: (wchar_t const*)wcsrchr((wchar_t const*)(S), (C)), \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     wcsrchr((S), (C))                               \
+)
+# define wcspbrk(S, A)                                                  \
+  _Generic(                                                             \
+           (S),                                                         \
+           void const*: (wchar_t const*)wcspbrk((wchar_t const*)(S), (A)), \
+           wchar_t const*: (wchar_t const*)wcspbrk((wchar_t const*)(S), (A)), \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     wcspbrk((S), (A))                               \
+)
+# define wcsstr(H, N)                                                   \
+  _Generic(                                                             \
+           (H),                                                         \
+           void const*: (wchar_t const*)wcsstr((wchar_t const*)(H), (N)), \
+           wchar_t const*: (wchar_t const*)wcsstr((wchar_t const*)(H), (N)), \
+           /* volatile qualification of *S is an error for this call */ \
+           default:     wcsstr((H), (N))                                \
+)
+#endif
+
+
 int wmemcmp (const wchar_t *, const wchar_t *, size_t);
 wchar_t *wmemcpy (wchar_t *__restrict, const wchar_t *__restrict, size_t);
 wchar_t *wmemmove (wchar_t *, const wchar_t *, size_t);
diff --git a/src/include/string.h b/src/include/string.h
index 2133b5c1..f536c26b 100644
--- a/src/include/string.h
+++ b/src/include/string.h
@@ -8,4 +8,10 @@ hidden char *__stpcpy(char *, const char *);
 hidden char *__stpncpy(char *, const char *, size_t);
 hidden char *__strchrnul(const char *, int);
 
+#undef memchr
+#undef strchr
+#undef strrchr
+#undef strpbrk
+#undef strstr
+
 #endif
diff --git a/src/include/wchar.h b/src/include/wchar.h
index 79f5d0e7..dcd1cce1 100644
--- a/src/include/wchar.h
+++ b/src/include/wchar.h
@@ -5,5 +5,11 @@
 
 #include "../../include/wchar.h"
 
+#undef wmemchr
+#undef wcschr
+#undef wcsrchr
+#undef wcspbrk
+#undef wcsstr
+
 #endif
 
diff --git a/src/string/memchr.c b/src/string/memchr.c
index 65f0d789..f11c0573 100644
--- a/src/string/memchr.c
+++ b/src/string/memchr.c
@@ -8,7 +8,7 @@
 #define HIGHS (ONES * (UCHAR_MAX/2+1))
 #define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
 
-void *memchr(const void *src, int c, size_t n)
+void *(memchr)(const void *src, int c, size_t n)
 {
 	const unsigned char *s = src;
 	c = (unsigned char)c;
diff --git a/src/string/strchr.c b/src/string/strchr.c
index 3cbc828b..3a86beeb 100644
--- a/src/string/strchr.c
+++ b/src/string/strchr.c
@@ -1,6 +1,6 @@
 #include <string.h>
 
-char *strchr(const char *s, int c)
+char *(strchr)(const char *s, int c)
 {
 	char *r = __strchrnul(s, c);
 	return *(unsigned char *)r == (unsigned char)c ? r : 0;
diff --git a/src/string/strpbrk.c b/src/string/strpbrk.c
index 55947c64..0941fc51 100644
--- a/src/string/strpbrk.c
+++ b/src/string/strpbrk.c
@@ -1,6 +1,6 @@
 #include <string.h>
 
-char *strpbrk(const char *s, const char *b)
+char *(strpbrk)(const char *s, const char *b)
 {
 	s += strcspn(s, b);
 	return *s ? (char *)s : 0;
diff --git a/src/string/strrchr.c b/src/string/strrchr.c
index 98ad1b04..908fd8d4 100644
--- a/src/string/strrchr.c
+++ b/src/string/strrchr.c
@@ -1,6 +1,6 @@
 #include <string.h>
 
-char *strrchr(const char *s, int c)
+char *(strrchr)(const char *s, int c)
 {
 	return __memrchr(s, c, strlen(s) + 1);
 }
diff --git a/src/string/strstr.c b/src/string/strstr.c
index 96657bc2..5c622a82 100644
--- a/src/string/strstr.c
+++ b/src/string/strstr.c
@@ -135,7 +135,7 @@ static char *twoway_strstr(const unsigned char *h, const unsigned char *n)
 	}
 }
 
-char *strstr(const char *h, const char *n)
+char *(strstr)(const char *h, const char *n)
 {
 	/* Return immediately on empty needle */
 	if (!n[0]) return (char *)h;
diff --git a/src/string/wcschr.c b/src/string/wcschr.c
index 8dfc2f31..2bf7a111 100644
--- a/src/string/wcschr.c
+++ b/src/string/wcschr.c
@@ -1,6 +1,6 @@
 #include <wchar.h>
 
-wchar_t *wcschr(const wchar_t *s, wchar_t c)
+wchar_t *(wcschr)(const wchar_t *s, wchar_t c)
 {
 	if (!c) return (wchar_t *)s + wcslen(s);
 	for (; *s && *s != c; s++);
diff --git a/src/string/wcspbrk.c b/src/string/wcspbrk.c
index 0c72c197..eb76b5ff 100644
--- a/src/string/wcspbrk.c
+++ b/src/string/wcspbrk.c
@@ -1,6 +1,6 @@
 #include <wchar.h>
 
-wchar_t *wcspbrk(const wchar_t *s, const wchar_t *b)
+wchar_t *(wcspbrk)(const wchar_t *s, const wchar_t *b)
 {
 	s += wcscspn(s, b);
 	return *s ? (wchar_t *)s : NULL;
diff --git a/src/string/wcsrchr.c b/src/string/wcsrchr.c
index 8961b9e2..889303f3 100644
--- a/src/string/wcsrchr.c
+++ b/src/string/wcsrchr.c
@@ -1,6 +1,6 @@
 #include <wchar.h>
 
-wchar_t *wcsrchr(const wchar_t *s, wchar_t c)
+wchar_t *(wcsrchr)(const wchar_t *s, wchar_t c)
 {
 	const wchar_t *p;
 	for (p=s+wcslen(s); p>=s && *p!=c; p--);
diff --git a/src/string/wcsstr.c b/src/string/wcsstr.c
index 4caaef3c..8f27dbea 100644
--- a/src/string/wcsstr.c
+++ b/src/string/wcsstr.c
@@ -90,7 +90,7 @@ static wchar_t *twoway_wcsstr(const wchar_t *h, const wchar_t *n)
 	}
 }
 
-wchar_t *wcsstr(const wchar_t *restrict h, const wchar_t *restrict n)
+wchar_t *(wcsstr)(const wchar_t *restrict h, const wchar_t *restrict n)
 {
 	/* Return immediately on empty needle or haystack */
 	if (!n[0]) return (wchar_t *)h;
diff --git a/src/string/wmemchr.c b/src/string/wmemchr.c
index 2bc2c270..3d761488 100644
--- a/src/string/wmemchr.c
+++ b/src/string/wmemchr.c
@@ -1,6 +1,6 @@
 #include <wchar.h>
 
-wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n)
+wchar_t *(wmemchr)(const wchar_t *s, wchar_t c, size_t n)
 {
 	for (; n && *s != c; n--, s++);
 	return n ? (wchar_t *)s : 0;
-- 
2.34.1

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.