Name n3612, alx-0019r4 - add strpfx(), stppfx(), wcspfx(), and wcppfx() Principles - Codify existing practice to address evident deficiencies. - Enable secure programming Category Standardize common APIs Author Alejandro Colomar Cc: Yair Lenga Cc: Joseph Myers Cc: Patrizia Kaye Cc: Christopher Bazley History r0 (2025-06-09): - Initial draft. r1 (2025-06-09): - tfix - Move from comparison functions to search functions. QChar and WQchar_t are defined for search functions, but not for comparison ones. This API is a blurry hybrid, so put it there for simplicity. r2 (2025-06-10; n3595): - Rename to strpfx(). r3 (2025-06-12): - Rename to stppfx(), and add strpfx() as a bool variant, for use as a callback compatible with streq(). - Update the introductions to generic functions. r4 (2025-06-26; n3612): - wfix Rationale strncmp(3) is too confusing, and many projects come up with a wrapper strpfx() to do one of the common operations with it: Check if a string starts with a prefix, and optionally skip it. By developing such a wrapper in shadow utils, and replacing code that used strncmp(3) to use the wrapper, I've found and fixed several bugs. This API, unlike streq(), needs to return a pointer instead of a bool. This is because a common use case of this API is to skip an optional prefix. Here's an example of such a use, found in the shadow-utils project: tty = stppfx(tty, "/dev/") ?: tty; Still, this pointer is boolean-like, in the sense that stppfx() returns non-null if the prefix is found, and a null pointer if it is not found, so the usual case of just searching for a prefix can be programmed exacly as if this API returned a bool: if (stppfx(buf, "#")) goto next; Since in some cases it might be useful as a callback that is compatible with streq(), we add a bool-returning variant, strpfx(). Prior art Many projects define this API, either as a macro or as a function. I've developed it in shadow-utils as a const-generic macro. Here's an example implementation: #define stppfx(s, prefix) \ ({ \ const char *p_; \ \ p_ = stppfx_(s, prefix); \ \ _Generic(s, \ const char *: p_, \ const void *: p_, \ char *: const_cast(char *, p_),\ void *: const_cast(char *, p_) \ ); \ }) const char * stppfx_(const char *s, const char *prefix) { if (strncmp(s, prefix, strlen(prefix)) != 0) return NULL; return s + strlen(prefix); } #define const_cast(T, p) _Generic(p, const T: (T) (p)) bool strpfx(const char *s, const char *prefix) { return stppfx(s, prefix); } OpenSSH defines a similar API, although they have a third parameter which allows ignoring case differences. I prefer having a separate stpcasepfx() variant, like shadow-utils does. I have not added stpcasepfx() in this proposal because ISO C does not currently have strcasecmp(). OpenSSH uses a function, with the usual constness issues in string APIs that were solved in C2y. Proposed wording Based on N3550. 7.28.5.1 String handling :: Search functions :: Introduction @@ p1 The stateless search functions in this subclause -(memchr, strchr, strpbrk, strrchr, strstr) +(memchr, strchr, strpbrk, strrchr, strstr, stppfx) are generic functions. ... 7.28.5 String handling :: Search functions ## New sections after 7.28.5.8 ("The strstr function"): +7.28.5.8+1 The stppfx generic function + +Synopsis +1 #include + QChar *stppfx(QChar *s, const char *prefix); + +Description +2 The stppfx generic function + determines whether the string pointed to by s + starts with the sequence of characters + (excluding the terminating null character) + in the string pointed to by prefix. + +Returns +3 The stppfx generic function returns + s + strlen(prefix) + if the string starts with the given prefix, + or a null pointer otherwise. + +7.28.5.8+2 The strpfx function + +Synopsis +1 #include + bool strpfx(const char *s, const char *prefix); + +Description +2 The strpfx function + is similar to stppfx, + but returns a boolean value. + +Returns +3 The strpfx function returns + true + if and only if the string starts with the given prefix. 7.33.4.6.1 Wide string search functions :: Introduction @@ p1 The stateless search functions in this subclause -(wcschr, wcspbrk, wcsrchr, wmemchr, wcsstr) +(wcschr, wcspbrk, wcsrchr, wmemchr, wcsstr, wcppfx) are generic functions. ... 7.33.4.6 Wide string search functions ## New sections after 7.33.4.6.7 ("The wcpstr function"): +7.33.4.6.7+1 The wcppfx generic function + +Synopsis +1 #include + QWchar_t *wcppfx(QWchar_t *s, const wchar_t *prefix); + +Description +2 The wcppfx generic function + is equivalent to + stppfx, + except that it handles wide strings. + +7.33.4.6.7+2 The wcspfx function + +Synopsis +1 #include + bool wcspfx(const wchar_t *s, const wchar_t *prefix); + +Description +2 The wcspfx function + is equivalent to + strpfx, + except that it handles wide strings.