Name n3595 - add strpfx() 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 This paper is an alternative to n3567 from Yair Lenga. 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): - Rename to strpfx(). Description 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 = strpfx(tty, "/dev/") ?: tty; Still, this pointer is boolean-like, in the sense that strpfx() 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 (strpfx(buf, "#")) goto next; 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 strpfx(s, prefix) \ ({ \ const char *p_; \ \ p_ = strpfx_(s, prefix); \ \ _Generic(s, \ const char *: p_, \ const void *: p_, \ char *: const_cast(char *, p_),\ void *: const_cast(char *, p_) \ ); \ }) const char * strpfx_(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)) OpenSSH defines a similar API, although they have a third parameter which allows ignoring case differences. I prefer having a separate strcasepfx() variant, like shadow-utils does. I have not added strcasepfx() 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 String handling :: Search functions ## New section after 7.28.5.8 ("The strstr function"): +7.28.5.8+1 The strpfx generic function + +Synopsis +1 #include + QChar *strpfx(QChar *s, const char *prefix); + +Description +2 The strpfx function + checks if 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 strpfx function returns + s + strlen(prefix) + if the string starts with the given prefix, + or a null pointer otherwise. 7.33.4.6 Wide string search functions ## New section after 7.33.4.6.7 ("The wcsstr function"): +7.33.4.6.7+1 The wcspfx generic function + +Synopsis +1 #include + QWchar_t *wcspfx(QWchar_t *s, const wchar_t *prefix); + +Description +2 The wcspfx function + is equivalent to + strpfx, + except that it handles wide strings.