* [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names
@ 2026-07-02 12:33 Alejandro Colomar
2026-07-02 12:33 ` [PATCH v1 2/2] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
` (3 more replies)
0 siblings, 4 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-02 12:33 UTC (permalink / raw)
To: linux-man; +Cc: Alejandro Colomar, Serge E. Hallyn
[-- Attachment #1: Type: text/plain, Size: 12277 bytes --]
Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
Hi Serge,
I finally took some time to do this that you asked me to do some time
ago. I've edited all the str*(3) manual pages so that the NAME section
actually explains the name of the function.
There's an exception: strpbrk(3). I refuse to explain that name. I've
explained it as if it were called strchrs(), which is a name that Plan9
uses for that function, and which would have been a better name for it.
(See patch 2/2).
Have a lovely day!
Alex
man/man3/strcasecmp.3 | 2 +-
man/man3/strchr.3 | 2 +-
man/man3/strcmp.3 | 2 +-
man/man3/strcoll.3 | 2 +-
man/man3/strcpy.3 | 2 +-
man/man3/strdup.3 | 2 +-
man/man3/strerror.3 | 5 +++--
man/man3/strfmon.3 | 2 +-
man/man3/strfromd.3 | 3 +--
man/man3/strfry.3 | 2 +-
man/man3/strftime.3 | 2 +-
man/man3/string.3 | 5 +----
man/man3/strlen.3 | 2 +-
man/man3/strncat.3 | 5 +----
man/man3/strnlen.3 | 2 +-
man/man3/strpbrk.3 | 2 +-
man/man3/strptime.3 | 2 +-
man/man3/strsep.3 | 2 +-
man/man3/strsignal.3 | 2 +-
man/man3/strspn.3 | 2 +-
man/man3/strtod.3 | 2 +-
man/man3/strtoimax.3 | 2 +-
man/man3/strtok.3 | 2 +-
man/man3/strtol.3 | 2 +-
man/man3/strtoul.3 | 2 +-
man/man3/strverscmp.3 | 2 +-
man/man3/strxfrm.3 | 2 +-
27 files changed, 29 insertions(+), 35 deletions(-)
diff --git a/man/man3/strcasecmp.3 b/man/man3/strcasecmp.3
index aa554341da4c..65941ddf6524 100644
--- a/man/man3/strcasecmp.3
+++ b/man/man3/strcasecmp.3
@@ -6,7 +6,7 @@
.\"
.TH strcasecmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcasecmp, strncasecmp \- compare two strings ignoring case
+strcasecmp, strncasecmp \- strings case-insensitive compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strchr.3 b/man/man3/strchr.3
index 4e6a96d8cde6..12d08f63ddbb 100644
--- a/man/man3/strchr.3
+++ b/man/man3/strchr.3
@@ -6,7 +6,7 @@
.\"
.TH strchr 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strchr \- locate character in string
+strchr \- string search character
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcmp.3 b/man/man3/strcmp.3
index a088412917d2..0a573e52d936 100644
--- a/man/man3/strcmp.3
+++ b/man/man3/strcmp.3
@@ -7,7 +7,7 @@
.\"
.TH strcmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcmp, strncmp \- compare two strings
+strcmp, strncmp \- strings compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcoll.3 b/man/man3/strcoll.3
index 32d620f52c51..ea94f2a90224 100644
--- a/man/man3/strcoll.3
+++ b/man/man3/strcoll.3
@@ -6,7 +6,7 @@
.\"
.TH strcoll 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcoll \- compare two strings using the current locale
+strcoll \- strings collate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcpy.3 b/man/man3/strcpy.3
index cea24ff04020..d4d0e4246545 100644
--- a/man/man3/strcpy.3
+++ b/man/man3/strcpy.3
@@ -5,7 +5,7 @@
.\"
.TH strcpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcpy, strcat \- copy or catenate a string
+strcpy, strcat \- string copy/catenate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strdup.3 b/man/man3/strdup.3
index b4026ed41b4a..05671cd936c2 100644
--- a/man/man3/strdup.3
+++ b/man/man3/strdup.3
@@ -6,7 +6,7 @@
.\"
.TH strdup 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strdup, strndup \- duplicate a string
+strdup, strndup \- string duplicate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strerror.3 b/man/man3/strerror.3
index e3306c7e1608..637d1d812d52 100644
--- a/man/man3/strerror.3
+++ b/man/man3/strerror.3
@@ -7,8 +7,9 @@
.\"
.TH strerror 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l \-
-return string describing error number
+strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l
+\-
+string error-string
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfmon.3 b/man/man3/strfmon.3
index e0d9790395cb..dee915c8a9f4 100644
--- a/man/man3/strfmon.3
+++ b/man/man3/strfmon.3
@@ -6,7 +6,7 @@
.\"
.TH strfmon 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfmon, strfmon_l \- convert monetary value to a string
+strfmon, strfmon_l \- string format monetary value
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfromd.3 b/man/man3/strfromd.3
index c221f82df0a8..fa8a10feec01 100644
--- a/man/man3/strfromd.3
+++ b/man/man3/strfromd.3
@@ -6,8 +6,7 @@
.\"
.TH strfromd 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfromd, strfromf, strfroml \- convert a floating-point value into
-a string
+strfromd, strfromf, strfroml \- string from float
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
index 99917e4a6261..54ea313ea262 100644
--- a/man/man3/strfry.3
+++ b/man/man3/strfry.3
@@ -6,7 +6,7 @@
.\"
.TH strfry 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfry \- randomize a string
+strfry \- string fry
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strftime.3 b/man/man3/strftime.3
index 66351f6bd43a..bdba4089b934 100644
--- a/man/man3/strftime.3
+++ b/man/man3/strftime.3
@@ -6,7 +6,7 @@
.\"
.TH strftime 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strftime \- format date and time
+strftime \- string format date and time
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/string.3 b/man/man3/string.3
index 89fe51fba609..2141822e1fd1 100644
--- a/man/man3/string.3
+++ b/man/man3/string.3
@@ -5,10 +5,7 @@
.\"
.TH string 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-stpcpy, strcasecmp, strcat, strchr, strcmp, strcoll, strcpy, strcspn,
-strdup, strfry, strlen, strncat, strncmp, strncpy, strncasecmp, strpbrk,
-strrchr, strsep, strspn, strstr, strtok, strxfrm
-\- string operations
+string \- string operations
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strlen.3 b/man/man3/strlen.3
index 1dd99bd8e250..639bdd6ac2d2 100644
--- a/man/man3/strlen.3
+++ b/man/man3/strlen.3
@@ -6,7 +6,7 @@
.\"
.TH strlen 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strlen \- calculate the length of a string
+strlen \- string length
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strncat.3 b/man/man3/strncat.3
index 4b0217b02673..dd39d54939d9 100644
--- a/man/man3/strncat.3
+++ b/man/man3/strncat.3
@@ -5,10 +5,7 @@
.\"
.TH strncat 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strncat
-\-
-append non-null bytes from a source array to a string,
-and null-terminate the result
+strncat \- nonstring catenate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strnlen.3 b/man/man3/strnlen.3
index fbd719ee4888..553a39a9ec7a 100644
--- a/man/man3/strnlen.3
+++ b/man/man3/strnlen.3
@@ -6,7 +6,7 @@
.\"
.TH strnlen 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strnlen \- determine the length of a fixed-size string
+strnlen \- nonstring length
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
index 1f56cf18dbac..6e5b79a48bbe 100644
--- a/man/man3/strpbrk.3
+++ b/man/man3/strpbrk.3
@@ -6,7 +6,7 @@
.\"
.TH strpbrk 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strpbrk \- search a string for any of a set of bytes
+strpbrk \- string search characters
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strptime.3 b/man/man3/strptime.3
index 979bffea7bf8..4502acfe6d1e 100644
--- a/man/man3/strptime.3
+++ b/man/man3/strptime.3
@@ -6,7 +6,7 @@
.\"
.TH strptime 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strptime \- convert a string representation of time to a time tm structure
+strptime \- string parse time
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strsep.3 b/man/man3/strsep.3
index 46ba6c5819bd..98859f64909f 100644
--- a/man/man3/strsep.3
+++ b/man/man3/strsep.3
@@ -6,7 +6,7 @@
.\"
.TH strsep 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strsep \- extract token from string
+strsep \- string separate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strsignal.3 b/man/man3/strsignal.3
index 4a4e8c86e71d..964a5e5aacaf 100644
--- a/man/man3/strsignal.3
+++ b/man/man3/strsignal.3
@@ -8,7 +8,7 @@
.TH strsignal 3 (date) "Linux man-pages (unreleased)"
.SH NAME
strsignal, sigabbrev_np, sigdescr_np, sys_siglist \-
-return string describing signal
+string signal description
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strspn.3 b/man/man3/strspn.3
index fe4414317df6..7c85d209c59d 100644
--- a/man/man3/strspn.3
+++ b/man/man3/strspn.3
@@ -6,7 +6,7 @@
.\"
.TH strspn 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strspn, strcspn \- get length of a prefix substring
+strspn, strcspn \- string [complementary] span
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtod.3 b/man/man3/strtod.3
index 15147ee51006..84a9934ba267 100644
--- a/man/man3/strtod.3
+++ b/man/man3/strtod.3
@@ -6,7 +6,7 @@
.\"
.TH strtod 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtod, strtof, strtold \- convert ASCII string to floating-point number
+strtod, strtof, strtold \- string to float
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtoimax.3 b/man/man3/strtoimax.3
index 71267754ed7a..08413a9f8101 100644
--- a/man/man3/strtoimax.3
+++ b/man/man3/strtoimax.3
@@ -6,7 +6,7 @@
.\"
.TH strtoimax 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtoimax, strtoumax \- convert string to integer
+strtoimax, strtoumax \- string to [u]intmax_t
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtok.3 b/man/man3/strtok.3
index 2d733e886086..4ce708ecbcb4 100644
--- a/man/man3/strtok.3
+++ b/man/man3/strtok.3
@@ -6,7 +6,7 @@
.\"
.TH strtok 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtok \- extract tokens from strings
+strtok \- string tokenize
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtol.3 b/man/man3/strtol.3
index f88b717780f3..4d9c49eaf28a 100644
--- a/man/man3/strtol.3
+++ b/man/man3/strtol.3
@@ -7,7 +7,7 @@
.\"
.TH strtol 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtol, strtoll, strtoq \- convert a string to a long integer
+strtol, strtoll, strtoq \- string to long int
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtoul.3 b/man/man3/strtoul.3
index a6f6fd4a8ada..670fcb3868a1 100644
--- a/man/man3/strtoul.3
+++ b/man/man3/strtoul.3
@@ -6,7 +6,7 @@
.\"
.TH strtoul 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtoul, strtoull, strtouq \- convert a string to an unsigned long integer
+strtoul, strtoull, strtouq \- string to unsigned long int
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strverscmp.3 b/man/man3/strverscmp.3
index d072471b1431..caadb9717a3b 100644
--- a/man/man3/strverscmp.3
+++ b/man/man3/strverscmp.3
@@ -7,7 +7,7 @@
.\"
.TH strverscmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strverscmp \- compare two version strings
+strverscmp \- strings version compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strxfrm.3 b/man/man3/strxfrm.3
index d4ac57006eb0..a892bf4c813b 100644
--- a/man/man3/strxfrm.3
+++ b/man/man3/strxfrm.3
@@ -6,7 +6,7 @@
.\"
.TH strxfrm 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strxfrm \- string transformation
+strxfrm \- string transform
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v1 2/2] man/man3/strpbrk.3: BUGS: Clarify the NAME
2026-07-02 12:33 [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Alejandro Colomar
@ 2026-07-02 12:33 ` Alejandro Colomar
2026-07-03 5:03 ` [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Mark Harris
` (2 subsequent siblings)
3 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-02 12:33 UTC (permalink / raw)
To: linux-man; +Cc: Alejandro Colomar, Serge E. Hallyn
[-- Attachment #1: Type: text/plain, Size: 607 bytes --]
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/strpbrk.3 | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
index 6e5b79a48bbe..ee7a84f1a944 100644
--- a/man/man3/strpbrk.3
+++ b/man/man3/strpbrk.3
@@ -51,6 +51,10 @@ .SH STANDARDS
C11, POSIX.1-2008.
.SH HISTORY
POSIX.1-2001, C89, SVr4, 4.3BSD.
+.SH BUGS
+The name of this function is bad.
+It should have been named strchrs().
+The NAME section above reflects this.
.SH SEE ALSO
.BR memchr (3),
.BR strchr (3),
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names
2026-07-02 12:33 [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Alejandro Colomar
2026-07-02 12:33 ` [PATCH v1 2/2] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
@ 2026-07-03 5:03 ` Mark Harris
2026-07-03 11:16 ` Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 0/4] str*.3, mem*.3: " Alejandro Colomar
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
3 siblings, 1 reply; 31+ messages in thread
From: Mark Harris @ 2026-07-03 5:03 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: linux-man, Serge E. Hallyn
Alejandro Colomar wrote:
>
> Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
>
> Hi Serge,
>
> I finally took some time to do this that you asked me to do some time
> ago. I've edited all the str*(3) manual pages so that the NAME section
> actually explains the name of the function.
>
> There's an exception: strpbrk(3). I refuse to explain that name. I've
> explained it as if it were called strchrs(), which is a name that Plan9
> uses for that function, and which would have been a better name for it.
> (See patch 2/2).
>
>
> Have a lovely day!
> Alex
>
> man/man3/strcasecmp.3 | 2 +-
> man/man3/strchr.3 | 2 +-
> man/man3/strcmp.3 | 2 +-
> man/man3/strcoll.3 | 2 +-
> man/man3/strcpy.3 | 2 +-
> man/man3/strdup.3 | 2 +-
> man/man3/strerror.3 | 5 +++--
> man/man3/strfmon.3 | 2 +-
> man/man3/strfromd.3 | 3 +--
> man/man3/strfry.3 | 2 +-
> man/man3/strftime.3 | 2 +-
> man/man3/string.3 | 5 +----
> man/man3/strlen.3 | 2 +-
> man/man3/strncat.3 | 5 +----
> man/man3/strnlen.3 | 2 +-
> man/man3/strpbrk.3 | 2 +-
> man/man3/strptime.3 | 2 +-
> man/man3/strsep.3 | 2 +-
> man/man3/strsignal.3 | 2 +-
> man/man3/strspn.3 | 2 +-
> man/man3/strtod.3 | 2 +-
> man/man3/strtoimax.3 | 2 +-
> man/man3/strtok.3 | 2 +-
> man/man3/strtol.3 | 2 +-
> man/man3/strtoul.3 | 2 +-
> man/man3/strverscmp.3 | 2 +-
> man/man3/strxfrm.3 | 2 +-
> 27 files changed, 29 insertions(+), 35 deletions(-)
>
> diff --git a/man/man3/strcasecmp.3 b/man/man3/strcasecmp.3
> index aa554341da4c..65941ddf6524 100644
> --- a/man/man3/strcasecmp.3
> +++ b/man/man3/strcasecmp.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strcasecmp 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcasecmp, strncasecmp \- compare two strings ignoring case
> +strcasecmp, strncasecmp \- strings case-insensitive compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strchr.3 b/man/man3/strchr.3
> index 4e6a96d8cde6..12d08f63ddbb 100644
> --- a/man/man3/strchr.3
> +++ b/man/man3/strchr.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strchr 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strchr \- locate character in string
> +strchr \- string search character
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strcmp.3 b/man/man3/strcmp.3
> index a088412917d2..0a573e52d936 100644
> --- a/man/man3/strcmp.3
> +++ b/man/man3/strcmp.3
> @@ -7,7 +7,7 @@
> .\"
> .TH strcmp 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcmp, strncmp \- compare two strings
> +strcmp, strncmp \- strings compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strcoll.3 b/man/man3/strcoll.3
> index 32d620f52c51..ea94f2a90224 100644
> --- a/man/man3/strcoll.3
> +++ b/man/man3/strcoll.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strcoll 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcoll \- compare two strings using the current locale
> +strcoll \- strings collate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strcpy.3 b/man/man3/strcpy.3
> index cea24ff04020..d4d0e4246545 100644
> --- a/man/man3/strcpy.3
> +++ b/man/man3/strcpy.3
> @@ -5,7 +5,7 @@
> .\"
> .TH strcpy 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcpy, strcat \- copy or catenate a string
> +strcpy, strcat \- string copy/catenate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strdup.3 b/man/man3/strdup.3
> index b4026ed41b4a..05671cd936c2 100644
> --- a/man/man3/strdup.3
> +++ b/man/man3/strdup.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strdup 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strdup, strndup \- duplicate a string
> +strdup, strndup \- string duplicate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strerror.3 b/man/man3/strerror.3
> index e3306c7e1608..637d1d812d52 100644
> --- a/man/man3/strerror.3
> +++ b/man/man3/strerror.3
> @@ -7,8 +7,9 @@
> .\"
> .TH strerror 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l \-
> -return string describing error number
> +strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l
> +\-
> +string error-string
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strfmon.3 b/man/man3/strfmon.3
> index e0d9790395cb..dee915c8a9f4 100644
> --- a/man/man3/strfmon.3
> +++ b/man/man3/strfmon.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strfmon 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strfmon, strfmon_l \- convert monetary value to a string
> +strfmon, strfmon_l \- string format monetary value
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strfromd.3 b/man/man3/strfromd.3
> index c221f82df0a8..fa8a10feec01 100644
> --- a/man/man3/strfromd.3
> +++ b/man/man3/strfromd.3
> @@ -6,8 +6,7 @@
> .\"
> .TH strfromd 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strfromd, strfromf, strfroml \- convert a floating-point value into
> -a string
> +strfromd, strfromf, strfroml \- string from float
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
> index 99917e4a6261..54ea313ea262 100644
> --- a/man/man3/strfry.3
> +++ b/man/man3/strfry.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strfry 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strfry \- randomize a string
> +strfry \- string fry
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strftime.3 b/man/man3/strftime.3
> index 66351f6bd43a..bdba4089b934 100644
> --- a/man/man3/strftime.3
> +++ b/man/man3/strftime.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strftime 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strftime \- format date and time
> +strftime \- string format date and time
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/string.3 b/man/man3/string.3
> index 89fe51fba609..2141822e1fd1 100644
> --- a/man/man3/string.3
> +++ b/man/man3/string.3
> @@ -5,10 +5,7 @@
> .\"
> .TH string 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -stpcpy, strcasecmp, strcat, strchr, strcmp, strcoll, strcpy, strcspn,
> -strdup, strfry, strlen, strncat, strncmp, strncpy, strncasecmp, strpbrk,
> -strrchr, strsep, strspn, strstr, strtok, strxfrm
> -\- string operations
> +string \- string operations
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strlen.3 b/man/man3/strlen.3
> index 1dd99bd8e250..639bdd6ac2d2 100644
> --- a/man/man3/strlen.3
> +++ b/man/man3/strlen.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strlen 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strlen \- calculate the length of a string
> +strlen \- string length
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strncat.3 b/man/man3/strncat.3
> index 4b0217b02673..dd39d54939d9 100644
> --- a/man/man3/strncat.3
> +++ b/man/man3/strncat.3
> @@ -5,10 +5,7 @@
> .\"
> .TH strncat 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strncat
> -\-
> -append non-null bytes from a source array to a string,
> -and null-terminate the result
> +strncat \- nonstring catenate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strnlen.3 b/man/man3/strnlen.3
> index fbd719ee4888..553a39a9ec7a 100644
> --- a/man/man3/strnlen.3
> +++ b/man/man3/strnlen.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strnlen 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strnlen \- determine the length of a fixed-size string
> +strnlen \- nonstring length
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
> index 1f56cf18dbac..6e5b79a48bbe 100644
> --- a/man/man3/strpbrk.3
> +++ b/man/man3/strpbrk.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strpbrk 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strpbrk \- search a string for any of a set of bytes
> +strpbrk \- string search characters
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strptime.3 b/man/man3/strptime.3
> index 979bffea7bf8..4502acfe6d1e 100644
> --- a/man/man3/strptime.3
> +++ b/man/man3/strptime.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strptime 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strptime \- convert a string representation of time to a time tm structure
> +strptime \- string parse time
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strsep.3 b/man/man3/strsep.3
> index 46ba6c5819bd..98859f64909f 100644
> --- a/man/man3/strsep.3
> +++ b/man/man3/strsep.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strsep 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strsep \- extract token from string
> +strsep \- string separate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strsignal.3 b/man/man3/strsignal.3
> index 4a4e8c86e71d..964a5e5aacaf 100644
> --- a/man/man3/strsignal.3
> +++ b/man/man3/strsignal.3
> @@ -8,7 +8,7 @@
> .TH strsignal 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> strsignal, sigabbrev_np, sigdescr_np, sys_siglist \-
> -return string describing signal
> +string signal description
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strspn.3 b/man/man3/strspn.3
> index fe4414317df6..7c85d209c59d 100644
> --- a/man/man3/strspn.3
> +++ b/man/man3/strspn.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strspn 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strspn, strcspn \- get length of a prefix substring
> +strspn, strcspn \- string [complementary] span
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtod.3 b/man/man3/strtod.3
> index 15147ee51006..84a9934ba267 100644
> --- a/man/man3/strtod.3
> +++ b/man/man3/strtod.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtod 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtod, strtof, strtold \- convert ASCII string to floating-point number
> +strtod, strtof, strtold \- string to float
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtoimax.3 b/man/man3/strtoimax.3
> index 71267754ed7a..08413a9f8101 100644
> --- a/man/man3/strtoimax.3
> +++ b/man/man3/strtoimax.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtoimax 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtoimax, strtoumax \- convert string to integer
> +strtoimax, strtoumax \- string to [u]intmax_t
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtok.3 b/man/man3/strtok.3
> index 2d733e886086..4ce708ecbcb4 100644
> --- a/man/man3/strtok.3
> +++ b/man/man3/strtok.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtok 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtok \- extract tokens from strings
> +strtok \- string tokenize
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtol.3 b/man/man3/strtol.3
> index f88b717780f3..4d9c49eaf28a 100644
> --- a/man/man3/strtol.3
> +++ b/man/man3/strtol.3
> @@ -7,7 +7,7 @@
> .\"
> .TH strtol 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtol, strtoll, strtoq \- convert a string to a long integer
> +strtol, strtoll, strtoq \- string to long int
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtoul.3 b/man/man3/strtoul.3
> index a6f6fd4a8ada..670fcb3868a1 100644
> --- a/man/man3/strtoul.3
> +++ b/man/man3/strtoul.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtoul 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtoul, strtoull, strtouq \- convert a string to an unsigned long integer
> +strtoul, strtoull, strtouq \- string to unsigned long int
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strverscmp.3 b/man/man3/strverscmp.3
> index d072471b1431..caadb9717a3b 100644
> --- a/man/man3/strverscmp.3
> +++ b/man/man3/strverscmp.3
> @@ -7,7 +7,7 @@
> .\"
> .TH strverscmp 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strverscmp \- compare two version strings
> +strverscmp \- strings version compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strxfrm.3 b/man/man3/strxfrm.3
> index d4ac57006eb0..a892bf4c813b 100644
> --- a/man/man3/strxfrm.3
> +++ b/man/man3/strxfrm.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strxfrm 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strxfrm \- string transformation
> +strxfrm \- string transform
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> --
> 2.53.0
>
The text after the \- is documented as a "summary-description", and
many users and tools (including man -k, apropos, and whatis) rely on
this. Repeating the function name in a slightly more expanded form is
not only contrary to what users expect, but is much less useful than a
proper summary description. In most cases the new text does not
provide the summary that users expect from tools like man -k, is
inconsistent with other summaries that might be shown by man -k, and
is not even grammatically correct, making it needlessly difficult to
interpret as a summary or contrast with other summaries to determine
which of several matching functions is desired.
If an expanded form of the function name is useful, it could be added
to the NOTES section.
- Mark
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names
2026-07-03 5:03 ` [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Mark Harris
@ 2026-07-03 11:16 ` Alejandro Colomar
0 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-03 11:16 UTC (permalink / raw)
To: Mark Harris; +Cc: Douglas McIlroy, linux-man, Serge E. Hallyn
[-- Attachment #1: Type: text/plain, Size: 8472 bytes --]
[CC += Doug]
Hi Mark,
Thanks for the feedback! See below for comments.
On 2026-07-02T22:03:05-0700, Mark Harris wrote:
> Alejandro Colomar wrote:
> >
> > Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
> > Signed-off-by: Alejandro Colomar <alx@kernel.org>
> > ---
> >
> > Hi Serge,
> >
> > I finally took some time to do this that you asked me to do some time
> > ago. I've edited all the str*(3) manual pages so that the NAME section
> > actually explains the name of the function.
> >
> > There's an exception: strpbrk(3). I refuse to explain that name. I've
> > explained it as if it were called strchrs(), which is a name that Plan9
> > uses for that function, and which would have been a better name for it.
> > (See patch 2/2).
> >
> >
> > Have a lovely day!
> > Alex
> >
> > man/man3/strcasecmp.3 | 2 +-
> > man/man3/strchr.3 | 2 +-
> > man/man3/strcmp.3 | 2 +-
> > man/man3/strcoll.3 | 2 +-
> > man/man3/strcpy.3 | 2 +-
> > man/man3/strdup.3 | 2 +-
> > man/man3/strerror.3 | 5 +++--
> > man/man3/strfmon.3 | 2 +-
> > man/man3/strfromd.3 | 3 +--
> > man/man3/strfry.3 | 2 +-
> > man/man3/strftime.3 | 2 +-
> > man/man3/string.3 | 5 +----
> > man/man3/strlen.3 | 2 +-
> > man/man3/strncat.3 | 5 +----
> > man/man3/strnlen.3 | 2 +-
> > man/man3/strpbrk.3 | 2 +-
> > man/man3/strptime.3 | 2 +-
> > man/man3/strsep.3 | 2 +-
> > man/man3/strsignal.3 | 2 +-
> > man/man3/strspn.3 | 2 +-
> > man/man3/strtod.3 | 2 +-
> > man/man3/strtoimax.3 | 2 +-
> > man/man3/strtok.3 | 2 +-
> > man/man3/strtol.3 | 2 +-
> > man/man3/strtoul.3 | 2 +-
> > man/man3/strverscmp.3 | 2 +-
> > man/man3/strxfrm.3 | 2 +-
> > 27 files changed, 29 insertions(+), 35 deletions(-)
> >
> > diff --git a/man/man3/strcasecmp.3 b/man/man3/strcasecmp.3
> > index aa554341da4c..65941ddf6524 100644
> > --- a/man/man3/strcasecmp.3
> > +++ b/man/man3/strcasecmp.3
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strcasecmp 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strcasecmp, strncasecmp \- compare two strings ignoring case
> > +strcasecmp, strncasecmp \- strings case-insensitive compare
> > .SH LIBRARY
> > Standard C library
> > .RI ( libc ,\~ \-lc )
[...]
> > diff --git a/man/man3/strspn.3 b/man/man3/strspn.3
> > index fe4414317df6..7c85d209c59d 100644
> > --- a/man/man3/strspn.3
> > +++ b/man/man3/strspn.3
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strspn 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strspn, strcspn \- get length of a prefix substring
> > +strspn, strcspn \- string [complementary] span
> > .SH LIBRARY
> > Standard C library
> > .RI ( libc ,\~ \-lc )
[...]
> > diff --git a/man/man3/strxfrm.3 b/man/man3/strxfrm.3
> > index d4ac57006eb0..a892bf4c813b 100644
> > --- a/man/man3/strxfrm.3
> > +++ b/man/man3/strxfrm.3
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strxfrm 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strxfrm \- string transformation
> > +strxfrm \- string transform
> > .SH LIBRARY
> > Standard C library
> > .RI ( libc ,\~ \-lc )
> > --
> > 2.53.0
> >
>
> The text after the \- is documented as a "summary-description", and
> many users and tools (including man -k, apropos, and whatis) rely on
> this.
I thought precisely about whatis(1) when doing this. Here's what
whatis(1) shows after this change (and a similar one for mem*(3)):
$ find man/man3/ -type f \
| grep -e/mem -e/str \
| xargs basename -s.3 \
| xargs whatis \
| grep '(3)' \
| sort;
memalign (3) - memory aligned allocate
memccpy (3) - memory until-and-including-character copy
memchr (3) - memory search character
memcmp (3) - memory compare
memcpy (3) - memory copy
memeq (3) - memory equal
memfrob (3) - memory frobnicate (obfuscate)
memmem (3) - memory search submemory
memmove (3) - memory move
mempcpy (3) - memory return-offset-pointer copy
memrchr (3) - memory rear-search character
memset (3) - memory set
strcasecmp (3) - strings case-insensitive compare
strcasestr (3) - string case-insensitive search substring
strcat (3) - string copy/catenate
strchr (3) - string search character
strchrnul (3) - string search character or NUL
strcmp (3) - strings compare
strcoll (3) - strings collate
strcpy (3) - string copy/catenate
strcspn (3) - string [complementary] span
strdup (3) - string duplicate
strdupa (3) - string duplicate using alloca
streq (3) - strings equal
strerror (3) - string error-string
strerror_l (3) - string error-string
strerror_r (3) - string error-string
strerrordesc_np (3) - string error-string
strerrorname_np (3) - string error-string
strfmon (3) - string format monetary value
strfmon_l (3) - string format monetary value
strfromd (3) - string from float
strfromf (3) - string from float
strfroml (3) - string from float
strfry (3) - string fry (obfuscate)
strftime (3) - string format date and time
strftime_l (3) - string format date and time
string (3) - byte operations
strlen (3) - string length
strncasecmp (3) - strings case-insensitive compare
strncat (3) - nonstring catenate
strncmp (3) - strings compare
strncpy (3) - fill a fixed-size buffer with non-null bytes from a st...
strndup (3) - string duplicate
strndupa (3) - string duplicate using alloca
strnlen (3) - nonstring length
strnul (3) - string search NUL
strpbrk (3) - string search characters
strptime (3) - string parse time
strrchr (3) - string rear-search character
strsep (3) - string separate
strsignal (3) - string signal description
strspn (3) - string [complementary] span
strstr (3) - string search substring
strtod (3) - string to float
strtof (3) - string to float
strtoimax (3) - string to [u]intmax_t
strtok (3) - string tokenize
strtok_r (3) - string tokenize reentrant
strtol (3) - string to long int
strtold (3) - string to float
strtoll (3) - string to long int
strtoq (3) - string to long int
strtoul (3) - string to unsigned long int
strtoull (3) - string to unsigned long int
strtoumax (3) - string to [u]intmax_t
strtouq (3) - string to unsigned long int
strverscmp (3) - strings version compare
strxfrm (3) - string transform
As an example of how I think this is useful, I learnt the difference
between strftime(3) and strptime(3) while doing this change. The
ethymology of the name was the key to understanding it. strftime(3) is
for _formatting_ a string, while strptime(3) is for _parsing_ from a
string; I'll never forget that. Before, I knew one of them was for
reading from a string, and the other for writing to a string, but never
remembered which is which, so I had to consult the page every time.
> Repeating the function name in a slightly more expanded form is
> not only contrary to what users expect, but is much less useful than a
> proper summary description. In most cases the new text does not
> provide the summary that users expect from tools like man -k, is
> inconsistent with other summaries that might be shown by man -k, and
> is not even grammatically correct, making it needlessly difficult to
> interpret as a summary or contrast with other summaries to determine
> which of several matching functions is desired.
I agree I wasn't entirely happy about it not being gramatically correct,
but though that maybe it's not necessary to be so.
> If an expanded form of the function name is useful, it could be added
> to the NOTES section.
I've CCd Doug McIlroy. I'm interested in his opinion before deciding
what to do. Anyone else reading this, please give opinions too. I'm
still undecided.
Have a lovely day!
Alex
>
>
>
> - Mark
>
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 0/4] str*.3, mem*.3: NAME: Explain the names
2026-07-02 12:33 [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Alejandro Colomar
2026-07-02 12:33 ` [PATCH v1 2/2] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
2026-07-03 5:03 ` [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Mark Harris
@ 2026-07-08 15:09 ` Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 1/4] man/man3/str*.3: " Alejandro Colomar
` (3 more replies)
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
3 siblings, 4 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-08 15:09 UTC (permalink / raw)
To: linux-man; +Cc: Alejandro Colomar
[-- Attachment #1: Type: text/plain, Size: 3387 bytes --]
Hi!
Here's v2 of this patch set. Changes:
- Use a better description of strcoll(3), as suggested by Serge.
- Rewrite string(3).
- Make bstring(3) a link page linking to string(3).
- Also cover mem*() functions in this patch set.
See range-diff below.
Have a lovely day!
Alex
Alejandro Colomar (4):
man/man3/str*.3: NAME: Explain the names
man/man3/strpbrk.3: BUGS: Clarify the NAME
man/man3/mem*.3: NAME: Explain the names
man/man3/[b]string.3: Rewrite and merge
man/man3/bstring.3 | 81 +-------------
man/man3/memalign.3 | 4 +-
man/man3/memccpy.3 | 2 +-
man/man3/memchr.3 | 2 +-
man/man3/memcmp.3 | 2 +-
man/man3/memcpy.3 | 2 +-
man/man3/memfrob.3 | 2 +-
man/man3/memmem.3 | 2 +-
man/man3/memmove.3 | 2 +-
man/man3/mempcpy.3 | 2 +-
man/man3/memset.3 | 2 +-
man/man3/strcasecmp.3 | 2 +-
man/man3/strchr.3 | 2 +-
man/man3/strcmp.3 | 2 +-
man/man3/strcoll.3 | 2 +-
man/man3/strcpy.3 | 2 +-
man/man3/strdup.3 | 2 +-
man/man3/strerror.3 | 5 +-
man/man3/strfmon.3 | 2 +-
man/man3/strfromd.3 | 3 +-
man/man3/strfry.3 | 2 +-
man/man3/strftime.3 | 2 +-
man/man3/string.3 | 241 ++++++++++++++++++++++++------------------
man/man3/strlen.3 | 2 +-
man/man3/strncat.3 | 5 +-
man/man3/strnlen.3 | 2 +-
man/man3/strpbrk.3 | 6 +-
man/man3/strptime.3 | 2 +-
man/man3/strsep.3 | 2 +-
man/man3/strsignal.3 | 2 +-
man/man3/strspn.3 | 2 +-
man/man3/strtod.3 | 2 +-
man/man3/strtoimax.3 | 2 +-
man/man3/strtok.3 | 2 +-
man/man3/strtol.3 | 2 +-
man/man3/strtoul.3 | 2 +-
man/man3/strverscmp.3 | 2 +-
man/man3/strxfrm.3 | 2 +-
38 files changed, 179 insertions(+), 228 deletions(-)
Range-diff against v1:
1: e1446fbce9d7 ! 1: b17a1548f3ab man/man3/str*.3: NAME: Explain the names
@@ Commit message
man/man3/str*.3: NAME: Explain the names
Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
+ Cc: Mark Harris <mark.hsj@gmail.com>
+ Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
+ Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
## man/man3/strcasecmp.3 ##
@@ man/man3/strcoll.3
.TH strcoll 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcoll \- compare two strings using the current locale
-+strcoll \- strings collate
++strcoll \- strings LC_COLLATE compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
2: a1c4e794224f ! 2: a7508ac0f477 man/man3/strpbrk.3: BUGS: Clarify the NAME
@@ Metadata
## Commit message ##
man/man3/strpbrk.3: BUGS: Clarify the NAME
+ Cc: "Serge E. Hallyn" <serge@hallyn.com>
+ Cc: Mark Harris <mark.hsj@gmail.com>
+ Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
+ Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
## man/man3/strfry.3 ##
-: ------------ > 3: dcc7a485cbe0 man/man3/mem*.3: NAME: Explain the names
-: ------------ > 4: 89d3fbe27c06 man/man3/[b]string.3: Rewrite and merge
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names
2026-07-08 15:09 ` [PATCH v2 0/4] str*.3, mem*.3: " Alejandro Colomar
@ 2026-07-08 15:09 ` Alejandro Colomar
2026-07-24 18:52 ` sergeh
2026-07-08 15:09 ` [PATCH v2 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
` (2 subsequent siblings)
3 siblings, 1 reply; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-08 15:09 UTC (permalink / raw)
To: linux-man
Cc: Alejandro Colomar, Serge E. Hallyn, Mark Harris,
G. Branden Robinson, Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 11962 bytes --]
Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/strcasecmp.3 | 2 +-
man/man3/strchr.3 | 2 +-
man/man3/strcmp.3 | 2 +-
man/man3/strcoll.3 | 2 +-
man/man3/strcpy.3 | 2 +-
man/man3/strdup.3 | 2 +-
man/man3/strerror.3 | 5 +++--
man/man3/strfmon.3 | 2 +-
man/man3/strfromd.3 | 3 +--
man/man3/strfry.3 | 2 +-
man/man3/strftime.3 | 2 +-
man/man3/string.3 | 5 +----
man/man3/strlen.3 | 2 +-
man/man3/strncat.3 | 5 +----
man/man3/strnlen.3 | 2 +-
man/man3/strpbrk.3 | 2 +-
man/man3/strptime.3 | 2 +-
man/man3/strsep.3 | 2 +-
man/man3/strsignal.3 | 2 +-
man/man3/strspn.3 | 2 +-
man/man3/strtod.3 | 2 +-
man/man3/strtoimax.3 | 2 +-
man/man3/strtok.3 | 2 +-
man/man3/strtol.3 | 2 +-
man/man3/strtoul.3 | 2 +-
man/man3/strverscmp.3 | 2 +-
man/man3/strxfrm.3 | 2 +-
27 files changed, 29 insertions(+), 35 deletions(-)
diff --git a/man/man3/strcasecmp.3 b/man/man3/strcasecmp.3
index aa554341da4c..65941ddf6524 100644
--- a/man/man3/strcasecmp.3
+++ b/man/man3/strcasecmp.3
@@ -6,7 +6,7 @@
.\"
.TH strcasecmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcasecmp, strncasecmp \- compare two strings ignoring case
+strcasecmp, strncasecmp \- strings case-insensitive compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strchr.3 b/man/man3/strchr.3
index 4e6a96d8cde6..12d08f63ddbb 100644
--- a/man/man3/strchr.3
+++ b/man/man3/strchr.3
@@ -6,7 +6,7 @@
.\"
.TH strchr 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strchr \- locate character in string
+strchr \- string search character
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcmp.3 b/man/man3/strcmp.3
index a088412917d2..0a573e52d936 100644
--- a/man/man3/strcmp.3
+++ b/man/man3/strcmp.3
@@ -7,7 +7,7 @@
.\"
.TH strcmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcmp, strncmp \- compare two strings
+strcmp, strncmp \- strings compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcoll.3 b/man/man3/strcoll.3
index 32d620f52c51..1f9eb2441b13 100644
--- a/man/man3/strcoll.3
+++ b/man/man3/strcoll.3
@@ -6,7 +6,7 @@
.\"
.TH strcoll 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcoll \- compare two strings using the current locale
+strcoll \- strings LC_COLLATE compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcpy.3 b/man/man3/strcpy.3
index cea24ff04020..d4d0e4246545 100644
--- a/man/man3/strcpy.3
+++ b/man/man3/strcpy.3
@@ -5,7 +5,7 @@
.\"
.TH strcpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcpy, strcat \- copy or catenate a string
+strcpy, strcat \- string copy/catenate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strdup.3 b/man/man3/strdup.3
index b4026ed41b4a..05671cd936c2 100644
--- a/man/man3/strdup.3
+++ b/man/man3/strdup.3
@@ -6,7 +6,7 @@
.\"
.TH strdup 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strdup, strndup \- duplicate a string
+strdup, strndup \- string duplicate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strerror.3 b/man/man3/strerror.3
index e3306c7e1608..637d1d812d52 100644
--- a/man/man3/strerror.3
+++ b/man/man3/strerror.3
@@ -7,8 +7,9 @@
.\"
.TH strerror 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l \-
-return string describing error number
+strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l
+\-
+string error-string
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfmon.3 b/man/man3/strfmon.3
index e0d9790395cb..dee915c8a9f4 100644
--- a/man/man3/strfmon.3
+++ b/man/man3/strfmon.3
@@ -6,7 +6,7 @@
.\"
.TH strfmon 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfmon, strfmon_l \- convert monetary value to a string
+strfmon, strfmon_l \- string format monetary value
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfromd.3 b/man/man3/strfromd.3
index c221f82df0a8..fa8a10feec01 100644
--- a/man/man3/strfromd.3
+++ b/man/man3/strfromd.3
@@ -6,8 +6,7 @@
.\"
.TH strfromd 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfromd, strfromf, strfroml \- convert a floating-point value into
-a string
+strfromd, strfromf, strfroml \- string from float
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
index 99917e4a6261..54ea313ea262 100644
--- a/man/man3/strfry.3
+++ b/man/man3/strfry.3
@@ -6,7 +6,7 @@
.\"
.TH strfry 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfry \- randomize a string
+strfry \- string fry
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strftime.3 b/man/man3/strftime.3
index 66351f6bd43a..bdba4089b934 100644
--- a/man/man3/strftime.3
+++ b/man/man3/strftime.3
@@ -6,7 +6,7 @@
.\"
.TH strftime 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strftime \- format date and time
+strftime \- string format date and time
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/string.3 b/man/man3/string.3
index 89fe51fba609..2141822e1fd1 100644
--- a/man/man3/string.3
+++ b/man/man3/string.3
@@ -5,10 +5,7 @@
.\"
.TH string 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-stpcpy, strcasecmp, strcat, strchr, strcmp, strcoll, strcpy, strcspn,
-strdup, strfry, strlen, strncat, strncmp, strncpy, strncasecmp, strpbrk,
-strrchr, strsep, strspn, strstr, strtok, strxfrm
-\- string operations
+string \- string operations
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strlen.3 b/man/man3/strlen.3
index 1dd99bd8e250..639bdd6ac2d2 100644
--- a/man/man3/strlen.3
+++ b/man/man3/strlen.3
@@ -6,7 +6,7 @@
.\"
.TH strlen 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strlen \- calculate the length of a string
+strlen \- string length
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strncat.3 b/man/man3/strncat.3
index 4b0217b02673..dd39d54939d9 100644
--- a/man/man3/strncat.3
+++ b/man/man3/strncat.3
@@ -5,10 +5,7 @@
.\"
.TH strncat 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strncat
-\-
-append non-null bytes from a source array to a string,
-and null-terminate the result
+strncat \- nonstring catenate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strnlen.3 b/man/man3/strnlen.3
index fbd719ee4888..553a39a9ec7a 100644
--- a/man/man3/strnlen.3
+++ b/man/man3/strnlen.3
@@ -6,7 +6,7 @@
.\"
.TH strnlen 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strnlen \- determine the length of a fixed-size string
+strnlen \- nonstring length
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
index 1f56cf18dbac..6e5b79a48bbe 100644
--- a/man/man3/strpbrk.3
+++ b/man/man3/strpbrk.3
@@ -6,7 +6,7 @@
.\"
.TH strpbrk 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strpbrk \- search a string for any of a set of bytes
+strpbrk \- string search characters
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strptime.3 b/man/man3/strptime.3
index 979bffea7bf8..4502acfe6d1e 100644
--- a/man/man3/strptime.3
+++ b/man/man3/strptime.3
@@ -6,7 +6,7 @@
.\"
.TH strptime 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strptime \- convert a string representation of time to a time tm structure
+strptime \- string parse time
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strsep.3 b/man/man3/strsep.3
index 46ba6c5819bd..98859f64909f 100644
--- a/man/man3/strsep.3
+++ b/man/man3/strsep.3
@@ -6,7 +6,7 @@
.\"
.TH strsep 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strsep \- extract token from string
+strsep \- string separate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strsignal.3 b/man/man3/strsignal.3
index 4a4e8c86e71d..964a5e5aacaf 100644
--- a/man/man3/strsignal.3
+++ b/man/man3/strsignal.3
@@ -8,7 +8,7 @@
.TH strsignal 3 (date) "Linux man-pages (unreleased)"
.SH NAME
strsignal, sigabbrev_np, sigdescr_np, sys_siglist \-
-return string describing signal
+string signal description
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strspn.3 b/man/man3/strspn.3
index fe4414317df6..7c85d209c59d 100644
--- a/man/man3/strspn.3
+++ b/man/man3/strspn.3
@@ -6,7 +6,7 @@
.\"
.TH strspn 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strspn, strcspn \- get length of a prefix substring
+strspn, strcspn \- string [complementary] span
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtod.3 b/man/man3/strtod.3
index 15147ee51006..84a9934ba267 100644
--- a/man/man3/strtod.3
+++ b/man/man3/strtod.3
@@ -6,7 +6,7 @@
.\"
.TH strtod 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtod, strtof, strtold \- convert ASCII string to floating-point number
+strtod, strtof, strtold \- string to float
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtoimax.3 b/man/man3/strtoimax.3
index 71267754ed7a..08413a9f8101 100644
--- a/man/man3/strtoimax.3
+++ b/man/man3/strtoimax.3
@@ -6,7 +6,7 @@
.\"
.TH strtoimax 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtoimax, strtoumax \- convert string to integer
+strtoimax, strtoumax \- string to [u]intmax_t
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtok.3 b/man/man3/strtok.3
index 2d733e886086..4ce708ecbcb4 100644
--- a/man/man3/strtok.3
+++ b/man/man3/strtok.3
@@ -6,7 +6,7 @@
.\"
.TH strtok 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtok \- extract tokens from strings
+strtok \- string tokenize
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtol.3 b/man/man3/strtol.3
index f88b717780f3..4d9c49eaf28a 100644
--- a/man/man3/strtol.3
+++ b/man/man3/strtol.3
@@ -7,7 +7,7 @@
.\"
.TH strtol 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtol, strtoll, strtoq \- convert a string to a long integer
+strtol, strtoll, strtoq \- string to long int
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtoul.3 b/man/man3/strtoul.3
index a6f6fd4a8ada..670fcb3868a1 100644
--- a/man/man3/strtoul.3
+++ b/man/man3/strtoul.3
@@ -6,7 +6,7 @@
.\"
.TH strtoul 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtoul, strtoull, strtouq \- convert a string to an unsigned long integer
+strtoul, strtoull, strtouq \- string to unsigned long int
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strverscmp.3 b/man/man3/strverscmp.3
index d072471b1431..caadb9717a3b 100644
--- a/man/man3/strverscmp.3
+++ b/man/man3/strverscmp.3
@@ -7,7 +7,7 @@
.\"
.TH strverscmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strverscmp \- compare two version strings
+strverscmp \- strings version compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strxfrm.3 b/man/man3/strxfrm.3
index d4ac57006eb0..a892bf4c813b 100644
--- a/man/man3/strxfrm.3
+++ b/man/man3/strxfrm.3
@@ -6,7 +6,7 @@
.\"
.TH strxfrm 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strxfrm \- string transformation
+strxfrm \- string transform
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v2 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME
2026-07-08 15:09 ` [PATCH v2 0/4] str*.3, mem*.3: " Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 1/4] man/man3/str*.3: " Alejandro Colomar
@ 2026-07-08 15:09 ` Alejandro Colomar
2026-07-24 18:53 ` sergeh
2026-07-08 15:09 ` [PATCH v2 3/4] man/man3/mem*.3: NAME: Explain the names Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 4/4] man/man3/[b]string.3: Rewrite and merge Alejandro Colomar
3 siblings, 1 reply; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-08 15:09 UTC (permalink / raw)
To: linux-man
Cc: Alejandro Colomar, Serge E. Hallyn, Mark Harris,
G. Branden Robinson, Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 1147 bytes --]
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/strfry.3 | 2 +-
man/man3/strpbrk.3 | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
index 54ea313ea262..a65603af918a 100644
--- a/man/man3/strfry.3
+++ b/man/man3/strfry.3
@@ -6,7 +6,7 @@
.\"
.TH strfry 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfry \- string fry
+strfry \- string fry (obfuscate)
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
index 6e5b79a48bbe..ee7a84f1a944 100644
--- a/man/man3/strpbrk.3
+++ b/man/man3/strpbrk.3
@@ -51,6 +51,10 @@ .SH STANDARDS
C11, POSIX.1-2008.
.SH HISTORY
POSIX.1-2001, C89, SVr4, 4.3BSD.
+.SH BUGS
+The name of this function is bad.
+It should have been named strchrs().
+The NAME section above reflects this.
.SH SEE ALSO
.BR memchr (3),
.BR strchr (3),
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v2 3/4] man/man3/mem*.3: NAME: Explain the names
2026-07-08 15:09 ` [PATCH v2 0/4] str*.3, mem*.3: " Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 1/4] man/man3/str*.3: " Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
@ 2026-07-08 15:09 ` Alejandro Colomar
2026-07-24 18:55 ` sergeh
2026-07-08 15:09 ` [PATCH v2 4/4] man/man3/[b]string.3: Rewrite and merge Alejandro Colomar
3 siblings, 1 reply; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-08 15:09 UTC (permalink / raw)
To: linux-man
Cc: Alejandro Colomar, Serge E. Hallyn, Mark Harris,
G. Branden Robinson, Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 4275 bytes --]
Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/memalign.3 | 4 +---
man/man3/memccpy.3 | 2 +-
man/man3/memchr.3 | 2 +-
man/man3/memcmp.3 | 2 +-
man/man3/memcpy.3 | 2 +-
man/man3/memfrob.3 | 2 +-
man/man3/memmem.3 | 2 +-
man/man3/memmove.3 | 2 +-
man/man3/mempcpy.3 | 2 +-
man/man3/memset.3 | 2 +-
10 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/man/man3/memalign.3 b/man/man3/memalign.3
index 12e8a5bb0c33..5954693e3d30 100644
--- a/man/man3/memalign.3
+++ b/man/man3/memalign.3
@@ -5,9 +5,7 @@
.\"
.TH memalign 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memalign
-\-
-allocate aligned memory
+memalign \- memory aligned allocate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memccpy.3 b/man/man3/memccpy.3
index d21e7d014af6..706f8152f58c 100644
--- a/man/man3/memccpy.3
+++ b/man/man3/memccpy.3
@@ -6,7 +6,7 @@
.\"
.TH memccpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memccpy \- copy memory area
+memccpy \- memory until-and-including-character copy
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memchr.3 b/man/man3/memchr.3
index 40ea45fdfb83..0a493ff4029d 100644
--- a/man/man3/memchr.3
+++ b/man/man3/memchr.3
@@ -6,7 +6,7 @@
.\"
.TH memchr 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memchr \- scan memory for a character
+memchr \- memory search character
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memcmp.3 b/man/man3/memcmp.3
index 815bd10252fe..9cecb282ba4f 100644
--- a/man/man3/memcmp.3
+++ b/man/man3/memcmp.3
@@ -6,7 +6,7 @@
.\"
.TH memcmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memcmp \- compare memory areas
+memcmp \- memory compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memcpy.3 b/man/man3/memcpy.3
index c94c501b0374..f8f049bf5c4c 100644
--- a/man/man3/memcpy.3
+++ b/man/man3/memcpy.3
@@ -7,7 +7,7 @@
.\"
.TH memcpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memcpy \- copy memory area
+memcpy \- memory copy
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memfrob.3 b/man/man3/memfrob.3
index 90534b749c4c..ee41da0ced7b 100644
--- a/man/man3/memfrob.3
+++ b/man/man3/memfrob.3
@@ -6,7 +6,7 @@
.\"
.TH memfrob 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memfrob \- frobnicate (obfuscate) a memory area
+memfrob \- memory frobnicate (obfuscate)
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memmem.3 b/man/man3/memmem.3
index 0224a130537e..13fd8b7cea7b 100644
--- a/man/man3/memmem.3
+++ b/man/man3/memmem.3
@@ -6,7 +6,7 @@
.\"
.TH memmem 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memmem \- locate a substring
+memmem \- memory search submemory
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memmove.3 b/man/man3/memmove.3
index c549c6145176..99f152412142 100644
--- a/man/man3/memmove.3
+++ b/man/man3/memmove.3
@@ -6,7 +6,7 @@
.\"
.TH memmove 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memmove \- copy memory area
+memmove \- memory move
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/mempcpy.3 b/man/man3/mempcpy.3
index d5ae5812758e..012e6326dd00 100644
--- a/man/man3/mempcpy.3
+++ b/man/man3/mempcpy.3
@@ -7,7 +7,7 @@
.\"
.TH mempcpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-mempcpy, wmempcpy \- copy memory area
+mempcpy, wmempcpy \- memory return-offset-pointer copy
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memset.3 b/man/man3/memset.3
index 370ac64fdf25..91b3b476abf9 100644
--- a/man/man3/memset.3
+++ b/man/man3/memset.3
@@ -6,7 +6,7 @@
.\"
.TH memset 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memset \- fill memory with a constant byte
+memset \- memory set
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v2 4/4] man/man3/[b]string.3: Rewrite and merge
2026-07-08 15:09 ` [PATCH v2 0/4] str*.3, mem*.3: " Alejandro Colomar
` (2 preceding siblings ...)
2026-07-08 15:09 ` [PATCH v2 3/4] man/man3/mem*.3: NAME: Explain the names Alejandro Colomar
@ 2026-07-08 15:09 ` Alejandro Colomar
3 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-08 15:09 UTC (permalink / raw)
To: linux-man
Cc: Alejandro Colomar, Serge E. Hallyn, Mark Harris,
G. Branden Robinson, Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 7542 bytes --]
Let's have a single page with an organized list of functions.
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/bstring.3 | 81 +--------------
man/man3/string.3 | 238 ++++++++++++++++++++++++++-------------------
2 files changed, 137 insertions(+), 182 deletions(-)
diff --git a/man/man3/bstring.3 b/man/man3/bstring.3
index c3b5782d3554..f02a9f29817b 100644
--- a/man/man3/bstring.3
+++ b/man/man3/bstring.3
@@ -1,80 +1 @@
-.\" Copyright 1993, David Metcalfe <david@prism.demon.co.uk>
-.\" Copyright, the authors of the Linux man-pages project
-.\"
-.\" SPDX-License-Identifier: Linux-man-pages-copyleft
-.\"
-.TH bstring 3 (date) "Linux man-pages (unreleased)"
-.SH NAME
-bcmp, bcopy, bzero, memccpy, memchr, memcmp, memcpy, memfrob, memmem,
-memmove, memset \- byte string operations
-.SH LIBRARY
-Standard C library
-.RI ( libc ,\~ \-lc )
-.SH SYNOPSIS
-.nf
-.B #include <string.h>
-.P
-.BR "int bcmp(" "size_t n;"
-.BI " const void " s1 [ n "], const void " s2 [ n "], size_t " n );
-.P
-.BR "void bcopy(" "size_t n;"
-.BI " const void " src [ n "], void " dest [ n "], size_t " n );
-.P
-.BR "void bzero(" "size_t n;"
-.BI " void " s [ n "], size_t " n );
-.P
-.BR "void *memccpy(" "size_t n;"
-.BI " void " dest [ n "], const void " src [ n "], int " c ", size_t " n );
-.P
-.BR "void *memchr(" "size_t n;"
-.BI " const void " s [ n "], int " c ", size_t " n );
-.P
-.BR "int memcmp(" "size_t n;"
-.BI " const void " s1 [ n "], const void " s2 [ n "], size_t " n );
-.P
-.BR "void *memcpy(" "size_t n;"
-.BI " void " dest [ n "], const void " src [ n "], size_t " n );
-.P
-.BR "void *memfrob(" "size_t n;"
-.BI " void " s [ n "], size_t " n );
-.P
-.BR "void *memmem(" "size_t hsize, size_t nsize;"
-.BI " const void " haystack [ hsize "], size_t " hsize ,
-.BI " const void " needle [ nsize "], size_t " nsize );
-.P
-.BR "void *memmove(" "size_t n;"
-.BI " void " dest [ n "], const void " src [ n "], size_t " n );
-.P
-.BR "void *memset(" "size_t n;"
-.BI " void " s [ n "], int " c ", size_t " n );
-.fi
-.SH DESCRIPTION
-The byte string functions perform operations on strings (byte arrays)
-that are not necessarily null-terminated.
-See the individual man pages
-for descriptions of each function.
-.SH NOTES
-The functions
-.BR bcmp ()
-and
-.BR bcopy ()
-are obsolete.
-Use
-.BR memcmp ()
-and
-.BR memmove ()
-instead.
-.\" The old functions are not even available on some non-GNU+Linux systems.
-.SH SEE ALSO
-.BR bcmp (3),
-.BR bcopy (3),
-.BR bzero (3),
-.BR memccpy (3),
-.BR memchr (3),
-.BR memcmp (3),
-.BR memcpy (3),
-.BR memfrob (3),
-.BR memmem (3),
-.BR memmove (3),
-.BR memset (3),
-.BR string (3)
+.so man3/string.3
diff --git a/man/man3/string.3 b/man/man3/string.3
index 2141822e1fd1..da87799a5e69 100644
--- a/man/man3/string.3
+++ b/man/man3/string.3
@@ -5,7 +5,7 @@
.\"
.TH string 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-string \- string operations
+string \- byte operations
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
@@ -15,115 +15,149 @@ .SH SYNOPSIS
.B #include <strings.h>
.fi
.SH DESCRIPTION
-The string functions perform operations on null-terminated
-strings.
-See the individual man pages for descriptions of each function.
+.SS Write
.TP
-.BR strcasecmp (3)
-Compare two strings,
-ignoring case.
+Write bytes
+.RS
.TP
-.BR strncasecmp (3)
-Compare the first bytes of two strings,
-ignoring case.
+.BR bzero (3)
+.TQ
+.BR memset (3)
+.RE
+.SS Copy
.TP
-.BR stpcpy (3)
-Copy a string,
-returning a pointer to the end of the resulting string.
+Copy bytes
+.RS
.TP
-.BR strcat (3)
-Append a string into an existing string.
+.BR memmove (3)
+.TQ
+.BR memcpy (3)
+.TQ
+.BR mempcpy (3)
+.TQ
+.BR memccpy (3)
+.RE
.TP
-.BR strchr (3)
-Find the first occurrence of a character
-in a string.
-.TP
-.BR strcmp (3)
-Compare two strings.
-.TP
-.BR strcoll (3)
-Compare two strings,
-using the current locale.
+Copy strings
+.RS
.TP
.BR strcpy (3)
-Copy a string.
+.TQ
+.BR stpcpy (3)
+.RE
.TP
-.BR strcspn (3)
-Calculate the length of the initial segment of a string
-which does not contain any of the rejected bytes.
-.TP
-.BR strdup (3)
-Duplicate a string
-in memory allocated using
-.BR malloc (3).
-.TP
-.BR strfry (3)
-Randomly swap the characters in a string.
-.TP
-.BR strlen (3)
-Return the length of a string.
-.TP
-.BR strncat (3)
-Append non-null bytes from an array
-to a string,
-and null-terminate the result.
-.TP
-.BR strncmp (3)
-Compare the first bytes of two strings.
-.TP
-.BR strpbrk (3)
-Find the first occurrence in a string
-of one of the bytes in the accepted bytes.
-.TP
-.BR strrchr (3)
-Find the last occurrence of a character
-in a string.
-.TP
-.BR strsep (3)
-Extract the initial field in a string
-that is delimited by one of the delimiter bytes.
-.TP
-.BR strspn (3)
-Calculate the length of the initial segment of a string
-that consists entirely of accepted bytes.
-.TP
-.BR strstr (3)
-Find the first occurrence of a substring
-in a string.
-.TP
-.BR strtok (3)
-Extract tokens from a string
-that are delimited by one of the delimiter bytes.
-.TP
-.BR strxfrm (3)
-Transforms a string
-to the current locale
-and copies the first bytes to a buffer.
+Copy nonstrings
+.RS
.TP
.BR strncpy (3)
-Fill a fixed-size buffer with leading non-null bytes from a source array,
-padding with null bytes as needed.
-.SH SEE ALSO
-.BR bstring (3),
-.BR stpcpy (3),
-.BR strcasecmp (3),
-.BR strcat (3),
-.BR strchr (3),
-.BR strcmp (3),
-.BR strcoll (3),
-.BR strcpy (3),
-.BR strcspn (3),
-.BR strdup (3),
-.BR strfry (3),
-.BR strlen (3),
-.BR strncasecmp (3),
-.BR strncat (3),
-.BR strncmp (3),
-.BR strncpy (3),
-.BR strpbrk (3),
-.BR strrchr (3),
-.BR strsep (3),
-.BR strspn (3),
-.BR strstr (3),
-.BR strtok (3),
+.RE
+.SS Catenate
+.TP
+Catenate strings
+.RS
+.TP
+.BR strcat (3)
+.RE
+.TP
+Catenate nonstrings
+.RS
+.TP
+.BR strncat (3)
+.RE
+.SS Duplicate
+.TP
+Duplicate strings
+.RS
+.TP
+.BR strdup (3)
+.TQ
+.BR strdupa (3)
+.RE
+.TP
+Duplicate nonstrings
+.RS
+.TP
+.BR strndup (3)
+.TQ
+.BR strndupa (3)
+.RE
+.SS Compare
+.TP
+Compare bytes
+.RS
+.TP
+.BR memeq (3)
+.TQ
+.BR memcmp (3)
+.RE
+.TP
+Compare strings
+.RS
+.TP
+.BR streq (3)
+.TQ
+.BR strcmp (3)
+.TQ
+.BR strcasecmp (3)
+.TQ
+.BR strcoll (3)
+.TQ
+.BR strverscmp (3)
+.RE
+.TP
+Compare nonstrings
+.RS
+.TP
+.BR strncmp (3)
+.TQ
+.BR strncasecmp (3)
+.RE
+.SS Search
+.TP
+Search bytes
+.RS
+.TP
+.BR memchr (3)
+.TQ
+.BR memrchr (3)
+.TQ
+.BR memmem (3)
+.RE
+.TP
+Search strings
+.RS
+.TP
+.BR strlen (3)
+.TQ
+.BR strnul (3)
+.TQ
+.BR strchrnul (3)
+.TQ
+.BR strchr (3)
+.TQ
+.BR strrchr (3)
+.TQ
+.BR strpbrk (3)
+.TQ
+.BR strstr (3)
+.TQ
+.BR strcasestr (3)
+.TQ
+.BR strspn (3)
+.TQ
+.BR strcspn (3)
+.RE
+.SS Separate
+.TP
+Separate strings
+.RS
+.TP
+.BR strsep (3)
+.TQ
+.BR strtok (3)
+.RE
+.SS Others
+.TP
.BR strxfrm (3)
+.SH SEE ALSO
+.BR string_copying (7)
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names
2026-07-08 15:09 ` [PATCH v2 1/4] man/man3/str*.3: " Alejandro Colomar
@ 2026-07-24 18:52 ` sergeh
2026-07-25 22:09 ` Alejandro Colomar
0 siblings, 1 reply; 31+ messages in thread
From: sergeh @ 2026-07-24 18:52 UTC (permalink / raw)
To: Alejandro Colomar
Cc: linux-man, Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
On Wed, Jul 08, 2026 at 05:09:10PM +0200, Alejandro Colomar wrote:
> Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
> Cc: Mark Harris <mark.hsj@gmail.com>
> Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
> Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
Most of these look good to me, just a few notes:
> ---
> man/man3/strcasecmp.3 | 2 +-
> man/man3/strchr.3 | 2 +-
> man/man3/strcmp.3 | 2 +-
> man/man3/strcoll.3 | 2 +-
> man/man3/strcpy.3 | 2 +-
> man/man3/strdup.3 | 2 +-
> man/man3/strerror.3 | 5 +++--
> man/man3/strfmon.3 | 2 +-
> man/man3/strfromd.3 | 3 +--
> man/man3/strfry.3 | 2 +-
> man/man3/strftime.3 | 2 +-
> man/man3/string.3 | 5 +----
> man/man3/strlen.3 | 2 +-
> man/man3/strncat.3 | 5 +----
> man/man3/strnlen.3 | 2 +-
> man/man3/strpbrk.3 | 2 +-
> man/man3/strptime.3 | 2 +-
> man/man3/strsep.3 | 2 +-
> man/man3/strsignal.3 | 2 +-
> man/man3/strspn.3 | 2 +-
> man/man3/strtod.3 | 2 +-
> man/man3/strtoimax.3 | 2 +-
> man/man3/strtok.3 | 2 +-
> man/man3/strtol.3 | 2 +-
> man/man3/strtoul.3 | 2 +-
> man/man3/strverscmp.3 | 2 +-
> man/man3/strxfrm.3 | 2 +-
> 27 files changed, 29 insertions(+), 35 deletions(-)
>
> diff --git a/man/man3/strcasecmp.3 b/man/man3/strcasecmp.3
> index aa554341da4c..65941ddf6524 100644
> --- a/man/man3/strcasecmp.3
> +++ b/man/man3/strcasecmp.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strcasecmp 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcasecmp, strncasecmp \- compare two strings ignoring case
> +strcasecmp, strncasecmp \- strings case-insensitive compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strchr.3 b/man/man3/strchr.3
> index 4e6a96d8cde6..12d08f63ddbb 100644
> --- a/man/man3/strchr.3
> +++ b/man/man3/strchr.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strchr 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strchr \- locate character in string
> +strchr \- string search character
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strcmp.3 b/man/man3/strcmp.3
> index a088412917d2..0a573e52d936 100644
> --- a/man/man3/strcmp.3
> +++ b/man/man3/strcmp.3
> @@ -7,7 +7,7 @@
> .\"
> .TH strcmp 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcmp, strncmp \- compare two strings
> +strcmp, strncmp \- strings compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strcoll.3 b/man/man3/strcoll.3
> index 32d620f52c51..1f9eb2441b13 100644
> --- a/man/man3/strcoll.3
> +++ b/man/man3/strcoll.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strcoll 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcoll \- compare two strings using the current locale
> +strcoll \- strings LC_COLLATE compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strcpy.3 b/man/man3/strcpy.3
> index cea24ff04020..d4d0e4246545 100644
> --- a/man/man3/strcpy.3
> +++ b/man/man3/strcpy.3
> @@ -5,7 +5,7 @@
> .\"
> .TH strcpy 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strcpy, strcat \- copy or catenate a string
> +strcpy, strcat \- string copy/catenate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strdup.3 b/man/man3/strdup.3
> index b4026ed41b4a..05671cd936c2 100644
> --- a/man/man3/strdup.3
> +++ b/man/man3/strdup.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strdup 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strdup, strndup \- duplicate a string
> +strdup, strndup \- string duplicate
string [bounded] duplicate
maybe?
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strerror.3 b/man/man3/strerror.3
> index e3306c7e1608..637d1d812d52 100644
> --- a/man/man3/strerror.3
> +++ b/man/man3/strerror.3
> @@ -7,8 +7,9 @@
> .\"
> .TH strerror 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l \-
> -return string describing error number
> +strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l
> +\-
> +string error-string
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strfmon.3 b/man/man3/strfmon.3
> index e0d9790395cb..dee915c8a9f4 100644
> --- a/man/man3/strfmon.3
> +++ b/man/man3/strfmon.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strfmon 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strfmon, strfmon_l \- convert monetary value to a string
> +strfmon, strfmon_l \- string format monetary value
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strfromd.3 b/man/man3/strfromd.3
> index c221f82df0a8..fa8a10feec01 100644
> --- a/man/man3/strfromd.3
> +++ b/man/man3/strfromd.3
> @@ -6,8 +6,7 @@
> .\"
> .TH strfromd 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strfromd, strfromf, strfroml \- convert a floating-point value into
> -a string
> +strfromd, strfromf, strfroml \- string from float
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
> index 99917e4a6261..54ea313ea262 100644
> --- a/man/man3/strfry.3
> +++ b/man/man3/strfry.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strfry 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strfry \- randomize a string
> +strfry \- string fry
Maybe at least 'string fry (randomize)' ? Because while stirfry
is amusing, it's confusing if you haven't heard it before.
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strftime.3 b/man/man3/strftime.3
> index 66351f6bd43a..bdba4089b934 100644
> --- a/man/man3/strftime.3
> +++ b/man/man3/strftime.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strftime 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strftime \- format date and time
> +strftime \- string format date and time
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/string.3 b/man/man3/string.3
> index 89fe51fba609..2141822e1fd1 100644
> --- a/man/man3/string.3
> +++ b/man/man3/string.3
> @@ -5,10 +5,7 @@
> .\"
> .TH string 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -stpcpy, strcasecmp, strcat, strchr, strcmp, strcoll, strcpy, strcspn,
> -strdup, strfry, strlen, strncat, strncmp, strncpy, strncasecmp, strpbrk,
> -strrchr, strsep, strspn, strstr, strtok, strxfrm
> -\- string operations
> +string \- string operations
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strlen.3 b/man/man3/strlen.3
> index 1dd99bd8e250..639bdd6ac2d2 100644
> --- a/man/man3/strlen.3
> +++ b/man/man3/strlen.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strlen 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strlen \- calculate the length of a string
> +strlen \- string length
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strncat.3 b/man/man3/strncat.3
> index 4b0217b02673..dd39d54939d9 100644
> --- a/man/man3/strncat.3
> +++ b/man/man3/strncat.3
> @@ -5,10 +5,7 @@
> .\"
> .TH strncat 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strncat
> -\-
> -append non-null bytes from a source array to a string,
> -and null-terminate the result
> +strncat \- nonstring catenate
why nonstring? The source string doesn't *have* to be a string,
but can be, right? (IIRC, you define a nonstring as an array
of given length that doesn't necessarily end in \0? I could be
mis-remembering)
string bounded concatente maybe?
I think it helps the quick association in the mind if the start
of the string matches more closely (str).
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strnlen.3 b/man/man3/strnlen.3
> index fbd719ee4888..553a39a9ec7a 100644
> --- a/man/man3/strnlen.3
> +++ b/man/man3/strnlen.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strnlen 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strnlen \- determine the length of a fixed-size string
> +strnlen \- nonstring length
string bounded length?
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
> index 1f56cf18dbac..6e5b79a48bbe 100644
> --- a/man/man3/strpbrk.3
> +++ b/man/man3/strpbrk.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strpbrk 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strpbrk \- search a string for any of a set of bytes
> +strpbrk \- string search characters
does the p stand for returning pointer?
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strptime.3 b/man/man3/strptime.3
> index 979bffea7bf8..4502acfe6d1e 100644
> --- a/man/man3/strptime.3
> +++ b/man/man3/strptime.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strptime 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strptime \- convert a string representation of time to a time tm structure
> +strptime \- string parse time
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strsep.3 b/man/man3/strsep.3
> index 46ba6c5819bd..98859f64909f 100644
> --- a/man/man3/strsep.3
> +++ b/man/man3/strsep.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strsep 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strsep \- extract token from string
> +strsep \- string separate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strsignal.3 b/man/man3/strsignal.3
> index 4a4e8c86e71d..964a5e5aacaf 100644
> --- a/man/man3/strsignal.3
> +++ b/man/man3/strsignal.3
> @@ -8,7 +8,7 @@
> .TH strsignal 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> strsignal, sigabbrev_np, sigdescr_np, sys_siglist \-
> -return string describing signal
> +string signal description
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strspn.3 b/man/man3/strspn.3
> index fe4414317df6..7c85d209c59d 100644
> --- a/man/man3/strspn.3
> +++ b/man/man3/strspn.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strspn 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strspn, strcspn \- get length of a prefix substring
> +strspn, strcspn \- string [complementary] span
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtod.3 b/man/man3/strtod.3
> index 15147ee51006..84a9934ba267 100644
> --- a/man/man3/strtod.3
> +++ b/man/man3/strtod.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtod 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtod, strtof, strtold \- convert ASCII string to floating-point number
> +strtod, strtof, strtold \- string to float
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtoimax.3 b/man/man3/strtoimax.3
> index 71267754ed7a..08413a9f8101 100644
> --- a/man/man3/strtoimax.3
> +++ b/man/man3/strtoimax.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtoimax 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtoimax, strtoumax \- convert string to integer
> +strtoimax, strtoumax \- string to [u]intmax_t
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtok.3 b/man/man3/strtok.3
> index 2d733e886086..4ce708ecbcb4 100644
> --- a/man/man3/strtok.3
> +++ b/man/man3/strtok.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtok 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtok \- extract tokens from strings
> +strtok \- string tokenize
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtol.3 b/man/man3/strtol.3
> index f88b717780f3..4d9c49eaf28a 100644
> --- a/man/man3/strtol.3
> +++ b/man/man3/strtol.3
> @@ -7,7 +7,7 @@
> .\"
> .TH strtol 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtol, strtoll, strtoq \- convert a string to a long integer
> +strtol, strtoll, strtoq \- string to long int
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strtoul.3 b/man/man3/strtoul.3
> index a6f6fd4a8ada..670fcb3868a1 100644
> --- a/man/man3/strtoul.3
> +++ b/man/man3/strtoul.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strtoul 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strtoul, strtoull, strtouq \- convert a string to an unsigned long integer
> +strtoul, strtoull, strtouq \- string to unsigned long int
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strverscmp.3 b/man/man3/strverscmp.3
> index d072471b1431..caadb9717a3b 100644
> --- a/man/man3/strverscmp.3
> +++ b/man/man3/strverscmp.3
> @@ -7,7 +7,7 @@
> .\"
> .TH strverscmp 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strverscmp \- compare two version strings
> +strverscmp \- strings version compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strxfrm.3 b/man/man3/strxfrm.3
> index d4ac57006eb0..a892bf4c813b 100644
> --- a/man/man3/strxfrm.3
> +++ b/man/man3/strxfrm.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strxfrm 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strxfrm \- string transformation
> +strxfrm \- string transform
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME
2026-07-08 15:09 ` [PATCH v2 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
@ 2026-07-24 18:53 ` sergeh
2026-07-25 22:10 ` Alejandro Colomar
0 siblings, 1 reply; 31+ messages in thread
From: sergeh @ 2026-07-24 18:53 UTC (permalink / raw)
To: Alejandro Colomar
Cc: linux-man, Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
On Wed, Jul 08, 2026 at 05:09:13PM +0200, Alejandro Colomar wrote:
> Cc: "Serge E. Hallyn" <serge@hallyn.com>
> Cc: Mark Harris <mark.hsj@gmail.com>
> Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
> Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
D'oh, i should have read this one before the comment to patch 1 :)
Acked-by: Serge Hallyn <sergeh@kernel.org>
thanks
> ---
> man/man3/strfry.3 | 2 +-
> man/man3/strpbrk.3 | 4 ++++
> 2 files changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
> index 54ea313ea262..a65603af918a 100644
> --- a/man/man3/strfry.3
> +++ b/man/man3/strfry.3
> @@ -6,7 +6,7 @@
> .\"
> .TH strfry 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -strfry \- string fry
> +strfry \- string fry (obfuscate)
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
> index 6e5b79a48bbe..ee7a84f1a944 100644
> --- a/man/man3/strpbrk.3
> +++ b/man/man3/strpbrk.3
> @@ -51,6 +51,10 @@ .SH STANDARDS
> C11, POSIX.1-2008.
> .SH HISTORY
> POSIX.1-2001, C89, SVr4, 4.3BSD.
> +.SH BUGS
> +The name of this function is bad.
> +It should have been named strchrs().
> +The NAME section above reflects this.
> .SH SEE ALSO
> .BR memchr (3),
> .BR strchr (3),
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 3/4] man/man3/mem*.3: NAME: Explain the names
2026-07-08 15:09 ` [PATCH v2 3/4] man/man3/mem*.3: NAME: Explain the names Alejandro Colomar
@ 2026-07-24 18:55 ` sergeh
0 siblings, 0 replies; 31+ messages in thread
From: sergeh @ 2026-07-24 18:55 UTC (permalink / raw)
To: Alejandro Colomar
Cc: linux-man, Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
On Wed, Jul 08, 2026 at 05:09:17PM +0200, Alejandro Colomar wrote:
> Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
> Cc: Mark Harris <mark.hsj@gmail.com>
> Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
> Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
Acked-by: Serge Hallyn <sergeh@kernel.org>
> ---
> man/man3/memalign.3 | 4 +---
> man/man3/memccpy.3 | 2 +-
> man/man3/memchr.3 | 2 +-
> man/man3/memcmp.3 | 2 +-
> man/man3/memcpy.3 | 2 +-
> man/man3/memfrob.3 | 2 +-
> man/man3/memmem.3 | 2 +-
> man/man3/memmove.3 | 2 +-
> man/man3/mempcpy.3 | 2 +-
> man/man3/memset.3 | 2 +-
> 10 files changed, 10 insertions(+), 12 deletions(-)
>
> diff --git a/man/man3/memalign.3 b/man/man3/memalign.3
> index 12e8a5bb0c33..5954693e3d30 100644
> --- a/man/man3/memalign.3
> +++ b/man/man3/memalign.3
> @@ -5,9 +5,7 @@
> .\"
> .TH memalign 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memalign
> -\-
> -allocate aligned memory
> +memalign \- memory aligned allocate
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memccpy.3 b/man/man3/memccpy.3
> index d21e7d014af6..706f8152f58c 100644
> --- a/man/man3/memccpy.3
> +++ b/man/man3/memccpy.3
> @@ -6,7 +6,7 @@
> .\"
> .TH memccpy 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memccpy \- copy memory area
> +memccpy \- memory until-and-including-character copy
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memchr.3 b/man/man3/memchr.3
> index 40ea45fdfb83..0a493ff4029d 100644
> --- a/man/man3/memchr.3
> +++ b/man/man3/memchr.3
> @@ -6,7 +6,7 @@
> .\"
> .TH memchr 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memchr \- scan memory for a character
> +memchr \- memory search character
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memcmp.3 b/man/man3/memcmp.3
> index 815bd10252fe..9cecb282ba4f 100644
> --- a/man/man3/memcmp.3
> +++ b/man/man3/memcmp.3
> @@ -6,7 +6,7 @@
> .\"
> .TH memcmp 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memcmp \- compare memory areas
> +memcmp \- memory compare
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memcpy.3 b/man/man3/memcpy.3
> index c94c501b0374..f8f049bf5c4c 100644
> --- a/man/man3/memcpy.3
> +++ b/man/man3/memcpy.3
> @@ -7,7 +7,7 @@
> .\"
> .TH memcpy 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memcpy \- copy memory area
> +memcpy \- memory copy
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memfrob.3 b/man/man3/memfrob.3
> index 90534b749c4c..ee41da0ced7b 100644
> --- a/man/man3/memfrob.3
> +++ b/man/man3/memfrob.3
> @@ -6,7 +6,7 @@
> .\"
> .TH memfrob 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memfrob \- frobnicate (obfuscate) a memory area
> +memfrob \- memory frobnicate (obfuscate)
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memmem.3 b/man/man3/memmem.3
> index 0224a130537e..13fd8b7cea7b 100644
> --- a/man/man3/memmem.3
> +++ b/man/man3/memmem.3
> @@ -6,7 +6,7 @@
> .\"
> .TH memmem 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memmem \- locate a substring
> +memmem \- memory search submemory
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memmove.3 b/man/man3/memmove.3
> index c549c6145176..99f152412142 100644
> --- a/man/man3/memmove.3
> +++ b/man/man3/memmove.3
> @@ -6,7 +6,7 @@
> .\"
> .TH memmove 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memmove \- copy memory area
> +memmove \- memory move
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/mempcpy.3 b/man/man3/mempcpy.3
> index d5ae5812758e..012e6326dd00 100644
> --- a/man/man3/mempcpy.3
> +++ b/man/man3/mempcpy.3
> @@ -7,7 +7,7 @@
> .\"
> .TH mempcpy 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -mempcpy, wmempcpy \- copy memory area
> +mempcpy, wmempcpy \- memory return-offset-pointer copy
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> diff --git a/man/man3/memset.3 b/man/man3/memset.3
> index 370ac64fdf25..91b3b476abf9 100644
> --- a/man/man3/memset.3
> +++ b/man/man3/memset.3
> @@ -6,7 +6,7 @@
> .\"
> .TH memset 3 (date) "Linux man-pages (unreleased)"
> .SH NAME
> -memset \- fill memory with a constant byte
> +memset \- memory set
> .SH LIBRARY
> Standard C library
> .RI ( libc ,\~ \-lc )
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names
2026-07-24 18:52 ` sergeh
@ 2026-07-25 22:09 ` Alejandro Colomar
2026-07-26 23:14 ` alx-0096r1 - string (and nonstring) copying Alejandro Colomar
2026-07-27 2:39 ` [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names Serge E. Hallyn
0 siblings, 2 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-25 22:09 UTC (permalink / raw)
To: sergeh
Cc: linux-man, Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 3449 bytes --]
Hi Serge,
On 2026-07-24T13:52:10-0500, sergeh@kernel.org wrote:
> On Wed, Jul 08, 2026 at 05:09:10PM +0200, Alejandro Colomar wrote:
> > Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
> > Cc: Mark Harris <mark.hsj@gmail.com>
> > Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
> > Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> > Signed-off-by: Alejandro Colomar <alx@kernel.org>
>
> Most of these look good to me, just a few notes:
Thanks!
[...]
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strdup 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strdup, strndup \- duplicate a string
> > +strdup, strndup \- string duplicate
>
> string [bounded] duplicate
> maybe?
I think for consistency with the other strn*() functions, the following
would be better:
string [nonstring] duplicate
[...]
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strfry 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strfry \- randomize a string
> > +strfry \- string fry
>
> Maybe at least 'string fry (randomize)' ? Because while stirfry
> is amusing, it's confusing if you haven't heard it before.
Hmmm, randomize seems to generous, and one may think it is kind of
a shred(1), while it isn't. Maybe 'string fry (reorder)'?
[...]
> > @@ -5,10 +5,7 @@
> > .\"
> > .TH strncat 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strncat
> > -\-
> > -append non-null bytes from a source array to a string,
> > -and null-terminate the result
> > +strncat \- nonstring catenate
>
> why nonstring? The source string doesn't *have* to be a string,
> but can be, right?
Yup, it can be a string, although it would be useless (if you want
a string, you can use strcat(3)).
> (IIRC, you define a nonstring as an array
> of given length that doesn't necessarily end in \0? I could be
> mis-remembering)
Yes, a nonstring is a character array that doesn't necessarily end in
\0. That's why a string is a valid nonstring, but not the other way
around.
> string bounded concatente maybe?
Nope; that's what makes people misunderstand these functions, and
confuse them with safe truncating functions (e.g., strscpy(9)).
I'll send you a copy of a paper I'm writing for the C Committee.
> I think it helps the quick association in the mind if the start
> of the string matches more closely (str).
In this specific case, it's not a good idea. strn*() are NOT string
functions.
[...]
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strnlen 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strnlen \- determine the length of a fixed-size string
> > +strnlen \- nonstring length
>
> string bounded length?
Nope. It doesn't handle strings.
[...]
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strpbrk 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strpbrk \- search a string for any of a set of bytes
> > +strpbrk \- string search characters
>
> does the p stand for returning pointer?
<https://stackoverflow.com/a/501005/6872717>
People say it was "string pointer break". It was a weird name, and
I ignore why they called it like that. It seems to come from 4.3BSD;
blame them. :)
I expanded it as if it were called strchrs(), which is what it should
have been called. It's also the name used in Plan9 internally (in some
cases).
Have a lovely night!
Alex
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME
2026-07-24 18:53 ` sergeh
@ 2026-07-25 22:10 ` Alejandro Colomar
0 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-25 22:10 UTC (permalink / raw)
To: sergeh
Cc: linux-man, Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 1684 bytes --]
Hi Serge,
On 2026-07-24T13:53:04-0500, sergeh@kernel.org wrote:
> On Wed, Jul 08, 2026 at 05:09:13PM +0200, Alejandro Colomar wrote:
> > Cc: "Serge E. Hallyn" <serge@hallyn.com>
> > Cc: Mark Harris <mark.hsj@gmail.com>
> > Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
> > Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> > Signed-off-by: Alejandro Colomar <alx@kernel.org>
>
> D'oh, i should have read this one before the comment to patch 1 :)
Heh! :)
> Acked-by: Serge Hallyn <sergeh@kernel.org>
Thanks!
Cheers,
Alex
>
> thanks
>
> > ---
> > man/man3/strfry.3 | 2 +-
> > man/man3/strpbrk.3 | 4 ++++
> > 2 files changed, 5 insertions(+), 1 deletion(-)
> >
> > diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
> > index 54ea313ea262..a65603af918a 100644
> > --- a/man/man3/strfry.3
> > +++ b/man/man3/strfry.3
> > @@ -6,7 +6,7 @@
> > .\"
> > .TH strfry 3 (date) "Linux man-pages (unreleased)"
> > .SH NAME
> > -strfry \- string fry
> > +strfry \- string fry (obfuscate)
> > .SH LIBRARY
> > Standard C library
> > .RI ( libc ,\~ \-lc )
> > diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
> > index 6e5b79a48bbe..ee7a84f1a944 100644
> > --- a/man/man3/strpbrk.3
> > +++ b/man/man3/strpbrk.3
> > @@ -51,6 +51,10 @@ .SH STANDARDS
> > C11, POSIX.1-2008.
> > .SH HISTORY
> > POSIX.1-2001, C89, SVr4, 4.3BSD.
> > +.SH BUGS
> > +The name of this function is bad.
> > +It should have been named strchrs().
> > +The NAME section above reflects this.
> > .SH SEE ALSO
> > .BR memchr (3),
> > .BR strchr (3),
> > --
> > 2.53.0
> >
>
>
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names
2026-07-02 12:33 [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Alejandro Colomar
` (2 preceding siblings ...)
2026-07-08 15:09 ` [PATCH v2 0/4] str*.3, mem*.3: " Alejandro Colomar
@ 2026-07-25 23:49 ` Alejandro Colomar
2026-07-25 23:49 ` [PATCH v3 1/4] man/man3/str*.3: " Alejandro Colomar
` (4 more replies)
3 siblings, 5 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-25 23:49 UTC (permalink / raw)
To: linux-man
Cc: Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy, Alejandro Colomar
[-- Attachment #1: Type: text/plain, Size: 3962 bytes --]
Hi,
Here's v3 of this patch set. Changes:
- Move change that accidentally was in patch 2/4 into patch 1/4.
- Also add strndup expansion in strdup/strndup(3)
- Clarify what 'fry' means in strfry(3).
- Add Acked-by Serge.
Have a lovely night!
Alex
Alejandro Colomar (4):
man/man3/str*.3: NAME: Explain the names
man/man3/strpbrk.3: BUGS: Clarify the NAME
man/man3/mem*.3: NAME: Explain the names
man/man3/[b]string.3: Rewrite and merge
man/man3/bstring.3 | 81 +-------------
man/man3/memalign.3 | 4 +-
man/man3/memccpy.3 | 2 +-
man/man3/memchr.3 | 2 +-
man/man3/memcmp.3 | 2 +-
man/man3/memcpy.3 | 2 +-
man/man3/memfrob.3 | 2 +-
man/man3/memmem.3 | 2 +-
man/man3/memmove.3 | 2 +-
man/man3/mempcpy.3 | 2 +-
man/man3/memset.3 | 2 +-
man/man3/strcasecmp.3 | 2 +-
man/man3/strchr.3 | 2 +-
man/man3/strcmp.3 | 2 +-
man/man3/strcoll.3 | 2 +-
man/man3/strcpy.3 | 2 +-
man/man3/strdup.3 | 2 +-
man/man3/strerror.3 | 5 +-
man/man3/strfmon.3 | 2 +-
man/man3/strfromd.3 | 3 +-
man/man3/strfry.3 | 2 +-
man/man3/strftime.3 | 2 +-
man/man3/string.3 | 241 ++++++++++++++++++++++++------------------
man/man3/strlen.3 | 2 +-
man/man3/strncat.3 | 5 +-
man/man3/strnlen.3 | 2 +-
man/man3/strpbrk.3 | 6 +-
man/man3/strptime.3 | 2 +-
man/man3/strsep.3 | 2 +-
man/man3/strsignal.3 | 2 +-
man/man3/strspn.3 | 2 +-
man/man3/strtod.3 | 2 +-
man/man3/strtoimax.3 | 2 +-
man/man3/strtok.3 | 2 +-
man/man3/strtol.3 | 2 +-
man/man3/strtoul.3 | 2 +-
man/man3/strverscmp.3 | 2 +-
man/man3/strxfrm.3 | 2 +-
38 files changed, 179 insertions(+), 228 deletions(-)
Range-diff against v2:
1: b17a1548f3ab ! 1: b3feb733f5fd man/man3/str*.3: NAME: Explain the names
@@ man/man3/strdup.3
.TH strdup 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strdup, strndup \- duplicate a string
-+strdup, strndup \- string duplicate
++strdup, strndup \- string/nonstring duplicate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
@@ man/man3/strfry.3
.TH strfry 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfry \- randomize a string
-+strfry \- string fry
++strfry \- string fry (obfuscate)
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
2: a7508ac0f477 ! 2: 7a06ee7f7e76 man/man3/strpbrk.3: BUGS: Clarify the NAME
@@ Metadata
## Commit message ##
man/man3/strpbrk.3: BUGS: Clarify the NAME
- Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
+ Acked-by: Serge Hallyn <sergeh@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
- ## man/man3/strfry.3 ##
-@@
- .\"
- .TH strfry 3 (date) "Linux man-pages (unreleased)"
- .SH NAME
--strfry \- string fry
-+strfry \- string fry (obfuscate)
- .SH LIBRARY
- Standard C library
- .RI ( libc ,\~ \-lc )
-
## man/man3/strpbrk.3 ##
@@ man/man3/strpbrk.3: .SH STANDARDS
C11, POSIX.1-2008.
3: dcc7a485cbe0 ! 3: 69f0c458bec5 man/man3/mem*.3: NAME: Explain the names
@@ Commit message
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
+ Acked-by: Serge Hallyn <sergeh@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
## man/man3/memalign.3 ##
4: 89d3fbe27c06 = 4: dcd1507df29b man/man3/[b]string.3: Rewrite and merge
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v3 1/4] man/man3/str*.3: NAME: Explain the names
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
@ 2026-07-25 23:49 ` Alejandro Colomar
2026-07-25 23:50 ` [PATCH v3 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
` (3 subsequent siblings)
4 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-25 23:49 UTC (permalink / raw)
To: linux-man
Cc: Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy, Alejandro Colomar
[-- Attachment #1: Type: text/plain, Size: 11984 bytes --]
Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/strcasecmp.3 | 2 +-
man/man3/strchr.3 | 2 +-
man/man3/strcmp.3 | 2 +-
man/man3/strcoll.3 | 2 +-
man/man3/strcpy.3 | 2 +-
man/man3/strdup.3 | 2 +-
man/man3/strerror.3 | 5 +++--
man/man3/strfmon.3 | 2 +-
man/man3/strfromd.3 | 3 +--
man/man3/strfry.3 | 2 +-
man/man3/strftime.3 | 2 +-
man/man3/string.3 | 5 +----
man/man3/strlen.3 | 2 +-
man/man3/strncat.3 | 5 +----
man/man3/strnlen.3 | 2 +-
man/man3/strpbrk.3 | 2 +-
man/man3/strptime.3 | 2 +-
man/man3/strsep.3 | 2 +-
man/man3/strsignal.3 | 2 +-
man/man3/strspn.3 | 2 +-
man/man3/strtod.3 | 2 +-
man/man3/strtoimax.3 | 2 +-
man/man3/strtok.3 | 2 +-
man/man3/strtol.3 | 2 +-
man/man3/strtoul.3 | 2 +-
man/man3/strverscmp.3 | 2 +-
man/man3/strxfrm.3 | 2 +-
27 files changed, 29 insertions(+), 35 deletions(-)
diff --git a/man/man3/strcasecmp.3 b/man/man3/strcasecmp.3
index aa554341da4c..65941ddf6524 100644
--- a/man/man3/strcasecmp.3
+++ b/man/man3/strcasecmp.3
@@ -6,7 +6,7 @@
.\"
.TH strcasecmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcasecmp, strncasecmp \- compare two strings ignoring case
+strcasecmp, strncasecmp \- strings case-insensitive compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strchr.3 b/man/man3/strchr.3
index 4e6a96d8cde6..12d08f63ddbb 100644
--- a/man/man3/strchr.3
+++ b/man/man3/strchr.3
@@ -6,7 +6,7 @@
.\"
.TH strchr 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strchr \- locate character in string
+strchr \- string search character
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcmp.3 b/man/man3/strcmp.3
index a088412917d2..0a573e52d936 100644
--- a/man/man3/strcmp.3
+++ b/man/man3/strcmp.3
@@ -7,7 +7,7 @@
.\"
.TH strcmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcmp, strncmp \- compare two strings
+strcmp, strncmp \- strings compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcoll.3 b/man/man3/strcoll.3
index 32d620f52c51..1f9eb2441b13 100644
--- a/man/man3/strcoll.3
+++ b/man/man3/strcoll.3
@@ -6,7 +6,7 @@
.\"
.TH strcoll 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcoll \- compare two strings using the current locale
+strcoll \- strings LC_COLLATE compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strcpy.3 b/man/man3/strcpy.3
index cea24ff04020..d4d0e4246545 100644
--- a/man/man3/strcpy.3
+++ b/man/man3/strcpy.3
@@ -5,7 +5,7 @@
.\"
.TH strcpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strcpy, strcat \- copy or catenate a string
+strcpy, strcat \- string copy/catenate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strdup.3 b/man/man3/strdup.3
index b4026ed41b4a..f3ab0611a323 100644
--- a/man/man3/strdup.3
+++ b/man/man3/strdup.3
@@ -6,7 +6,7 @@
.\"
.TH strdup 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strdup, strndup \- duplicate a string
+strdup, strndup \- string/nonstring duplicate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strerror.3 b/man/man3/strerror.3
index e3306c7e1608..637d1d812d52 100644
--- a/man/man3/strerror.3
+++ b/man/man3/strerror.3
@@ -7,8 +7,9 @@
.\"
.TH strerror 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l \-
-return string describing error number
+strerror, strerrorname_np, strerrordesc_np, strerror_r, strerror_l
+\-
+string error-string
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfmon.3 b/man/man3/strfmon.3
index e0d9790395cb..dee915c8a9f4 100644
--- a/man/man3/strfmon.3
+++ b/man/man3/strfmon.3
@@ -6,7 +6,7 @@
.\"
.TH strfmon 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfmon, strfmon_l \- convert monetary value to a string
+strfmon, strfmon_l \- string format monetary value
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfromd.3 b/man/man3/strfromd.3
index c221f82df0a8..fa8a10feec01 100644
--- a/man/man3/strfromd.3
+++ b/man/man3/strfromd.3
@@ -6,8 +6,7 @@
.\"
.TH strfromd 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfromd, strfromf, strfroml \- convert a floating-point value into
-a string
+strfromd, strfromf, strfroml \- string from float
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strfry.3 b/man/man3/strfry.3
index 99917e4a6261..a65603af918a 100644
--- a/man/man3/strfry.3
+++ b/man/man3/strfry.3
@@ -6,7 +6,7 @@
.\"
.TH strfry 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strfry \- randomize a string
+strfry \- string fry (obfuscate)
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strftime.3 b/man/man3/strftime.3
index 66351f6bd43a..bdba4089b934 100644
--- a/man/man3/strftime.3
+++ b/man/man3/strftime.3
@@ -6,7 +6,7 @@
.\"
.TH strftime 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strftime \- format date and time
+strftime \- string format date and time
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/string.3 b/man/man3/string.3
index 89fe51fba609..2141822e1fd1 100644
--- a/man/man3/string.3
+++ b/man/man3/string.3
@@ -5,10 +5,7 @@
.\"
.TH string 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-stpcpy, strcasecmp, strcat, strchr, strcmp, strcoll, strcpy, strcspn,
-strdup, strfry, strlen, strncat, strncmp, strncpy, strncasecmp, strpbrk,
-strrchr, strsep, strspn, strstr, strtok, strxfrm
-\- string operations
+string \- string operations
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strlen.3 b/man/man3/strlen.3
index 1dd99bd8e250..639bdd6ac2d2 100644
--- a/man/man3/strlen.3
+++ b/man/man3/strlen.3
@@ -6,7 +6,7 @@
.\"
.TH strlen 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strlen \- calculate the length of a string
+strlen \- string length
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strncat.3 b/man/man3/strncat.3
index 4b0217b02673..dd39d54939d9 100644
--- a/man/man3/strncat.3
+++ b/man/man3/strncat.3
@@ -5,10 +5,7 @@
.\"
.TH strncat 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strncat
-\-
-append non-null bytes from a source array to a string,
-and null-terminate the result
+strncat \- nonstring catenate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strnlen.3 b/man/man3/strnlen.3
index fbd719ee4888..553a39a9ec7a 100644
--- a/man/man3/strnlen.3
+++ b/man/man3/strnlen.3
@@ -6,7 +6,7 @@
.\"
.TH strnlen 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strnlen \- determine the length of a fixed-size string
+strnlen \- nonstring length
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
index 1f56cf18dbac..6e5b79a48bbe 100644
--- a/man/man3/strpbrk.3
+++ b/man/man3/strpbrk.3
@@ -6,7 +6,7 @@
.\"
.TH strpbrk 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strpbrk \- search a string for any of a set of bytes
+strpbrk \- string search characters
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strptime.3 b/man/man3/strptime.3
index 979bffea7bf8..4502acfe6d1e 100644
--- a/man/man3/strptime.3
+++ b/man/man3/strptime.3
@@ -6,7 +6,7 @@
.\"
.TH strptime 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strptime \- convert a string representation of time to a time tm structure
+strptime \- string parse time
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strsep.3 b/man/man3/strsep.3
index 46ba6c5819bd..98859f64909f 100644
--- a/man/man3/strsep.3
+++ b/man/man3/strsep.3
@@ -6,7 +6,7 @@
.\"
.TH strsep 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strsep \- extract token from string
+strsep \- string separate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strsignal.3 b/man/man3/strsignal.3
index 4a4e8c86e71d..964a5e5aacaf 100644
--- a/man/man3/strsignal.3
+++ b/man/man3/strsignal.3
@@ -8,7 +8,7 @@
.TH strsignal 3 (date) "Linux man-pages (unreleased)"
.SH NAME
strsignal, sigabbrev_np, sigdescr_np, sys_siglist \-
-return string describing signal
+string signal description
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strspn.3 b/man/man3/strspn.3
index fe4414317df6..7c85d209c59d 100644
--- a/man/man3/strspn.3
+++ b/man/man3/strspn.3
@@ -6,7 +6,7 @@
.\"
.TH strspn 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strspn, strcspn \- get length of a prefix substring
+strspn, strcspn \- string [complementary] span
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtod.3 b/man/man3/strtod.3
index 15147ee51006..84a9934ba267 100644
--- a/man/man3/strtod.3
+++ b/man/man3/strtod.3
@@ -6,7 +6,7 @@
.\"
.TH strtod 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtod, strtof, strtold \- convert ASCII string to floating-point number
+strtod, strtof, strtold \- string to float
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtoimax.3 b/man/man3/strtoimax.3
index 71267754ed7a..08413a9f8101 100644
--- a/man/man3/strtoimax.3
+++ b/man/man3/strtoimax.3
@@ -6,7 +6,7 @@
.\"
.TH strtoimax 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtoimax, strtoumax \- convert string to integer
+strtoimax, strtoumax \- string to [u]intmax_t
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtok.3 b/man/man3/strtok.3
index 2d733e886086..4ce708ecbcb4 100644
--- a/man/man3/strtok.3
+++ b/man/man3/strtok.3
@@ -6,7 +6,7 @@
.\"
.TH strtok 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtok \- extract tokens from strings
+strtok \- string tokenize
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtol.3 b/man/man3/strtol.3
index f88b717780f3..4d9c49eaf28a 100644
--- a/man/man3/strtol.3
+++ b/man/man3/strtol.3
@@ -7,7 +7,7 @@
.\"
.TH strtol 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtol, strtoll, strtoq \- convert a string to a long integer
+strtol, strtoll, strtoq \- string to long int
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strtoul.3 b/man/man3/strtoul.3
index a6f6fd4a8ada..670fcb3868a1 100644
--- a/man/man3/strtoul.3
+++ b/man/man3/strtoul.3
@@ -6,7 +6,7 @@
.\"
.TH strtoul 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strtoul, strtoull, strtouq \- convert a string to an unsigned long integer
+strtoul, strtoull, strtouq \- string to unsigned long int
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strverscmp.3 b/man/man3/strverscmp.3
index d072471b1431..caadb9717a3b 100644
--- a/man/man3/strverscmp.3
+++ b/man/man3/strverscmp.3
@@ -7,7 +7,7 @@
.\"
.TH strverscmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strverscmp \- compare two version strings
+strverscmp \- strings version compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/strxfrm.3 b/man/man3/strxfrm.3
index d4ac57006eb0..a892bf4c813b 100644
--- a/man/man3/strxfrm.3
+++ b/man/man3/strxfrm.3
@@ -6,7 +6,7 @@
.\"
.TH strxfrm 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-strxfrm \- string transformation
+strxfrm \- string transform
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v3 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
2026-07-25 23:49 ` [PATCH v3 1/4] man/man3/str*.3: " Alejandro Colomar
@ 2026-07-25 23:50 ` Alejandro Colomar
2026-07-25 23:50 ` [PATCH v3 3/4] man/man3/mem*.3: NAME: Explain the names Alejandro Colomar
` (2 subsequent siblings)
4 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-25 23:50 UTC (permalink / raw)
To: linux-man
Cc: Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy, Alejandro Colomar, Serge Hallyn
[-- Attachment #1: Type: text/plain, Size: 758 bytes --]
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Acked-by: Serge Hallyn <sergeh@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/strpbrk.3 | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/man/man3/strpbrk.3 b/man/man3/strpbrk.3
index 6e5b79a48bbe..ee7a84f1a944 100644
--- a/man/man3/strpbrk.3
+++ b/man/man3/strpbrk.3
@@ -51,6 +51,10 @@ .SH STANDARDS
C11, POSIX.1-2008.
.SH HISTORY
POSIX.1-2001, C89, SVr4, 4.3BSD.
+.SH BUGS
+The name of this function is bad.
+It should have been named strchrs().
+The NAME section above reflects this.
.SH SEE ALSO
.BR memchr (3),
.BR strchr (3),
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v3 3/4] man/man3/mem*.3: NAME: Explain the names
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
2026-07-25 23:49 ` [PATCH v3 1/4] man/man3/str*.3: " Alejandro Colomar
2026-07-25 23:50 ` [PATCH v3 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
@ 2026-07-25 23:50 ` Alejandro Colomar
2026-07-25 23:50 ` [PATCH v3 4/4] man/man3/[b]string.3: Rewrite and merge Alejandro Colomar
2026-07-29 15:15 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
4 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-25 23:50 UTC (permalink / raw)
To: linux-man
Cc: Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy, Alejandro Colomar, Serge Hallyn
[-- Attachment #1: Type: text/plain, Size: 4319 bytes --]
Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Acked-by: Serge Hallyn <sergeh@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/memalign.3 | 4 +---
man/man3/memccpy.3 | 2 +-
man/man3/memchr.3 | 2 +-
man/man3/memcmp.3 | 2 +-
man/man3/memcpy.3 | 2 +-
man/man3/memfrob.3 | 2 +-
man/man3/memmem.3 | 2 +-
man/man3/memmove.3 | 2 +-
man/man3/mempcpy.3 | 2 +-
man/man3/memset.3 | 2 +-
10 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/man/man3/memalign.3 b/man/man3/memalign.3
index 12e8a5bb0c33..5954693e3d30 100644
--- a/man/man3/memalign.3
+++ b/man/man3/memalign.3
@@ -5,9 +5,7 @@
.\"
.TH memalign 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memalign
-\-
-allocate aligned memory
+memalign \- memory aligned allocate
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memccpy.3 b/man/man3/memccpy.3
index d21e7d014af6..706f8152f58c 100644
--- a/man/man3/memccpy.3
+++ b/man/man3/memccpy.3
@@ -6,7 +6,7 @@
.\"
.TH memccpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memccpy \- copy memory area
+memccpy \- memory until-and-including-character copy
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memchr.3 b/man/man3/memchr.3
index 40ea45fdfb83..0a493ff4029d 100644
--- a/man/man3/memchr.3
+++ b/man/man3/memchr.3
@@ -6,7 +6,7 @@
.\"
.TH memchr 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memchr \- scan memory for a character
+memchr \- memory search character
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memcmp.3 b/man/man3/memcmp.3
index 815bd10252fe..9cecb282ba4f 100644
--- a/man/man3/memcmp.3
+++ b/man/man3/memcmp.3
@@ -6,7 +6,7 @@
.\"
.TH memcmp 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memcmp \- compare memory areas
+memcmp \- memory compare
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memcpy.3 b/man/man3/memcpy.3
index c94c501b0374..f8f049bf5c4c 100644
--- a/man/man3/memcpy.3
+++ b/man/man3/memcpy.3
@@ -7,7 +7,7 @@
.\"
.TH memcpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memcpy \- copy memory area
+memcpy \- memory copy
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memfrob.3 b/man/man3/memfrob.3
index 90534b749c4c..ee41da0ced7b 100644
--- a/man/man3/memfrob.3
+++ b/man/man3/memfrob.3
@@ -6,7 +6,7 @@
.\"
.TH memfrob 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memfrob \- frobnicate (obfuscate) a memory area
+memfrob \- memory frobnicate (obfuscate)
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memmem.3 b/man/man3/memmem.3
index 0224a130537e..13fd8b7cea7b 100644
--- a/man/man3/memmem.3
+++ b/man/man3/memmem.3
@@ -6,7 +6,7 @@
.\"
.TH memmem 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memmem \- locate a substring
+memmem \- memory search submemory
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memmove.3 b/man/man3/memmove.3
index c549c6145176..99f152412142 100644
--- a/man/man3/memmove.3
+++ b/man/man3/memmove.3
@@ -6,7 +6,7 @@
.\"
.TH memmove 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memmove \- copy memory area
+memmove \- memory move
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/mempcpy.3 b/man/man3/mempcpy.3
index d5ae5812758e..012e6326dd00 100644
--- a/man/man3/mempcpy.3
+++ b/man/man3/mempcpy.3
@@ -7,7 +7,7 @@
.\"
.TH mempcpy 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-mempcpy, wmempcpy \- copy memory area
+mempcpy, wmempcpy \- memory return-offset-pointer copy
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
diff --git a/man/man3/memset.3 b/man/man3/memset.3
index 370ac64fdf25..91b3b476abf9 100644
--- a/man/man3/memset.3
+++ b/man/man3/memset.3
@@ -6,7 +6,7 @@
.\"
.TH memset 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-memset \- fill memory with a constant byte
+memset \- memory set
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v3 4/4] man/man3/[b]string.3: Rewrite and merge
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
` (2 preceding siblings ...)
2026-07-25 23:50 ` [PATCH v3 3/4] man/man3/mem*.3: NAME: Explain the names Alejandro Colomar
@ 2026-07-25 23:50 ` Alejandro Colomar
2026-07-29 15:15 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
4 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-25 23:50 UTC (permalink / raw)
To: linux-man
Cc: Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy, Alejandro Colomar
[-- Attachment #1: Type: text/plain, Size: 7542 bytes --]
Let's have a single page with an organized list of functions.
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
man/man3/bstring.3 | 81 +--------------
man/man3/string.3 | 238 ++++++++++++++++++++++++++-------------------
2 files changed, 137 insertions(+), 182 deletions(-)
diff --git a/man/man3/bstring.3 b/man/man3/bstring.3
index c3b5782d3554..f02a9f29817b 100644
--- a/man/man3/bstring.3
+++ b/man/man3/bstring.3
@@ -1,80 +1 @@
-.\" Copyright 1993, David Metcalfe <david@prism.demon.co.uk>
-.\" Copyright, the authors of the Linux man-pages project
-.\"
-.\" SPDX-License-Identifier: Linux-man-pages-copyleft
-.\"
-.TH bstring 3 (date) "Linux man-pages (unreleased)"
-.SH NAME
-bcmp, bcopy, bzero, memccpy, memchr, memcmp, memcpy, memfrob, memmem,
-memmove, memset \- byte string operations
-.SH LIBRARY
-Standard C library
-.RI ( libc ,\~ \-lc )
-.SH SYNOPSIS
-.nf
-.B #include <string.h>
-.P
-.BR "int bcmp(" "size_t n;"
-.BI " const void " s1 [ n "], const void " s2 [ n "], size_t " n );
-.P
-.BR "void bcopy(" "size_t n;"
-.BI " const void " src [ n "], void " dest [ n "], size_t " n );
-.P
-.BR "void bzero(" "size_t n;"
-.BI " void " s [ n "], size_t " n );
-.P
-.BR "void *memccpy(" "size_t n;"
-.BI " void " dest [ n "], const void " src [ n "], int " c ", size_t " n );
-.P
-.BR "void *memchr(" "size_t n;"
-.BI " const void " s [ n "], int " c ", size_t " n );
-.P
-.BR "int memcmp(" "size_t n;"
-.BI " const void " s1 [ n "], const void " s2 [ n "], size_t " n );
-.P
-.BR "void *memcpy(" "size_t n;"
-.BI " void " dest [ n "], const void " src [ n "], size_t " n );
-.P
-.BR "void *memfrob(" "size_t n;"
-.BI " void " s [ n "], size_t " n );
-.P
-.BR "void *memmem(" "size_t hsize, size_t nsize;"
-.BI " const void " haystack [ hsize "], size_t " hsize ,
-.BI " const void " needle [ nsize "], size_t " nsize );
-.P
-.BR "void *memmove(" "size_t n;"
-.BI " void " dest [ n "], const void " src [ n "], size_t " n );
-.P
-.BR "void *memset(" "size_t n;"
-.BI " void " s [ n "], int " c ", size_t " n );
-.fi
-.SH DESCRIPTION
-The byte string functions perform operations on strings (byte arrays)
-that are not necessarily null-terminated.
-See the individual man pages
-for descriptions of each function.
-.SH NOTES
-The functions
-.BR bcmp ()
-and
-.BR bcopy ()
-are obsolete.
-Use
-.BR memcmp ()
-and
-.BR memmove ()
-instead.
-.\" The old functions are not even available on some non-GNU+Linux systems.
-.SH SEE ALSO
-.BR bcmp (3),
-.BR bcopy (3),
-.BR bzero (3),
-.BR memccpy (3),
-.BR memchr (3),
-.BR memcmp (3),
-.BR memcpy (3),
-.BR memfrob (3),
-.BR memmem (3),
-.BR memmove (3),
-.BR memset (3),
-.BR string (3)
+.so man3/string.3
diff --git a/man/man3/string.3 b/man/man3/string.3
index 2141822e1fd1..da87799a5e69 100644
--- a/man/man3/string.3
+++ b/man/man3/string.3
@@ -5,7 +5,7 @@
.\"
.TH string 3 (date) "Linux man-pages (unreleased)"
.SH NAME
-string \- string operations
+string \- byte operations
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
@@ -15,115 +15,149 @@ .SH SYNOPSIS
.B #include <strings.h>
.fi
.SH DESCRIPTION
-The string functions perform operations on null-terminated
-strings.
-See the individual man pages for descriptions of each function.
+.SS Write
.TP
-.BR strcasecmp (3)
-Compare two strings,
-ignoring case.
+Write bytes
+.RS
.TP
-.BR strncasecmp (3)
-Compare the first bytes of two strings,
-ignoring case.
+.BR bzero (3)
+.TQ
+.BR memset (3)
+.RE
+.SS Copy
.TP
-.BR stpcpy (3)
-Copy a string,
-returning a pointer to the end of the resulting string.
+Copy bytes
+.RS
.TP
-.BR strcat (3)
-Append a string into an existing string.
+.BR memmove (3)
+.TQ
+.BR memcpy (3)
+.TQ
+.BR mempcpy (3)
+.TQ
+.BR memccpy (3)
+.RE
.TP
-.BR strchr (3)
-Find the first occurrence of a character
-in a string.
-.TP
-.BR strcmp (3)
-Compare two strings.
-.TP
-.BR strcoll (3)
-Compare two strings,
-using the current locale.
+Copy strings
+.RS
.TP
.BR strcpy (3)
-Copy a string.
+.TQ
+.BR stpcpy (3)
+.RE
.TP
-.BR strcspn (3)
-Calculate the length of the initial segment of a string
-which does not contain any of the rejected bytes.
-.TP
-.BR strdup (3)
-Duplicate a string
-in memory allocated using
-.BR malloc (3).
-.TP
-.BR strfry (3)
-Randomly swap the characters in a string.
-.TP
-.BR strlen (3)
-Return the length of a string.
-.TP
-.BR strncat (3)
-Append non-null bytes from an array
-to a string,
-and null-terminate the result.
-.TP
-.BR strncmp (3)
-Compare the first bytes of two strings.
-.TP
-.BR strpbrk (3)
-Find the first occurrence in a string
-of one of the bytes in the accepted bytes.
-.TP
-.BR strrchr (3)
-Find the last occurrence of a character
-in a string.
-.TP
-.BR strsep (3)
-Extract the initial field in a string
-that is delimited by one of the delimiter bytes.
-.TP
-.BR strspn (3)
-Calculate the length of the initial segment of a string
-that consists entirely of accepted bytes.
-.TP
-.BR strstr (3)
-Find the first occurrence of a substring
-in a string.
-.TP
-.BR strtok (3)
-Extract tokens from a string
-that are delimited by one of the delimiter bytes.
-.TP
-.BR strxfrm (3)
-Transforms a string
-to the current locale
-and copies the first bytes to a buffer.
+Copy nonstrings
+.RS
.TP
.BR strncpy (3)
-Fill a fixed-size buffer with leading non-null bytes from a source array,
-padding with null bytes as needed.
-.SH SEE ALSO
-.BR bstring (3),
-.BR stpcpy (3),
-.BR strcasecmp (3),
-.BR strcat (3),
-.BR strchr (3),
-.BR strcmp (3),
-.BR strcoll (3),
-.BR strcpy (3),
-.BR strcspn (3),
-.BR strdup (3),
-.BR strfry (3),
-.BR strlen (3),
-.BR strncasecmp (3),
-.BR strncat (3),
-.BR strncmp (3),
-.BR strncpy (3),
-.BR strpbrk (3),
-.BR strrchr (3),
-.BR strsep (3),
-.BR strspn (3),
-.BR strstr (3),
-.BR strtok (3),
+.RE
+.SS Catenate
+.TP
+Catenate strings
+.RS
+.TP
+.BR strcat (3)
+.RE
+.TP
+Catenate nonstrings
+.RS
+.TP
+.BR strncat (3)
+.RE
+.SS Duplicate
+.TP
+Duplicate strings
+.RS
+.TP
+.BR strdup (3)
+.TQ
+.BR strdupa (3)
+.RE
+.TP
+Duplicate nonstrings
+.RS
+.TP
+.BR strndup (3)
+.TQ
+.BR strndupa (3)
+.RE
+.SS Compare
+.TP
+Compare bytes
+.RS
+.TP
+.BR memeq (3)
+.TQ
+.BR memcmp (3)
+.RE
+.TP
+Compare strings
+.RS
+.TP
+.BR streq (3)
+.TQ
+.BR strcmp (3)
+.TQ
+.BR strcasecmp (3)
+.TQ
+.BR strcoll (3)
+.TQ
+.BR strverscmp (3)
+.RE
+.TP
+Compare nonstrings
+.RS
+.TP
+.BR strncmp (3)
+.TQ
+.BR strncasecmp (3)
+.RE
+.SS Search
+.TP
+Search bytes
+.RS
+.TP
+.BR memchr (3)
+.TQ
+.BR memrchr (3)
+.TQ
+.BR memmem (3)
+.RE
+.TP
+Search strings
+.RS
+.TP
+.BR strlen (3)
+.TQ
+.BR strnul (3)
+.TQ
+.BR strchrnul (3)
+.TQ
+.BR strchr (3)
+.TQ
+.BR strrchr (3)
+.TQ
+.BR strpbrk (3)
+.TQ
+.BR strstr (3)
+.TQ
+.BR strcasestr (3)
+.TQ
+.BR strspn (3)
+.TQ
+.BR strcspn (3)
+.RE
+.SS Separate
+.TP
+Separate strings
+.RS
+.TP
+.BR strsep (3)
+.TQ
+.BR strtok (3)
+.RE
+.SS Others
+.TP
.BR strxfrm (3)
+.SH SEE ALSO
+.BR string_copying (7)
--
2.53.0
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* alx-0096r1 - string (and nonstring) copying
2026-07-25 22:09 ` Alejandro Colomar
@ 2026-07-26 23:14 ` Alejandro Colomar
2026-07-28 12:05 ` Mark Harris
2026-07-27 2:39 ` [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names Serge E. Hallyn
1 sibling, 1 reply; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-26 23:14 UTC (permalink / raw)
To: sergeh
Cc: linux-man, Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 36920 bytes --]
Hi Serge,
On 2026-07-26T00:09:23+0200, Alejandro Colomar wrote:
[...]
> > > @@ -5,10 +5,7 @@
> > > .\"
> > > .TH strncat 3 (date) "Linux man-pages (unreleased)"
> > > .SH NAME
> > > -strncat
> > > -\-
> > > -append non-null bytes from a source array to a string,
> > > -and null-terminate the result
> > > +strncat \- nonstring catenate
> >
> > why nonstring? The source string doesn't *have* to be a string,
> > but can be, right?
>
> Yup, it can be a string, although it would be useless (if you want
> a string, you can use strcat(3)).
>
> > (IIRC, you define a nonstring as an array
> > of given length that doesn't necessarily end in \0? I could be
> > mis-remembering)
>
> Yes, a nonstring is a character array that doesn't necessarily end in
> \0. That's why a string is a valid nonstring, but not the other way
> around.
>
> > string bounded concatente maybe?
>
> Nope; that's what makes people misunderstand these functions, and
> confuse them with safe truncating functions (e.g., strscpy(9)).
>
> I'll send you a copy of a paper I'm writing for the C Committee.
It is at the bottom of this email. I will publish it as an N document
in August.
> > I think it helps the quick association in the mind if the start
> > of the string matches more closely (str).
>
> In this specific case, it's not a good idea. strn*() are NOT string
> functions.
Have a lovely night!
Alex
---
Name
alx-0096r1 - string (and nonstring) copying
Principles
- Facilitate portability
- Allow programming freedom
- Avoid quiet changes
Category
Library: <string.h>
Author
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Cc: Kees Cook <kees@kernel.org>
Cc: Serge Hallyn <serge@hallyn.com>
History
<https://www.alejandro-colomar.es/src/alx/alx/std/wg14/alx-0096.git/>
r0 (2026-07-16):
- Initial draft.
r1 (2026-07-17):
- tfix, wfix
- Simplify diff.
- More carefully analyze memcpy_and_pad().
- strtcpy/cat(): If dst==0, the behavior is undefined.
- Add wording for removing strncpy_s().
- Add TODOs for wide-string wording.
- E2BIG or EMSGSIZE?
Abstract
strncpy(3) is a function with good semantics, but a bad name and
history, which has led to abuse.
Description
There's a recent push to remove strncpy(3) from the ISO C
standard without carefully analyzing the consequences of the
removal. There have been informal messages from committee
members in the mailing list, and now we have an N document that
will be heard in the next meeting:
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3935.htm>
N3935 (2026-07-15; "Removing strncpy")
N3935 contains some truth and good arguments in it, but it is
mixed with some mistakes too.
Below is an attempt at carefully analyzing the history of this
function and why it has been misused, and also a thorough
analysis of the replacement candidates.
Seventh Edition Unix; strncpy(3)
strncpy(3) was first introduced in Seventh Edition Unix (V7).
It was added for one very specific use case: copying a source
string into a destination character sequence in a fixed-size
member of a structure (not a string), and padding the unused
bytes with '\0'.
This was useful bad then in members of the utmp(5) structure,
and modern shadow-utils still need this function for dealing
with utmp(5).
While programs should avoid non-NUL-terminated character
sequences (aka, nonstrings) within fixed-width structure members
in code that is used only internally, this is sometimes
necessary in structures that are part of a protocol. Thus,
while this is a niche use case, it remains something that some
programs need to do, and need to do reliably and safely.
Another program that needs this functionality is tar(1) (from
what I've heard).
System V; mem*() functions
Back in the times of V7, the concept of a string was less
specific, and what we now call byte arrays, they called byte
strings. In fact, memcmp(3) was then called bcmp(3). That's
why all the byte functions are provided in <string.h>.
System V invented the mem*() functions --AFAIK--. They are
essentially the b*() funcitons from V7, with minor tweaks to the
parameters and return values. For example, memcpy(3) was made
consistent with strcpy(3), in that both take the input as $2
and write to $1. The old bcopy(3) was reversed compared to
strcpy(3), which was quite confusing and error-prone
(especially, before 'const' was invented).
POSIX says memcmp(3) was first standardized in Issue 1 of the
SVID (System V Interface Definition), which corroborates that
these functions were invented in SysV.
It is good that such a renovation of the names, parameters, and
return values happened.
It would have been good if strncpy(3) and strncat(3) would have
been renamed (and tweaked) too back then, but it didn't happen.
C89; string
C89 specified the term 'string' very clearly, in 4.1.1:
A string is a contiguous sequence of characters
terminated by and including the first null character.
This differed from the old ambiguous meaning. The header file
for operations on byte arrays remained <string.h> for historical
reasons, even if it conflicted with this specification of
string.
The strn*() functions, strncpy(3) and strncat(3), also remained
with their names, for historical reasons, even if it conflicted
with this specification of string.
Other than that, the standard was quite consistent and strict
with its meaning of the term string.
I believe this consistency --with the exceptions mentioned
above-- is what led programmers to trust string functions to
handle strings, and anything with a str*() in the name was
believed to be good for handling strings. Programmers forgot
the not-so-consistent history of the term string prior to
standardization.
In retrospective, the strncpy(3) function would have been better
called strtomem_pad(), which clearly reflects what it does.
Maybe that would have made programmers aware of its semantics,
and it would have significantly reduces misuses of the function.
strncat(3)
strncat(3) is similar to strncpy(3) --while at the same time,
very different--. It is actually the opposite of strncpy(3):
it takes a nonstring as input, and writes a string.
A better name for it would have been memtostr_cat().
Curiously, there's no 'cpy' version of strncat(3). There's
nothing with the semantics of memtostr() in ISO C, nor in the
historic Unix systems (V7, BSD, System V).
strn*(); [[gnu::nonstring]]
In general, the names of strn*() functions are unfortunate,
because they are better suited for handling nonstrings
(character sequences that don't fit in the standard
specification of 'string').
GNU C has the attribute [[gnu::nonstring]] to annotate these
things.
<https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-nonstring>
While renaming the functions would be better, one can think of
the 'n' in strn*() as a mnemonic for NonSTRing. This partially
mitigates the naming issue. Still, better names would be much
better:
strncpy() => strtomem_pad()
strncat() => memtostr_cat()
Linux; strtomem_pad()
Recently, it appeared in the (fake) news that Linux had
completely removed all uses of strncpy(3), and banned it in new
code.
This is not really true. They renamed strncpy(3) to
strtomem_pad(), which has essentially the same semantics of
strncpy(3), with a different name. They still use it. This
reflects that there are still legitimate uses for which
strncpy(3) is still good and necessary.
The number of uses of strtomem_pad() (i.e., strncpy(3) renamed)
is very small --because as explained above, it's a niche
function--, but they exist. Here are the uses in the Linux
source some time after v7.2-rc2:
$ grep -rc 'strtomem_pad(.\+)' | grep -v :0$
include/linux/string.h:1
lib/tests/string_kunit.c:1
drivers/soc/qcom/cmd-db.c:1
drivers/gpu/drm/drm_connector.c:2
drivers/auxdisplay/panel.c:3
arch/x86/coco/tdx/tdx.c:1
fs/nilfs2/ioctl.c:2
fs/ext4/file.c:1
fs/ext4/super.c:1
What they achieved is a removal of all misuses of strncpy(3),
while keeing the good ones. This is the really good news. And
we should learn of how Linux achieved it, as it has achieved
something that few people had achieved before.
Saying "Linux got rid of strncpy(3); let's wipe it here too"
without carefully analyzing exactly what they did and how would
be dangerous.
Instead, it seems Linux has proved that by carefully renaming
functions, we can get rid of their misuses.
Linux; strscpy(9), memtostr(), ...
Renaming strncpy(3) alone wasn't enough. One or more new
functions had to be designed to replace the existing misuses of
the function, so that code could replace the misuses by
something appropriate.
Linux claims to have replaced strncpy(3) with the following
functions or macros:
strscpy(9)
when the destination must be NUL-terminated.
strscpy_pad()
when the destination must be NUL-terminated and
zero-padded (for example, for structs crossing
privilege boundaries).
memtostr()
for NUL-terminated destinations copied from
non-NUL-terminated fixed-width sources, with the
__nonstring attribute on the source.
memtostr_pad()
for the same case, but with zero-padding.
strtomem()
for non-NUL-terminated fixed-width destinations,
with the __nonstring attribute on the
destination.
strtomem_pad()
for non-NUL-terminated destinations that also
need zero-padding.
memcpy_and_pad()
for bounded copies from potentially unterminated
sources where the destination size is a runtime
value.
Here's a more detailed analysis of whether we need these
functions in the standard, and also some comments about their
name or semantics.
strscpy(9)
This is the main use case that programmers miss
in ISO C: copying a string with truncation.
A better name for this function would have been
strtcpy(), with a T for truncation. Safety is
not absolute, and thus an 's' for safety in the
name of a function will likely result in misuse.
The name should describe the semantics of the
function.
The committee should seriously consider adding
this function to the standard, with high
priority.
strscpy_pad()
This is a niche function, which is useful for
writing strings in structures that are passed
from privileged code to non-privileged code.
Outside of a kernel, this functionality is rare,
and thus the committee should ignore this one,
at least in a first round.
A better name would be strtcpy_pad(), for
similar reasons.
memtostr()
This is the 'cpy' version of strncat(3).
As long as we have strncat(3) in the standard,
it would be good to have this 'cpy' version of
it, since they're useful for the same kinds of
code.
Implementations can use strncat(3) to trivially
implement this function:
#define memtostr(dst, src, n) \
strncat(strcpy(dst, ""), src, n)
(And in fact, this is a common workaround for
the lack of a 'cpy' version of strncat(3).
Programmers may open-code this directly, but
the pattern remains.)
If we add this function, it would be good to
rename strncat(3) to memtostr_cat().
memtostr_pad()
Like strscpy_pad(), this is rare. The kernel
needs this, beacuse it has to pass structures to
less privileged code, but regular programs don't
need this. We should avoid standardization, at
least in a first round.
strtomem()
This is *bogus*. The Linux kernel should remove
it ASAP.
Outside of tests, it has exactly one use case in
the Linux kernel:
$ grep -rc 'strtomem(.\+)' | grep -v :0$
include/linux/string.h:1
lib/tests/string_kunit.c:4
drivers/s390/char/sclp.c:1
The commit that added it says:
We don't need strtomem_pad() because `e` is
already memset to 0 -- rendering any
additional NUL-padding useless.
This would be more robust using strtomem_pad().
The performance difference should be negligible,
especially since the field has 4 bytes only.
This function is entirely unnecessary, and is
only asking for trouble (the day they remove
that memset(3), it will break).
If a copy didn't pad remaining bytes, it would
be impossible to distinguish data from noise, so
this function is evil.
strtomem_pad()
This is the old strncpy(3), with a better name.
It's still as useful and necessary as it has
always been.
memcpy_and_pad()
This seems a workaround for other cases in the
kernel that aren't necessarily useful for the
general public.
This is a niche function, which is useful for
copying fixed-width arrays in structures that
are received in privileged code from
non-privileged code.
Outside of a kernel, this functionality is rare,
and thus the committee should ignore this one,
at least in a first round.
So, the list of functions that we should consider for
standardization is smaller:
- strtcpy() (strscpy(9) in Linux)
- memtostr() (the 'cpy' of strncat(3))
- strtomem_pad() (strncpy() renamed)
As Jens Gustedt pointed out in the mailing list of the
committee, and as pointed above, strtomem without padding
doesn't make sense. Thus, the name strtomem_pad() can be
simplified to just strtomem(), because padding is obvious.
Thus, the functions that should be considered are:
- strtcpy() (strscpy(9) in Linux)
- memtostr() (the 'cpy' of strncat(3))
- strtomem() (strncpy(3) renamed)
And for consistency, strncat(3) should be renamed to something
like memtostrcat().
strncpy(3), strtomem(), and truncation
One thing we should consider improving if we'll have a breaking
change is to report truncation.
strtomem()/strncpy(3) necessarily truncates if the string
doesn't fit. However, it doesn't report truncation if it
happens. Since the return value of strtomem()/strncpy(3) isn't
very useful, we can take advantage and modify it to return an
error code if there's truncation. Let's return NULL on
truncation.
shadow; strtcpy(), strncpy(3)
The shadow project more or less agrees with the Linux kernel in
this regard. It has implemented strtcpy(), and uses strncpy(3).
It doesn't have memtostr() yet, because it happens to always
allocate the buffer at the same time, using strndup(3) --which
is essentially malloc(3)+memtostr()--, but that will change
soon, because (non-VLA) arrays are safer, as they allow
validation of source and destination sizes at compile time.
The implementations are independent, and happened to be
fundamentally identical. It seems that good implementations of
string and nonstring copying code converge to this set of
interfaces.
Here's a naive implementation of strtcpy():
ssize_t
strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
{
bool trunc;
size_t dlen, slen;
if (dsize == 0) // UB
abort();
slen = strnlen(src, dsize);
trunc = (slen == dsize);
dlen = slen - trunc;
stpcpy(mempcpy(dst, src, dlen), "");
if (trunc) {
errno = E2BIG;
return -1;
}
return slen;
}
shadow; strtcat()
A cat version of strtcpy() is also useful. The Linux kernel
seems to be misusing strncat(3), because they took priority on
addressing misuses of strncpy(3), but it would do good in also
replacing misuses of strncat(3).
shadow does have strtcat() --although the number of uses is way
smaller than those of strtcpy()--.
Thus, for consideration:
- strtcpy() (strscpy(9) in Linux)
- strtcat() (the 'cat' of strtcpy())
- memtostr() (the 'cpy' of strncat(3))
- memtostrcat() (strncat(3) renamed)
- strtomem() (strncpy(3) renamed)
An implementation of strtcat() is:
ssize_t
strtcat(char *restrict dst, const char *restrict src, size_t dsize)
{
char *p, *end;
end = dst + dsize;
p = stpecpy(strnul(dst), end, src);
if (p == NULL)
return -1;
return p - dst;
}
SEI CERT STR32-C
SEI CERT --which supposedly is a "secure" coding guideline--
recommends using strncpy(3) for copying a string with
truncation.
<https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/characters-and-strings-str/str32-c/#compliant-solution-truncation>
This is the kind of bad teaching that has had the negative
effects we have today. Programmers misuse tools like strncpy(3)
--among other reasons-- because they're taught by SEI CERT and
others to use those tools for something they weren't designed
for.
SEI CERT STR32-C proposes this code as correct code for copying
a string with truncation:
size_t func(const char *source) {
char c_str[STR_SIZE];
size_t ret = 0;
if (source) {
strncpy(c_str, source, sizeof(c_str) - 1);
c_str[sizeof(c_str) - 1] = '\0';
ret = strlen(c_str);
} else {
/* Handle null pointer */
}
return ret;
}
I claim that that code is bogus, and that good code for doing
that should instead look like this:
if (strtcpy(buf, source, countof(buf)) == -1)
goto trunc;
That's one line that does one thing and does it well. It takes
an input string, and copies the string into an output buffer,
truncating as necessary, not reading more than necessary, and
not writing more than necessary. It reports truncation if it
happens, because truncation is usually a bad thing.
For that, one must implement strtcpy() (see above for an
implementation).
System V; memccpy(3)
memccpy(3) was invented in System V (like the other mem*()
functions). The System V sources are not public (AFAIK), so
it's not known what this function was designed for. It was
later added to the BSDs and glibc for compatibility with SysV,
but nobody really knew what this function is good for. It
doesn't seem ergonomic for any basic functionality.
Ignoring tests, you can find 0 calls to memccpy(3) in NetBSD,
and 3 calls in FreeBSD --two of which are in two repeated
implementations of strncat(3), and the other one is really
unique, in "bin/sh/parser.c"--.
Debian shows several more uses of memccpy(3) that are not in
tests, but all of those seem to be newer than C23, so they seem
to be cases of "if C23 added this, it must be good, let's use
it" without really analyzing whether that function was actually
good.
These two unique calls found in NetBSD are *terrible*. By
*terrible* I mean as bad as misuses of strncpy(3). They show
how error-prone memccpy(3) is, even for the most natural use
cases. Here's one of the examples:
if (fmt[0] != '}') {
char *end;
end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
if (end == NULL) {
/*
* Format too long or no '}', so
* ignore "\D{" altogether.
* The loop will do i++, but nothing
* was written to ps, so do i-- here.
* Rewind fmt for similar reason.
*/
i--;
fmt--;
break;
}
*--end = '\0'; /* Ignore the copy of '}'. */
fmt += end - tfmt;
}
This is the most legitimate use case of memccpy(3) that one can
conceive: we want to copy the leading part of a string until a
delimiter character is found. And even this use is full of
opportunities for off-by-one bugs. A better design would have
not copied the delimiter, allowing the user to decide whether to
copy it or not (instead of forcing it to go back and remove it,
which is more complex).
POSIX; memccpy(3)
POSIX standardized memccpy(3) just because it was in System V.
POSIX derives from Issue 1 of the SVID. It's not surprising
that it's there. Especially, this function was always in POSIX,
and the early revisions of POSIX are known to have strong
preference for SysV functions, regardless of their quality or
widespread use.
Interestingly, POSIX mentions that memccpy(3) does not check for
overflow.
> The memccpy() function does not check for the overflow of the
> receiving memory area.
This is because the 4th parameter to memccpy(3) is not the size
of the destination buffer, but the size of the source buffer.
It is assumed that the destination buffer is large enough.
This hints that the original (System V) authors of the function
didn't consider copying strings as a use case for this function.
N2349 (2019-03-17; "Toward more efficient string copying and concatenation")
The C Committee, for C23, considered adding new functions for
copying strings. They didn't really had a clear idea of what
use case they intended to cover, and thus didn't really take an
informed decision regarding the design of the function. This
reminds us of the fiasco of Annex K.
This time, at least, they decided to restrict the search to
existing functions, instead of inventing more Annex-K-like
functions. This reduced the risk, but didn't remove it.
There was an inherent desire to add a function for copying
strings with truncation, due to the frustration of the historic
misuses of strncpy(3) and strncat(3) for that use.
However, the paper that discussed this (N2349) --surprisingly--
didn't mention safety in the title.
The proposal seemed to focus on efficiency... except it didn't,
either.
memccpy(3) has been historically not used (see above, 0 uses in
NetBSD, and 2 unique uses in FreeBSD), which has caused that
implementations have very little interest in optimizing it, and
thus is possibly one of the slowest <string.h> functions.
POSIX; stpcpy(3)
N2349 first suggests that
strcat(strcpy(d, s1), s2)
could be written as
memccpy(memccpy(d, s1, '\0', SIZE_MAX) - 1, s2, '\0', SIZE_MAX)
to be more efficient. This is insanely dangerous. I hope the
intention of the paper was to allow compilers to rewrite that
code, and not to suggest that programmers actually write that
code.
But even if this is for code generated by the optimizer, there's
a much more efficient alternative (both in actual
implementations, and in theoretical implementations that are
perfectly optimized), which is to use POSIX's stpcpy(3). This
is the equivalent code using stpcpy(3):
stpcpy(stpcpy(d, s1), s2)
And in this case, this is code that can be written by humans.
The simplicity of the interface makes it both more efficient
and safer.
stpcpy() would be another function interesting for
standardization, but it's less important than the ones above, so
let's ignore it in this first round.
It would certainly allow compilers to generate much more
efficient code, even when the programmer calls strcat(3).
And it would allow programmers writing slightly more ergonomic
code in certain cases.
shadow; stpecpy();; Plan9; strecpy()
Reading N2349 further, one finds an example of copying with
truncation:
char *p = memccpy (d, s1, '\0', dsize);
dsize -= (p - d - 1);
memccpy (p - 1, s2, '\0', dsize);
This is more prone to bugs than the case above, and more than
strncpy(3). Anyone suggesting to use this to improve safety
compared to strncpy(3), please, please, explain to me how they
think this can be safe in any way.
In fact, the code above is completely bogus, because if the
string is truncated, p will be NULL, and it invokes UB in
line 2. See how it was predictably prone to bugs? :)
At the bottom of the N2349 paper, there's a more correct --and
also more worrying-- example of how memccpy(3) could be used for
copying strings with truncation. This shows how terrible
memccpy(3) is for copying strings:
char *p = memccpy (d, s1, '\0', dsize);
if (p) {
--p;
p = memccpy (p, "/", '\0', dsize - (p - d));
if (p) {
--p;
p = memccpy (p, s2, '\0', dsize - (p - d));
}
}
if (!p)
d[dsize - 1] = '\0';
I think I don't need to explain what can go wrong in such
unreadable, brittle, and complex code.
It is evident that memccpy(3) must be removed from the standard.
Using a more suitable function --similar to POSIX's stpcpy(3)--,
this could be written much more safely:
char *p = d;
char *e = d + dsize;
p = stpecpy(p, e, s1);
p = stpecpy(p, e, "/");
p = stpecpy(p, e, s2);
if (p == NULL)
goto trunc; // The string was truncated
Here's how stpecpy() can be implemented for this:
char *
stpecpy(char *dst, const char *end, const char *restrict src)
{
ssize_t dlen;
if (dst == NULL)
return NULL;
dlen = strtcpy(dst, src, end - dst);
if (dlen == -1)
return NULL;
return dst + dlen;
}
With the strtcpy()/strtcat() discussed above, it could be
written as
if (strtcpy(d, s1, dsize) == -1)
goto trunc;
if (strtcat(d, "/", dsize) == -1)
goto trunc;
if (strtcat(d, s2, dsize) == -1)
goto trunc;
stpecpy() was invented by me, and is used in the shadow project.
Plan9 provides a function that is essentially the same, under
the name strecpy(2), although it reports truncation by returning
'end' instead of NULL. NULL is slightly safer, and programmers
related to Plan9 have acknowledged that stpecpy() is slightly
better because of this. Again, independent implementations
(several decades apart) seem to converge to this function.
Like stpcpy(3) compared to strcpy(3)/strcat(3), this stpecpy()
compared to strtcpy()/strtcat() would be another function
interesting for standardization, but less important than
strtcpy()/strtcat(), so let's ignore it in this first round.
It would certainly allow compilers to generate much more
efficient code, even when the programmer calls strtcat().
And it would allow programmers writing slightly more ergonomic
code in certain cases.
OpenBSD; strlcpy(3)/strlcat(3)
OpenBSD invented a set of functions for copying with truncation
(and thus, attempting to cover the same use case that
strtcpy()/strtcat() covers in shadow, or strscpy(9) in Linux).
They invented strlcpy(3)/strlcat(3).
They have two major flaws, though. They copied the conventions
from snprintf(3), which make error handling more difficult to
detect (instead of a simple -1 error code or a NULL pointer,
they return the hypothetical length of the string they tried to
create). Compare:
if (strtcpy(d, s, countof(d)) == -1)
goto trunc;
vs
if (strlcpy(d, s, countof(d)) >= countof(d))
goto trunc;
The second example could be accidentally written with > instead
of >=, which would result in an off-by-one bug.
The other major flaw of strlcpy(3)/strlcat(3) is that it must
read the entire source string, even after the function has
noticed that it will truncate the string. This makes it
vulnerable to denial of service (DoS) if an attacker can control
the length of the source string.
They don't have any advantage that would compensate for these
issues, so they seem inferior to strtcpy()/strtcat().
POSIX standardized this pair of functions in POSIX.1-2024,
mainly because they were the only functions in an existing libc
implementation that copies a string with truncation. This is
because the Linux kernel has strscpy(9), but it is only
available for internal code, and thus it wasn't a candidate for
standardization.
Annex K; strncpy_s()
Annex K is bad, but strncpy_s() is especially bad within
Annex K. It has absolutely no relationship with strncpy(3).
This is probably because Annex K was not written for meeting the
needs of existing code, but rather designed by committee, with
the attempt to remove all sources of UB at any cost.
The group or groups that designed Annex K seems to have picked
strncpy(3), and changed its semantics to make it free of UB,
even if that means it doesn't serve its purpose anymore.
The whole Annex K should be removed from the standard
eventually. However, strncpy_s() should be removed now,
because we're removing badly named string (and nonstring)
copying functions to avoid misuse, and this one is a case of
a badly named function that will mislead users.
Summary
Let's consider now the most essential functions for
standardization:
- strtcpy() (strscpy(9) in Linux)
- strtcat() (the 'cat' of strtcpy())
- memtostr() (the 'cpy' of strncat(3))
- memtostrcat() (strncat(3) renamed)
- strtomem() (strncpy(3) renamed)
These have proved in Linux that they serve to remove *all*
misuses of strncpy(3), (and in shadow, of strncat(3) too).
The reason why they work is that *both* they have an appropriate
name and they cover the use cases that users need.
Let's remove functions that are known to be bad:
- strncpy(3) (actually, rename it to strtomem())
- strncat(3) (actually, rename it to memtostrcat())
- memccpy(3) (this one is just terrible and useless)
- strncpy_s() (Annex K is bad; this is worse than bad)
There are other interesting functions that will cover more use
cases, and improve both efficiency and ergonomics/safety even
further, which should be considered in a second round of
standardization, after the first round proves successful.
For then (maybe in some years from now), let's consider:
- stpcpy(3) ('p' version of strcpy(3))
- stpecpy() ('p' version of strtcpy())
And as a general advice for programmers, please, please, use the
right tools for the job. If those tools are not available in
your system, you should write them! They won't cost you more
than a few lines of code. Misusing other function instead is
just negligence, and will increase the chances of having
important bugs.
Design decisions
We could change the order of parameters to put the size first.
However, because that would cause inconsistency between
<string.h> functions, let's not gamble.
Proposed wording
Based on N3886.
(The wording for wide strings is not yet written. First I want
to circulate a draft to discuss it, and then I'll do the boring
work of specifying the wide-string functions.)
7.28.2 String handling <string.h> :: Copying functions
@@ New subsections after 7.28.2.4 ("The strcpy function")
+7.28.2.<4+1> The <b>strtcpy</b> function
+
+Synopsis
+1
+ #include <string.h>
+ ssize_t
+ strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
+
+Description
+2
+ The <b>strtcpy</b> function
+ copies the string pointed to by <tt>src</tt>
+ (including the terminating null character)
+ into the array pointed to by <tt>dst</tt>.
+ If any characters would cause the copy to
+ write past the end of the array
+ determined by <tt>dst</tt> and <tt>dsize</tt>,
+ those characters are discarded,
+ and a null character is written
+ as the last character in the array.
+ If <tt>dsize</tt> is zero
+ or copying takes place between objects that overlap,
+ the behavior is undefined.
+
+Returns
+3
+ The <b>strtcpy</b> function
+ returns the length of the resulting string in <tt>dst</tt>
+ if all characters were copied,
+ or <tt>-1</tt>
+ if any characters were discarded.
## E2BIG need not be standardized, because -1 is enough to
## determine truncation. It is encouraged that implementations
## also set errno to E2BIG, for consistency. This
## recommendation extends to all <string.h> functions that
## truncate a source string: strtcpy(), strtcat(), strtomem().
## Or maybe EMSGSIZE would be more appropriate? Anyway, not
## something that ISO C should care about.
+7.28.2.<4+2> The <b>memtostr</b> function
+
+Synopsis
+1
+ #include <string.h>
+ char *
+ memtostr(char *restrict dst, const char *restrict src, size_t ssize)
+
+Description
+2
+ The <b>memtostr</b> function
+ copies not more than <tt>ssize</tt> characters
+ (a null character and characters that follow it are not copied)
+ from the array pointed to by <tt>src</tt>
+ into the array pointed to by <tt>dst</tt>.
+ A terminating null character is always appended to the result.
+ XXX)
+ If <tt>dst</tt> is a null pointer value
+ or copying takes place between objects that overlap,
+ the behavior is undefined.
+
+XXX)
+ Thus,
+ the maximum number of characters
+ that can end up in the array pointed to by <tt>dst</tt>
+ is <tt>ssize+1</tt>.
+
+Returns
+3
+ The <b>memtostr</b> function
+ returns the value of <tt>dst</tt>.
7.28.2.5 The strncpy function
@@ Title
-The strncpy function
+The strtomem function
@@ Synopsis, p1
#include <string.h>
char *
- strncpy(char *restrict s1, const char *restrict s2, size_t n)
+ strtomem(char *restrict dst, const char *restrict src, size_t dsize)
@@ Description, p2, p3
-The <b>strncpy</b> function
-copies not more than <tt>n</tt> characters
-(characters that follow a null character are not copied)
-from the array pointed to by <tt>s2</tt>
-to the array pointed to by <tt>s1</tt>.
-329)
+The <b>strtomem</b> function
+copies the string pointed to by <tt>src</tt>
+(excluding the terminating null character)
+into the array pointed to by <tt>dst</tt>.
+If any characters would cause the copy to
+write past the end of the array
+determined by <tt>dst</tt> and <tt>dsize</tt>,
+those characters are discarded.
+Any unused elements of the array pointed to by <tt>dst</tt>
+are filled with null characters.
If copying takes place between objects that overlap,
the behavior is undefined.
-
-If the array pointed to by s2
-is a string that is shorter than n characters,
-null characters are appended
-to the copy in the array pointed to by s1,
-until n characters in all have been written.
## This rewrite doesn't change semantics, but it makes it more
## evident what this function is good for.
@@ Footnote 329
-Thus,
-if there is no null character
-in the first n characters of the array pointed to by s2,
-the result will not be null-terminated.
## This is now evident, both by the name and the new description.
## More importantly, let's remove this comment that could lead
## programmers to think this might be useful for creating
## strings.
@@ Returns, p4
+The <b>strtomem</b> function
+returns the length of the resulting string in <tt>dst</tt>
+if all characters were copied,
+or a null pointer
+if any characters were discarded.
## There's a semantic change here compared to strncpy(3). The
## function now must read byte dsize+1 if a NUL wasn't present
## in the first dsize characters of src, to determine whether
## the source string was longer or not (to determine
## truncation).
7.28.3 String handling <string.h> :: Concatenation functions
@@ New subsection after 7.28.3.1 ("The strcat function")
+7.28.3.<1+1> The <b>strtcat</b> function
+
+Synopsis
+1
+ #include <string.h>
+ ssize_t
+ strtcat(char *restrict dst, const char *restrict src, size_t dsize)
+
+Description
+2
+ The <b>strtcat</b> function
+ appends a copy of the string pointed to by <tt>src</tt>
+ (including the terminating null character)
+ to the end of the string pointed to by <tt>dst</tt>.
+ The initial character of <tt>src</tt>
+ overwrites the null character at the end of <tt>dst</tt>.
+ If any characters would cause the copy to
+ write past the end of the array
+ determined by <tt>dst</tt> and <tt>dsize</tt>,
+ those characters are discarded,
+ and a null character is written
+ as the last character in the array.
+ If <tt>dsize</tt> is zero
+ or copying takes place between objects that overlap,
+ the behavior is undefined.
+
+Returns
+3
+ The <b>strtcat</b> function
+ returns the length of the resulting string in <tt>dst</tt>
+ if all characters were copied,
+ or <tt>-1</tt>
+ if any characters were discarded.
7.28.3.2 The strncat function
@@
s/strncat/memtostrcat/g
s/s1/dst/g
s/s2/src/g
s/\<n\>/ssize/g
7.33.4.3 Wide string copying functions
TODO.
7.33.4.4 Wide string concatenation functions
TODO.
K.3.7.2 Copying functions
@@
-K.3.7.2.4 The strncpy_s function
## Remove the entire K.3.7.2.4 subsection.
## Remove any references to strncpy_s in the entire standard.
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names
2026-07-25 22:09 ` Alejandro Colomar
2026-07-26 23:14 ` alx-0096r1 - string (and nonstring) copying Alejandro Colomar
@ 2026-07-27 2:39 ` Serge E. Hallyn
2026-07-27 7:51 ` Alejandro Colomar
1 sibling, 1 reply; 31+ messages in thread
From: Serge E. Hallyn @ 2026-07-27 2:39 UTC (permalink / raw)
To: Alejandro Colomar
Cc: linux-man, Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
On Sun, Jul 26, 2026 at 12:09:19AM +0200, Alejandro Colomar wrote:
> Hi Serge,
>
> On 2026-07-24T13:52:10-0500, sergeh@kernel.org wrote:
> > On Wed, Jul 08, 2026 at 05:09:10PM +0200, Alejandro Colomar wrote:
> > > Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
> > > Cc: Mark Harris <mark.hsj@gmail.com>
> > > Cc: "G. Branden Robinson" <g.branden.robinson@gmail.com>
> > > Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> > > Signed-off-by: Alejandro Colomar <alx@kernel.org>
> >
> > Most of these look good to me, just a few notes:
>
> Thanks!
>
> [...]
> > > @@ -6,7 +6,7 @@
> > > .\"
> > > .TH strdup 3 (date) "Linux man-pages (unreleased)"
> > > .SH NAME
> > > -strdup, strndup \- duplicate a string
> > > +strdup, strndup \- string duplicate
> >
> > string [bounded] duplicate
> > maybe?
>
> I think for consistency with the other strn*() functions, the following
> would be better:
>
> string [nonstring] duplicate
>
> [...]
> > > @@ -6,7 +6,7 @@
> > > .\"
> > > .TH strfry 3 (date) "Linux man-pages (unreleased)"
> > > .SH NAME
> > > -strfry \- randomize a string
> > > +strfry \- string fry
> >
> > Maybe at least 'string fry (randomize)' ? Because while stirfry
> > is amusing, it's confusing if you haven't heard it before.
>
> Hmmm, randomize seems to generous, and one may think it is kind of
> a shred(1), while it isn't. Maybe 'string fry (reorder)'?
>
> [...]
> > > @@ -5,10 +5,7 @@
> > > .\"
> > > .TH strncat 3 (date) "Linux man-pages (unreleased)"
> > > .SH NAME
> > > -strncat
> > > -\-
> > > -append non-null bytes from a source array to a string,
> > > -and null-terminate the result
> > > +strncat \- nonstring catenate
> >
> > why nonstring? The source string doesn't *have* to be a string,
> > but can be, right?
>
> Yup, it can be a string, although it would be useless (if you want
> a string, you can use strcat(3)).
If the destination is 10 bytes long and has a 5 character string now,
and the source is a valid string that's 15 characters long, it's still
not safe to use strcat.
> > (IIRC, you define a nonstring as an array
> > of given length that doesn't necessarily end in \0? I could be
> > mis-remembering)
>
> Yes, a nonstring is a character array that doesn't necessarily end in
> \0. That's why a string is a valid nonstring, but not the other way
> around.
>
> > string bounded concatente maybe?
>
> Nope; that's what makes people misunderstand these functions, and
> confuse them with safe truncating functions (e.g., strscpy(9)).
>
> I'll send you a copy of a paper I'm writing for the C Committee.
>
> > I think it helps the quick association in the mind if the start
> > of the string matches more closely (str).
>
> In this specific case, it's not a good idea. strn*() are NOT string
> functions.
>
> [...]
> > > @@ -6,7 +6,7 @@
> > > .\"
> > > .TH strnlen 3 (date) "Linux man-pages (unreleased)"
> > > .SH NAME
> > > -strnlen \- determine the length of a fixed-size string
> > > +strnlen \- nonstring length
> >
> > string bounded length?
>
> Nope. It doesn't handle strings.
>
> [...]
> > > @@ -6,7 +6,7 @@
> > > .\"
> > > .TH strpbrk 3 (date) "Linux man-pages (unreleased)"
> > > .SH NAME
> > > -strpbrk \- search a string for any of a set of bytes
> > > +strpbrk \- string search characters
> >
> > does the p stand for returning pointer?
>
> <https://stackoverflow.com/a/501005/6872717>
>
> People say it was "string pointer break". It was a weird name, and
> I ignore why they called it like that. It seems to come from 4.3BSD;
> blame them. :)
>
> I expanded it as if it were called strchrs(), which is what it should
> have been called. It's also the name used in Plan9 internally (in some
> cases).
>
>
> Have a lovely night!
> Alex
>
> --
> <https://www.alejandro-colomar.es>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names
2026-07-27 2:39 ` [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names Serge E. Hallyn
@ 2026-07-27 7:51 ` Alejandro Colomar
0 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-27 7:51 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: linux-man, Mark Harris, G. Branden Robinson, Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 1265 bytes --]
Hi Serge,
On 2026-07-26T21:39:34-0500, Serge E. Hallyn wrote:
> > [...]
> > > > @@ -5,10 +5,7 @@
> > > > .\"
> > > > .TH strncat 3 (date) "Linux man-pages (unreleased)"
> > > > .SH NAME
> > > > -strncat
> > > > -\-
> > > > -append non-null bytes from a source array to a string,
> > > > -and null-terminate the result
> > > > +strncat \- nonstring catenate
> > >
> > > why nonstring? The source string doesn't *have* to be a string,
> > > but can be, right?
> >
> > Yup, it can be a string, although it would be useless (if you want
> > a string, you can use strcat(3)).
>
> If the destination is 10 bytes long and has a 5 character string now,
> and the source is a valid string that's 15 characters long, it's still
> not safe to use strcat.
It's not safe to use strncat(3) either. At least for my high standards.
It's full of off-by-one opportunities, and other opportunities for bugs,
which will result in buffer overflows. :)
One should write and use strtcat(). Here's a patch set for doing that
in shadow, FWIW: <https://github.com/shadow-maint/shadow/pull/1334>.
That patch set is very small, so if you merge it already,
I'd appreciate it.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-26 23:14 ` alx-0096r1 - string (and nonstring) copying Alejandro Colomar
@ 2026-07-28 12:05 ` Mark Harris
2026-07-28 15:04 ` Alejandro Colomar
0 siblings, 1 reply; 31+ messages in thread
From: Mark Harris @ 2026-07-28 12:05 UTC (permalink / raw)
To: Alejandro Colomar
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
Alejandro Colomar wrote:
> Description
> There's a recent push to remove strncpy(3) from the ISO C
> standard without carefully analyzing the consequences of the
> removal. There have been informal messages from committee
> members in the mailing list, and now we have an N document that
> will be heard in the next meeting:
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3935.htm>
>
> N3935 (2026-07-15; "Removing strncpy")
> N3935 contains some truth and good arguments in it, but it is
> mixed with some mistakes too.
>
> Below is an attempt at carefully analyzing the history of this
> function and why it has been misused, and also a thorough
> analysis of the replacement candidates.
>
> Seventh Edition Unix; strncpy(3)
> strncpy(3) was first introduced in Seventh Edition Unix (V7).
>
> It was added for one very specific use case: copying a source
> string into a destination character sequence in a fixed-size
> member of a structure (not a string), and padding the unused
> bytes with '\0'.
>
> This was useful bad then in members of the utmp(5) structure,
> and modern shadow-utils still need this function for dealing
> with utmp(5).
It used to be much more common. For example, in V7 Unix there was
only one filesystem type and no opendir()/readdir(); any user program
wanting to read the contents of a directory would open() it for
reading like any other file and read the sequence of 16-byte directory
entries, each of which is a 2-byte inode number followed by a
fixed-size 14-byte filename. A shorter filename was padded with null
bytes at the end but there was no null if it was exactly 14 bytes (the
maximum length). strncpy() could write this kind of fixed-length
field, and strncat() could append such a filename to a path string.
At https://softwareengineering.stackexchange.com/questions/438025/what-was-the-original-purpose-of-c-strncpy-function/450802#450802
the original author (or so they claim) says that the original
motivation was to handle the fixed length command name in process
accounting records.
>
> While programs should avoid non-NUL-terminated character
> sequences (aka, nonstrings) within fixed-width structure members
> in code that is used only internally, this is sometimes
> necessary in structures that are part of a protocol. Thus,
> while this is a niche use case, it remains something that some
> programs need to do, and need to do reliably and safely.
>
> Another program that needs this functionality is tar(1) (from
> what I've heard).
>
> System V; mem*() functions
> Back in the times of V7, the concept of a string was less
> specific, and what we now call byte arrays, they called byte
> strings. In fact, memcmp(3) was then called bcmp(3). That's
> why all the byte functions are provided in <string.h>.
>
> System V invented the mem*() functions --AFAIK--. They are
> essentially the b*() funcitons from V7, with minor tweaks to the
> parameters and return values. For example, memcpy(3) was made
> consistent with strcpy(3), in that both take the input as $2
> and write to $1. The old bcopy(3) was reversed compared to
> strcpy(3), which was quite confusing and error-prone
> (especially, before 'const' was invented).
It was 4.2 BSD that introduced bcopy/bcmp/bzero in libc, the same year
(1983) that System V introduced the mem*() functions in their libc.
V7 Unix did not have any of these functions in libc, although it did
have a bcopy() function in the V7 kernel so BSD may have been trying
to be compatible with that. 4.3 BSD (1986) then also added the mem*()
functions for System V compatibility. Later the C89 committee chose
to standardize the mem*() functions but moved them from their own
header file memory.h (introduced by System V) to string.h with the
str*() functions.
>
> POSIX says memcmp(3) was first standardized in Issue 1 of the
> SVID (System V Interface Definition), which corroborates that
> these functions were invented in SysV.
>
> It is good that such a renovation of the names, parameters, and
> return values happened.
>
> It would have been good if strncpy(3) and strncat(3) would have
> been renamed (and tweaked) too back then, but it didn't happen.
>
> C89; string
> C89 specified the term 'string' very clearly, in 4.1.1:
>
> A string is a contiguous sequence of characters
> terminated by and including the first null character.
>
> This differed from the old ambiguous meaning. The header file
> for operations on byte arrays remained <string.h> for historical
> reasons, even if it conflicted with this specification of
> string.
When System V introduced the mem*() functions they put them in a new
header file memory.h. Also when 4.3 BSD added the mem*() functions
for System V compatibility, they added a memory.h header file with
these functions and did not add them to string.h. When C89 adopted
these functions they moved them from memory.h to string.h; they did
not "remain" in string.h since they did not start there. Modern Linux
and macOS have a memory.h that includes string.h, for compatibility
with code that may still include memory.h, even though no version of
ISO C or POSIX has ever required a memory.h.
>
> The strn*() functions, strncpy(3) and strncat(3), also remained
> with their names, for historical reasons, even if it conflicted
> with this specification of string.
>
> Other than that, the standard was quite consistent and strict
> with its meaning of the term string.
>
> I believe this consistency --with the exceptions mentioned
> above-- is what led programmers to trust string functions to
> handle strings, and anything with a str*() in the name was
> believed to be good for handling strings. Programmers forgot
> the not-so-consistent history of the term string prior to
> standardization.
>
> In retrospective, the strncpy(3) function would have been better
> called strtomem_pad(), which clearly reflects what it does.
> Maybe that would have made programmers aware of its semantics,
> and it would have significantly reduces misuses of the function.
>
> strncat(3)
> strncat(3) is similar to strncpy(3) --while at the same time,
> very different--. It is actually the opposite of strncpy(3):
> it takes a nonstring as input, and writes a string.
>
> A better name for it would have been memtostr_cat().
>
> Curiously, there's no 'cpy' version of strncat(3). There's
> nothing with the semantics of memtostr() in ISO C, nor in the
> historic Unix systems (V7, BSD, System V).
I think the main source of confusion with strncat() is that for other
str*() and mem*() functions that take a destination buffer and size
argument, the size is the size of the destination buffer (i.e., it
will not write to dst[size] or later), whereas for strncat() the size
only limits how much is read from the source.
So what I find more curious is that ISO C has no string concatenation
function that takes a destination buffer size (such as your
strtcat()). If that is what a person is looking for, the plausible
name and arguments and absence of other contenders can lead them to
conclude that strncat() must be that function.
>
> strn*(); [[gnu::nonstring]]
> In general, the names of strn*() functions are unfortunate,
> because they are better suited for handling nonstrings
> (character sequences that don't fit in the standard
> specification of 'string').
>
> GNU C has the attribute [[gnu::nonstring]] to annotate these
> things.
> <https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-nonstring>
>
> While renaming the functions would be better, one can think of
> the 'n' in strn*() as a mnemonic for NonSTRing. This partially
> mitigates the naming issue. Still, better names would be much
> better:
>
> strncpy() => strtomem_pad()
> strncat() => memtostr_cat()
>
> Linux; strtomem_pad()
> Recently, it appeared in the (fake) news that Linux had
> completely removed all uses of strncpy(3), and banned it in new
> code.
Perhaps clarify that you are referring to the Linux kernel, not Linux glibc.
> ...
> Thus, the functions that should be considered are:
>
> - strtcpy() (strscpy(9) in Linux)
> - memtostr() (the 'cpy' of strncat(3))
> - strtomem() (strncpy(3) renamed)
>
> And for consistency, strncat(3) should be renamed to something
> like memtostrcat().
Given that the entire point of strtomem() and memtostrcat() is to
change a confusing name, I am surprised that the new names you came up
with name mem and str in the opposite order that they apply to the
argument list. That is, memtostr(str, mem, n), introducing a whole
new area of potential confusion that was not present in any existing
mem*() or str*() functions. If reducing potential confusion is that
important I would have expected maybe strfrommem(str, mem, n) or
similar.
>
> strncpy(3), strtomem(), and truncation
> One thing we should consider improving if we'll have a breaking
> change is to report truncation.
>
> strtomem()/strncpy(3) necessarily truncates if the string
> doesn't fit. However, it doesn't report truncation if it
> happens. Since the return value of strtomem()/strncpy(3) isn't
> very useful, we can take advantage and modify it to return an
> error code if there's truncation. Let's return NULL on
> truncation.
>
> shadow; strtcpy(), strncpy(3)
> The shadow project more or less agrees with the Linux kernel in
> this regard. It has implemented strtcpy(), and uses strncpy(3).
> It doesn't have memtostr() yet, because it happens to always
> allocate the buffer at the same time, using strndup(3) --which
> is essentially malloc(3)+memtostr()--, but that will change
> soon, because (non-VLA) arrays are safer, as they allow
> validation of source and destination sizes at compile time.
>
> The implementations are independent, and happened to be
> fundamentally identical. It seems that good implementations of
> string and nonstring copying code converge to this set of
> interfaces.
>
> Here's a naive implementation of strtcpy():
>
> ssize_t
> strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
> {
> bool trunc;
> size_t dlen, slen;
>
> if (dsize == 0) // UB
> abort();
>
> slen = strnlen(src, dsize);
> trunc = (slen == dsize);
> dlen = slen - trunc;
>
> stpcpy(mempcpy(dst, src, dlen), "");
>
> if (trunc) {
> errno = E2BIG;
> return -1;
> }
>
> return slen;
> }
ISO C does not have ssize_t or E2BIG. Also this API does not handle
the case of slen too large for ssize_t, which may not be an issue for
Linux, however ISO C has to support a much wider range of systems.
The same issues apply to strtcat().
>
> shadow; strtcat()
> A cat version of strtcpy() is also useful. The Linux kernel
> seems to be misusing strncat(3), because they took priority on
> addressing misuses of strncpy(3), but it would do good in also
> replacing misuses of strncat(3).
>
> shadow does have strtcat() --although the number of uses is way
> smaller than those of strtcpy()--.
>
> Thus, for consideration:
>
> - strtcpy() (strscpy(9) in Linux)
> - strtcat() (the 'cat' of strtcpy())
> - memtostr() (the 'cpy' of strncat(3))
> - memtostrcat() (strncat(3) renamed)
> - strtomem() (strncpy(3) renamed)
>
> An implementation of strtcat() is:
>
> ssize_t
> strtcat(char *restrict dst, const char *restrict src, size_t dsize)
> {
> char *p, *end;
>
> end = dst + dsize;
>
> p = stpecpy(strnul(dst), end, src);
> if (p == NULL)
> return -1;
>
> return p - dst;
> }
>
> SEI CERT STR32-C
> SEI CERT --which supposedly is a "secure" coding guideline--
> recommends using strncpy(3) for copying a string with
> truncation.
> <https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/characters-and-strings-str/str32-c/#compliant-solution-truncation>
>
> This is the kind of bad teaching that has had the negative
> effects we have today. Programmers misuse tools like strncpy(3)
> --among other reasons-- because they're taught by SEI CERT and
> others to use those tools for something they weren't designed
> for.
>
> SEI CERT STR32-C proposes this code as correct code for copying
> a string with truncation:
>
> size_t func(const char *source) {
> char c_str[STR_SIZE];
> size_t ret = 0;
>
> if (source) {
> strncpy(c_str, source, sizeof(c_str) - 1);
> c_str[sizeof(c_str) - 1] = '\0';
> ret = strlen(c_str);
> } else {
> /* Handle null pointer */
> }
> return ret;
> }
>
> I claim that that code is bogus, and that good code for doing
> that should instead look like this:
>
> if (strtcpy(buf, source, countof(buf)) == -1)
> goto trunc;
>
> That's one line that does one thing and does it well. It takes
> an input string, and copies the string into an output buffer,
> truncating as necessary, not reading more than necessary, and
> not writing more than necessary. It reports truncation if it
> happens, because truncation is usually a bad thing.
>
> For that, one must implement strtcpy() (see above for an
> implementation).
>
> System V; memccpy(3)
> memccpy(3) was invented in System V (like the other mem*()
> functions). The System V sources are not public (AFAIK), so
> it's not known what this function was designed for. It was
> later added to the BSDs and glibc for compatibility with SysV,
> but nobody really knew what this function is good for. It
> doesn't seem ergonomic for any basic functionality.
It is for implementing functions like fgets(), which needs to copy the
next '\n'-delimited line from a buffer filled by read() (so not a
null-terminated string).
>
> Ignoring tests, you can find 0 calls to memccpy(3) in NetBSD,
> and 3 calls in FreeBSD --two of which are in two repeated
> implementations of strncat(3), and the other one is really
> unique, in "bin/sh/parser.c"--.
>
> Debian shows several more uses of memccpy(3) that are not in
> tests, but all of those seem to be newer than C23, so they seem
> to be cases of "if C23 added this, it must be good, let's use
> it" without really analyzing whether that function was actually
> good.
>
> These two unique calls found in NetBSD are *terrible*. By
> *terrible* I mean as bad as misuses of strncpy(3). They show
> how error-prone memccpy(3) is, even for the most natural use
> cases. Here's one of the examples:
>
> if (fmt[0] != '}') {
> char *end;
>
> end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
> if (end == NULL) {
> /*
> * Format too long or no '}', so
> * ignore "\D{" altogether.
> * The loop will do i++, but nothing
> * was written to ps, so do i-- here.
> * Rewind fmt for similar reason.
> */
> i--;
> fmt--;
> break;
> }
> *--end = '\0'; /* Ignore the copy of '}'. */
> fmt += end - tfmt;
> }
>
> This is the most legitimate use case of memccpy(3) that one can
> conceive: we want to copy the leading part of a string until a
> delimiter character is found. And even this use is full of
> opportunities for off-by-one bugs. A better design would have
> not copied the delimiter, allowing the user to decide whether to
> copy it or not (instead of forcing it to go back and remove it,
> which is more complex).
>
> POSIX; memccpy(3)
> POSIX standardized memccpy(3) just because it was in System V.
> POSIX derives from Issue 1 of the SVID. It's not surprising
> that it's there. Especially, this function was always in POSIX,
> and the early revisions of POSIX are known to have strong
> preference for SysV functions, regardless of their quality or
> widespread use.
>
> Interestingly, POSIX mentions that memccpy(3) does not check for
> overflow.
>
> > The memccpy() function does not check for the overflow of the
> > receiving memory area.
>
> This is because the 4th parameter to memccpy(3) is not the size
> of the destination buffer, but the size of the source buffer.
> It is assumed that the destination buffer is large enough.
That is silly; the maximum size applies to both the source and
destination, like memcpy(). Obviously if one is smaller than the
other, you must not pass in a size that is larger than the smaller of
the two, as with memcpy().
>
> This hints that the original (System V) authors of the function
> didn't consider copying strings as a use case for this function.
It's a "mem" function, not a "str" function, so it should be clear
that it is intended for processing byte arrays and not null-terminated
strings. That said, if you wanted to I guess you could use it to
implement a function like your strtcpy() pretty easily:
if (dsize == 0) abort();
char *p = memccpy(dst, src, '\0', dsize);
return p ? p-dst-1 : (dst[dsize-1] = '\0', -1);
Note that this just passes the destination buffer size directly to
memccpy(); unless I missed something this should not allow the
destination buffer to overflow.
I don't see sufficient justification for removing this function.
- Mark
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-28 12:05 ` Mark Harris
@ 2026-07-28 15:04 ` Alejandro Colomar
2026-07-28 15:58 ` Alejandro Colomar
2026-07-29 17:27 ` Mark Harris
0 siblings, 2 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-28 15:04 UTC (permalink / raw)
To: Mark Harris
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 16149 bytes --]
Hi Mark,
Thank you very much for the thorough review!
On 2026-07-28T05:05:51-0700, Mark Harris wrote:
[...]
> > Seventh Edition Unix; strncpy(3)
> > strncpy(3) was first introduced in Seventh Edition Unix (V7).
> >
> > It was added for one very specific use case: copying a source
> > string into a destination character sequence in a fixed-size
> > member of a structure (not a string), and padding the unused
> > bytes with '\0'.
> >
> > This was useful bad then in members of the utmp(5) structure,
> > and modern shadow-utils still need this function for dealing
> > with utmp(5).
>
> It used to be much more common. For example, in V7 Unix there was
> only one filesystem type and no opendir()/readdir(); any user program
> wanting to read the contents of a directory would open() it for
> reading like any other file and read the sequence of 16-byte directory
> entries, each of which is a 2-byte inode number followed by a
> fixed-size 14-byte filename. A shorter filename was padded with null
> bytes at the end but there was no null if it was exactly 14 bytes (the
> maximum length). strncpy() could write this kind of fixed-length
> field, and strncat() could append such a filename to a path string.
Indeed. These days, cheap storage means these 1-byte savings are less
common. They still appear in some cases, but they're significantly less
common.
> At https://softwareengineering.stackexchange.com/questions/438025/what-was-the-original-purpose-of-c-strncpy-function/450802#450802
Hmmmm, thanks for the link! That's interesting.
> the original author (or so they claim) says that the original
> motivation was to handle the fixed length command name in process
> accounting records.
The claim seems credible. He gives some detail that seems likely true:
He says the strncpy(3) functions were previously called strcpyn().
While I can't verify this, there's the macro SCPYN(). I had always
wondered why they reversed the order in the name --the natural thing
would be to call it SNCPY()--. If the original strncpy(3) was called
strcpyn(), this would make sense.
[...]
> > System V; mem*() functions
> > Back in the times of V7, the concept of a string was less
> > specific, and what we now call byte arrays, they called byte
> > strings. In fact, memcmp(3) was then called bcmp(3). That's
> > why all the byte functions are provided in <string.h>.
> >
> > System V invented the mem*() functions --AFAIK--. They are
> > essentially the b*() funcitons from V7, with minor tweaks to the
> > parameters and return values. For example, memcpy(3) was made
> > consistent with strcpy(3), in that both take the input as $2
> > and write to $1. The old bcopy(3) was reversed compared to
> > strcpy(3), which was quite confusing and error-prone
> > (especially, before 'const' was invented).
>
> It was 4.2 BSD that introduced bcopy/bcmp/bzero in libc, the same year
> (1983) that System V introduced the mem*() functions in their libc.
> V7 Unix did not have any of these functions in libc, although it did
> have a bcopy() function in the V7 kernel so BSD may have been trying
> to be compatible with that.
Hmmm, I had seen bcopy() existing there in the source code, and didn't
imagine that it would be a private function for the kernel only.
I also had seen some bcmp():
alx@devuan:~/src/unix/unix/v7$ grep -rn bcmp
usr/src/cmd/sa.c:54: extern tcmp(), ncmp(), bcmp();
usr/src/cmd/sa.c:202: qsort(tab, k, sizeof(tab[0]), nflg? ncmp: (bflg?bcmp:tcmp));
usr/src/cmd/sa.c:345:bcmp(p1, p2)
And I guessed it was the bcmp(3) we know, but now I've checked the
implementation and it was something entirely different.
Thanks for the correction! I'll fix the paper.
> 4.3 BSD (1986) then also added the mem*()
> functions for System V compatibility. Later the C89 committee chose
> to standardize the mem*() functions but moved them from their own
> header file memory.h (introduced by System V) to string.h with the
> str*() functions.
Wow! This is very interesting. So, after all, we shouldn't blame old
Unix systems, but the C Committee!! Another very important issue to add
to the list of bad inventions of the committee. :)
I wish we could re-introduce the <memory.h> header, and move these back
there (of course, with compat declarations in <string.h>).
> > POSIX says memcmp(3) was first standardized in Issue 1 of the
> > SVID (System V Interface Definition), which corroborates that
> > these functions were invented in SysV.
> >
> > It is good that such a renovation of the names, parameters, and
> > return values happened.
> >
> > It would have been good if strncpy(3) and strncat(3) would have
> > been renamed (and tweaked) too back then, but it didn't happen.
> >
> > C89; string
> > C89 specified the term 'string' very clearly, in 4.1.1:
> >
> > A string is a contiguous sequence of characters
> > terminated by and including the first null character.
> >
> > This differed from the old ambiguous meaning. The header file
> > for operations on byte arrays remained <string.h> for historical
> > reasons, even if it conflicted with this specification of
> > string.
>
> When System V introduced the mem*() functions they put them in a new
> header file memory.h. Also when 4.3 BSD added the mem*() functions
> for System V compatibility, they added a memory.h header file with
> these functions and did not add them to string.h. When C89 adopted
> these functions they moved them from memory.h to string.h; they did
> not "remain" in string.h since they did not start there. Modern Linux
> and macOS have a memory.h that includes string.h, for compatibility
> with code that may still include memory.h, even though no version of
> ISO C or POSIX has ever required a memory.h.
Thanks a lot for these details!! These are very important to understand
why this has been so wrong. I'll make sure to fix the paper to include
this history.
> > The strn*() functions, strncpy(3) and strncat(3), also remained
> > with their names, for historical reasons, even if it conflicted
> > with this specification of string.
> >
> > Other than that, the standard was quite consistent and strict
> > with its meaning of the term string.
> >
> > I believe this consistency --with the exceptions mentioned
> > above-- is what led programmers to trust string functions to
> > handle strings, and anything with a str*() in the name was
> > believed to be good for handling strings. Programmers forgot
> > the not-so-consistent history of the term string prior to
> > standardization.
> >
> > In retrospective, the strncpy(3) function would have been better
> > called strtomem_pad(), which clearly reflects what it does.
> > Maybe that would have made programmers aware of its semantics,
> > and it would have significantly reduces misuses of the function.
> >
> > strncat(3)
> > strncat(3) is similar to strncpy(3) --while at the same time,
> > very different--. It is actually the opposite of strncpy(3):
> > it takes a nonstring as input, and writes a string.
> >
> > A better name for it would have been memtostr_cat().
> >
> > Curiously, there's no 'cpy' version of strncat(3). There's
> > nothing with the semantics of memtostr() in ISO C, nor in the
> > historic Unix systems (V7, BSD, System V).
>
> I think the main source of confusion with strncat() is that for other
> str*() and mem*() functions that take a destination buffer and size
> argument, the size is the size of the destination buffer (i.e., it
> will not write to dst[size] or later), whereas for strncat() the size
> only limits how much is read from the source.
Yup. In retrospective, strn*() functions should be treated as a third
class of functions, entirely unrelated to str*() and mem*().
> So what I find more curious is that ISO C has no string concatenation
> function that takes a destination buffer size (such as your
> strtcat()).
I'd say this is reasonable, since it doesn't have strtcpy() either.
> If that is what a person is looking for, the plausible
> name and arguments and absence of other contenders can lead them to
> conclude that strncat() must be that function.
Yup.
[...]
> > Linux; strtomem_pad()
> > Recently, it appeared in the (fake) news that Linux had
> > completely removed all uses of strncpy(3), and banned it in new
> > code.
>
> Perhaps clarify that you are referring to the Linux kernel, not Linux glibc.
Okay.
> > ...
> > Thus, the functions that should be considered are:
> >
> > - strtcpy() (strscpy(9) in Linux)
> > - memtostr() (the 'cpy' of strncat(3))
> > - strtomem() (strncpy(3) renamed)
> >
> > And for consistency, strncat(3) should be renamed to something
> > like memtostrcat().
>
> Given that the entire point of strtomem() and memtostrcat() is to
> change a confusing name, I am surprised that the new names you came up
> with name mem and str in the opposite order that they apply to the
> argument list.
I derived the names from those invented by the kernel; they're not my
invention. I took their names because they were better than the ones
I had been using myself in my code. But I agree your suggestion seems
even better.
> That is, memtostr(str, mem, n), introducing a whole
> new area of potential confusion that was not present in any existing
> mem*() or str*() functions. If reducing potential confusion is that
> important I would have expected maybe strfrommem(str, mem, n) or
> similar.
Hmmmm, interesting suggestion! That makes it resemble the strfromd(3)
family, with which it actually has some (minor) relation. (It might be
a bit
strfrommemcat() would be a weird name, because the cat is next to mem
instead of str. Should it be strcatfrommem()?
[...]
> > shadow; strtcpy(), strncpy(3)
> > The shadow project more or less agrees with the Linux kernel in
> > this regard. It has implemented strtcpy(), and uses strncpy(3).
> > It doesn't have memtostr() yet, because it happens to always
> > allocate the buffer at the same time, using strndup(3) --which
> > is essentially malloc(3)+memtostr()--, but that will change
> > soon, because (non-VLA) arrays are safer, as they allow
> > validation of source and destination sizes at compile time.
> >
> > The implementations are independent, and happened to be
> > fundamentally identical. It seems that good implementations of
> > string and nonstring copying code converge to this set of
> > interfaces.
> >
> > Here's a naive implementation of strtcpy():
> >
> > ssize_t
> > strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
> > {
> > bool trunc;
> > size_t dlen, slen;
> >
> > if (dsize == 0) // UB
> > abort();
> >
> > slen = strnlen(src, dsize);
> > trunc = (slen == dsize);
> > dlen = slen - trunc;
> >
> > stpcpy(mempcpy(dst, src, dlen), "");
> >
> > if (trunc) {
> > errno = E2BIG;
> > return -1;
> > }
> >
> > return slen;
> > }
>
> ISO C does not have ssize_t or E2BIG.
I'm aware; this is a push to add ssize_t. In the wording, I have
specified strtcpy() as not-necessarily setting errno, though, since it's
not strictly necessary.
> Also this API does not handle
> the case of slen too large for ssize_t, which may not be an issue for
> Linux, however ISO C has to support a much wider range of systems.
> The same issues apply to strtcat().
I believe sizes beyond ssize_t are not valid. At least, for a ssize_t
that matches ptrdiff_t. No object may be larger than PTRDIFF_MAX,
because that would mean that pointer subtraction would be undefined:
char *first = a;
char *end = a + sizeof(a);
end - first; // Overflows ptrdiff_t
That's because pointer subtraction happens on ptrdiff_t instead of
size_t. Thus, we don't need to care about sizes larger than SSIZE_MAX.
[...]
> > System V; memccpy(3)
> > memccpy(3) was invented in System V (like the other mem*()
> > functions). The System V sources are not public (AFAIK), so
> > it's not known what this function was designed for. It was
> > later added to the BSDs and glibc for compatibility with SysV,
> > but nobody really knew what this function is good for. It
> > doesn't seem ergonomic for any basic functionality.
>
> It is for implementing functions like fgets(), which needs to copy the
> next '\n'-delimited line from a buffer filled by read() (so not a
> null-terminated string).
Hmmmm, do you have any such code? I'd be interested in reviewing it.
I still suspect it might have been better if it didn't copy the
terminator.
[...]
> > POSIX; memccpy(3)
> > POSIX standardized memccpy(3) just because it was in System V.
> > POSIX derives from Issue 1 of the SVID. It's not surprising
> > that it's there. Especially, this function was always in POSIX,
> > and the early revisions of POSIX are known to have strong
> > preference for SysV functions, regardless of their quality or
> > widespread use.
> >
> > Interestingly, POSIX mentions that memccpy(3) does not check for
> > overflow.
> >
> > > The memccpy() function does not check for the overflow of the
> > > receiving memory area.
> >
> > This is because the 4th parameter to memccpy(3) is not the size
> > of the destination buffer, but the size of the source buffer.
> > It is assumed that the destination buffer is large enough.
>
> That is silly; the maximum size applies to both the source and
> destination, like memcpy(). Obviously if one is smaller than the
> other, you must not pass in a size that is larger than the smaller of
> the two, as with memcpy().
>
> >
> > This hints that the original (System V) authors of the function
> > didn't consider copying strings as a use case for this function.
>
> It's a "mem" function, not a "str" function, so it should be clear
> that it is intended for processing byte arrays and not null-terminated
> strings. That said, if you wanted to I guess you could use it to
> implement a function like your strtcpy() pretty easily:
>
> if (dsize == 0) abort();
> char *p = memccpy(dst, src, '\0', dsize);
> return p ? p-dst-1 : (dst[dsize-1] = '\0', -1);
FWIW, I wouldn't call this 'pretty easily', but this is a bit
subjective. I'd possibly make bugs in the code above.
> Note that this just passes the destination buffer size directly to
> memccpy(); unless I missed something this should not allow the
> destination buffer to overflow.
Your implementation seems correct.
> I don't see sufficient justification for removing this function.
I'd like to review an fgets(3) implementation using memccpy(3) before
making a judgement.
Have a lovely day!
Alex
>
>
> - Mark
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-28 15:04 ` Alejandro Colomar
@ 2026-07-28 15:58 ` Alejandro Colomar
2026-07-29 17:27 ` Mark Harris
1 sibling, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-28 15:58 UTC (permalink / raw)
To: Mark Harris
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 958 bytes --]
Hi Mark,
On 2026-07-28T17:04:20+0200, Alejandro Colomar wrote:
[...]
> > That is, memtostr(str, mem, n), introducing a whole
> > new area of potential confusion that was not present in any existing
> > mem*() or str*() functions. If reducing potential confusion is that
> > important I would have expected maybe strfrommem(str, mem, n) or
> > similar.
>
> Hmmmm, interesting suggestion! That makes it resemble the strfromd(3)
> family, with which it actually has some (minor) relation. (It might be
> a bit
>
> strfrommemcat() would be a weird name, because the cat is next to mem
> instead of str. Should it be strcatfrommem()?
On the other hand, there's precedent in strtol(3) having the arguments
reversed compared to the name. Programmers are used to having the
destination in the first parameter.
Anyway, I'll mention these alternative names in the proposal.
Cheers,
Alex
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
` (3 preceding siblings ...)
2026-07-25 23:50 ` [PATCH v3 4/4] man/man3/[b]string.3: Rewrite and merge Alejandro Colomar
@ 2026-07-29 15:15 ` Alejandro Colomar
4 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-29 15:15 UTC (permalink / raw)
To: linux-man
Cc: Serge E. Hallyn, Mark Harris, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 639 bytes --]
Hi,
> Date: 2026-07-26 01:49:50+0200
> From: Alejandro Colomar <alx@kernel.org>
>
> Here's v3 of this patch set. Changes:
>
> - Move change that accidentally was in patch 2/4 into patch 1/4.
> - Also add strndup expansion in strdup/strndup(3)
> - Clarify what 'fry' means in strfry(3).
> - Add Acked-by Serge.
>
> Alejandro Colomar (4):
> man/man3/str*.3: NAME: Explain the names
> man/man3/strpbrk.3: BUGS: Clarify the NAME
> man/man3/mem*.3: NAME: Explain the names
> man/man3/[b]string.3: Rewrite and merge
I've applied this patch set.
Cheers,
Alex
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-28 15:04 ` Alejandro Colomar
2026-07-28 15:58 ` Alejandro Colomar
@ 2026-07-29 17:27 ` Mark Harris
2026-07-29 19:23 ` Alejandro Colomar
1 sibling, 1 reply; 31+ messages in thread
From: Mark Harris @ 2026-07-29 17:27 UTC (permalink / raw)
To: Alejandro Colomar
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
Alejandro Colomar wrote:
> > 4.3 BSD (1986) then also added the mem*()
> > functions for System V compatibility. Later the C89 committee chose
> > to standardize the mem*() functions but moved them from their own
> > header file memory.h (introduced by System V) to string.h with the
> > str*() functions.
>
> Wow! This is very interesting. So, after all, we shouldn't blame old
> Unix systems, but the C Committee!! Another very important issue to add
> to the list of bad inventions of the committee. :)
>
> I wish we could re-introduce the <memory.h> header, and move these back
> there (of course, with compat declarations in <string.h>).
Since you propose to add functions like memtostr() and strtomem(), I
would think that having them both in the same header file would be
beneficial. Otherwise it would add yet another area of potential
confusion for users: Do I need to include memory.h or string.h for
strtomem()? Does it depend on the source or destination argument? I
thought the goal was to reduce confusion.
> > That is, memtostr(str, mem, n), introducing a whole
> > new area of potential confusion that was not present in any existing
> > mem*() or str*() functions. If reducing potential confusion is that
> > important I would have expected maybe strfrommem(str, mem, n) or
> > similar.
>
> Hmmmm, interesting suggestion! That makes it resemble the strfromd(3)
> family, with which it actually has some (minor) relation. (It might be
> a bit
>
> strfrommemcat() would be a weird name, because the cat is next to mem
> instead of str. Should it be strcatfrommem()?
I think that strcatfrommem() is better than strfrommemcat(). Although
I am not convinced that it is worth changing the name of a
well-established 49-year-old function just because the name may
confuse some people, especially if it may end up just exchanging one
area of confusion for a different one that people haven't even yet
learned to watch out for.
> On the other hand, there's precedent in strtol(3) having the arguments
> reversed compared to the name. Programmers are used to having the
> destination in the first parameter.
strtol() is fine, because str is first in the name and is also the
first argument. What makes memtostr() confusing is that mem is
mentioned first in the name but it is the second argument, and str is
mentioned second but it is the first argument.
>
> [...]
> > > shadow; strtcpy(), strncpy(3)
> > > The shadow project more or less agrees with the Linux kernel in
> > > this regard. It has implemented strtcpy(), and uses strncpy(3).
> > > It doesn't have memtostr() yet, because it happens to always
> > > allocate the buffer at the same time, using strndup(3) --which
> > > is essentially malloc(3)+memtostr()--, but that will change
> > > soon, because (non-VLA) arrays are safer, as they allow
> > > validation of source and destination sizes at compile time.
> > >
> > > The implementations are independent, and happened to be
> > > fundamentally identical. It seems that good implementations of
> > > string and nonstring copying code converge to this set of
> > > interfaces.
> > >
> > > Here's a naive implementation of strtcpy():
> > >
> > > ssize_t
> > > strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
> > > {
> > > bool trunc;
> > > size_t dlen, slen;
> > >
> > > if (dsize == 0) // UB
> > > abort();
> > >
> > > slen = strnlen(src, dsize);
> > > trunc = (slen == dsize);
> > > dlen = slen - trunc;
> > >
> > > stpcpy(mempcpy(dst, src, dlen), "");
> > >
> > > if (trunc) {
> > > errno = E2BIG;
> > > return -1;
> > > }
> > >
> > > return slen;
> > > }
> >
> > ISO C does not have ssize_t or E2BIG.
>
> I'm aware; this is a push to add ssize_t. In the wording, I have
> specified strtcpy() as not-necessarily setting errno, though, since it's
> not strictly necessary.
>
> > Also this API does not handle
> > the case of slen too large for ssize_t, which may not be an issue for
> > Linux, however ISO C has to support a much wider range of systems.
> > The same issues apply to strtcat().
>
> I believe sizes beyond ssize_t are not valid. At least, for a ssize_t
> that matches ptrdiff_t. No object may be larger than PTRDIFF_MAX,
> because that would mean that pointer subtraction would be undefined:
>
> char *first = a;
> char *end = a + sizeof(a);
>
> end - first; // Overflows ptrdiff_t
>
> That's because pointer subtraction happens on ptrdiff_t instead of
> size_t. Thus, we don't need to care about sizes larger than SSIZE_MAX.
ISO C allows objects to have a size up to SIZE_MAX (§ 6.2.5). ISO C
does not limit objects to size PTRDIFF_MAX, it just says that the
pointer subtraction is undefined if the result is not representable in
an object of type ptrdiff_t (§ 6.5.7). It is the responsibility of
your code to avoid undefined behavior. Glibc malloc() deliberately
fails on sizes > PTRDIFF_MAX to help clients to avoid this undefined
behavior, but that is not an ISO C requirement and it is still
possible to obtain such an object in other ways. Also some systems
(e.g. with segmented or banked memory models) have a ptrdiff_t that is
larger than size_t.
>
> [...]
> > > System V; memccpy(3)
> > > memccpy(3) was invented in System V (like the other mem*()
> > > functions). The System V sources are not public (AFAIK), so
> > > it's not known what this function was designed for. It was
> > > later added to the BSDs and glibc for compatibility with SysV,
> > > but nobody really knew what this function is good for. It
> > > doesn't seem ergonomic for any basic functionality.
> >
> > It is for implementing functions like fgets(), which needs to copy the
> > next '\n'-delimited line from a buffer filled by read() (so not a
> > null-terminated string).
>
> Hmmmm, do you have any such code? I'd be interested in reviewing it.
> I still suspect it might have been better if it didn't copy the
> terminator.
Sure.
https://github.com/illumos/illumos-gate/blob/4b44494cae63d4bf0c7d3f34828503e68d1c0e69/usr/src/lib/libc/port/stdio/fgets.c#L44
>
> [...]
> > > POSIX; memccpy(3)
> > > POSIX standardized memccpy(3) just because it was in System V.
> > > POSIX derives from Issue 1 of the SVID. It's not surprising
> > > that it's there. Especially, this function was always in POSIX,
> > > and the early revisions of POSIX are known to have strong
> > > preference for SysV functions, regardless of their quality or
> > > widespread use.
> > >
> > > Interestingly, POSIX mentions that memccpy(3) does not check for
> > > overflow.
> > >
> > > > The memccpy() function does not check for the overflow of the
> > > > receiving memory area.
> > >
> > > This is because the 4th parameter to memccpy(3) is not the size
> > > of the destination buffer, but the size of the source buffer.
> > > It is assumed that the destination buffer is large enough.
> >
> > That is silly; the maximum size applies to both the source and
> > destination, like memcpy(). Obviously if one is smaller than the
> > other, you must not pass in a size that is larger than the smaller of
> > the two, as with memcpy().
> >
> > >
> > > This hints that the original (System V) authors of the function
> > > didn't consider copying strings as a use case for this function.
> >
> > It's a "mem" function, not a "str" function, so it should be clear
> > that it is intended for processing byte arrays and not null-terminated
> > strings. That said, if you wanted to I guess you could use it to
> > implement a function like your strtcpy() pretty easily:
> >
> > if (dsize == 0) abort();
> > char *p = memccpy(dst, src, '\0', dsize);
> > return p ? p-dst-1 : (dst[dsize-1] = '\0', -1);
>
> FWIW, I wouldn't call this 'pretty easily', but this is a bit
> subjective. I'd possibly make bugs in the code above.
It is shorter than your "naive" strtcpy() implementation, and doesn't
depend on an undocumented non-C23 function.
- Mark
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-29 17:27 ` Mark Harris
@ 2026-07-29 19:23 ` Alejandro Colomar
2026-07-30 17:58 ` Mark Harris
0 siblings, 1 reply; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-29 19:23 UTC (permalink / raw)
To: Mark Harris
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 15971 bytes --]
Hi Mark,
> Date: 2026-07-29 10:27:15-0700
> From: Mark Harris <mark.hsj@gmail.com>
>
> Alejandro Colomar wrote:
> > > 4.3 BSD (1986) then also added the mem*()
> > > functions for System V compatibility. Later the C89 committee chose
> > > to standardize the mem*() functions but moved them from their own
> > > header file memory.h (introduced by System V) to string.h with the
> > > str*() functions.
> >
> > Wow! This is very interesting. So, after all, we shouldn't blame old
> > Unix systems, but the C Committee!! Another very important issue to add
> > to the list of bad inventions of the committee. :)
> >
> > I wish we could re-introduce the <memory.h> header, and move these back
> > there (of course, with compat declarations in <string.h>).
>
> Since you propose to add functions like memtostr() and strtomem(), I
> would think that having them both in the same header file would be
> beneficial. Otherwise it would add yet another area of potential
> confusion for users: Do I need to include memory.h or string.h for
> strtomem()?
Actually, I'd use a third header file for these hybrids: <nonstring.h>.
All of the current strn*() functions belong there.
They don't belong in either <memory.h> or <string.h>.
> Does it depend on the source or destination argument? I
> thought the goal was to reduce confusion.
>
> > > That is, memtostr(str, mem, n), introducing a whole
> > > new area of potential confusion that was not present in any existing
> > > mem*() or str*() functions. If reducing potential confusion is that
> > > important I would have expected maybe strfrommem(str, mem, n) or
> > > similar.
> >
> > Hmmmm, interesting suggestion! That makes it resemble the strfromd(3)
> > family, with which it actually has some (minor) relation. (It might be
> > a bit
> >
> > strfrommemcat() would be a weird name, because the cat is next to mem
> > instead of str. Should it be strcatfrommem()?
>
> I think that strcatfrommem() is better than strfrommemcat().
Agree.
> Although
> I am not convinced that it is worth changing the name of a
> well-established 49-year-old function just because the name may
> confuse some people, especially if it may end up just exchanging one
> area of confusion for a different one that people haven't even yet
> learned to watch out for.
>
> > On the other hand, there's precedent in strtol(3) having the arguments
> > reversed compared to the name. Programmers are used to having the
> > destination in the first parameter.
>
> strtol() is fine, because str is first in the name and is also the
> first argument.
Ouch, I was wrong. Every time I read the strtol() manual page, I get
confused by the 'nptr' argument, which looks like it'd be a pointer in
which to store the number. I'll change it to 's'.
> What makes memtostr() confusing is that mem is
> mentioned first in the name but it is the second argument, and str is
> mentioned second but it is the first argument.
>
> > [...]
> > > > shadow; strtcpy(), strncpy(3)
> > > > The shadow project more or less agrees with the Linux kernel in
> > > > this regard. It has implemented strtcpy(), and uses strncpy(3).
> > > > It doesn't have memtostr() yet, because it happens to always
> > > > allocate the buffer at the same time, using strndup(3) --which
> > > > is essentially malloc(3)+memtostr()--, but that will change
> > > > soon, because (non-VLA) arrays are safer, as they allow
> > > > validation of source and destination sizes at compile time.
> > > >
> > > > The implementations are independent, and happened to be
> > > > fundamentally identical. It seems that good implementations of
> > > > string and nonstring copying code converge to this set of
> > > > interfaces.
> > > >
> > > > Here's a naive implementation of strtcpy():
> > > >
> > > > ssize_t
> > > > strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
> > > > {
> > > > bool trunc;
> > > > size_t dlen, slen;
> > > >
> > > > if (dsize == 0) // UB
> > > > abort();
> > > >
> > > > slen = strnlen(src, dsize);
> > > > trunc = (slen == dsize);
> > > > dlen = slen - trunc;
> > > >
> > > > stpcpy(mempcpy(dst, src, dlen), "");
> > > >
> > > > if (trunc) {
> > > > errno = E2BIG;
> > > > return -1;
> > > > }
> > > >
> > > > return slen;
> > > > }
> > >
> > > ISO C does not have ssize_t or E2BIG.
> >
> > I'm aware; this is a push to add ssize_t. In the wording, I have
> > specified strtcpy() as not-necessarily setting errno, though, since it's
> > not strictly necessary.
> >
> > > Also this API does not handle
> > > the case of slen too large for ssize_t, which may not be an issue for
> > > Linux, however ISO C has to support a much wider range of systems.
> > > The same issues apply to strtcat().
> >
> > I believe sizes beyond ssize_t are not valid. At least, for a ssize_t
> > that matches ptrdiff_t. No object may be larger than PTRDIFF_MAX,
> > because that would mean that pointer subtraction would be undefined:
> >
> > char *first = a;
> > char *end = a + sizeof(a);
> >
> > end - first; // Overflows ptrdiff_t
> >
> > That's because pointer subtraction happens on ptrdiff_t instead of
> > size_t. Thus, we don't need to care about sizes larger than SSIZE_MAX.
>
> ISO C allows objects to have a size up to SIZE_MAX (§ 6.2.5). ISO C
> does not limit objects to size PTRDIFF_MAX, it just says that the
> pointer subtraction is undefined if the result is not representable in
> an object of type ptrdiff_t (§ 6.5.7). It is the responsibility of
> your code to avoid undefined behavior. Glibc malloc() deliberately
> fails on sizes > PTRDIFF_MAX to help clients to avoid this undefined
> behavior, but that is not an ISO C requirement and it is still
> possible to obtain such an object in other ways.
Yup.
> Also some systems
> (e.g. with segmented or banked memory models) have a ptrdiff_t that is
> larger than size_t.
I'm interested in those systems. Do they support POSIX.1-2024 and/or
ISO C23? We were discussing the possibility of standardizing ssize_t in
ISO C, and also adding a requirement that it's as large as size_t, and
also potentially adding a requirement that it's the same type as
ptrdiff_t.
It would be good to know if those systems that have a larger ptrdiff_t
are still alive or can be ignored/forced to adapt.
> > [...]
> > > > System V; memccpy(3)
> > > > memccpy(3) was invented in System V (like the other mem*()
> > > > functions). The System V sources are not public (AFAIK), so
> > > > it's not known what this function was designed for. It was
> > > > later added to the BSDs and glibc for compatibility with SysV,
> > > > but nobody really knew what this function is good for. It
> > > > doesn't seem ergonomic for any basic functionality.
> > >
> > > It is for implementing functions like fgets(), which needs to copy the
> > > next '\n'-delimited line from a buffer filled by read() (so not a
> > > null-terminated string).
> >
> > Hmmmm, do you have any such code? I'd be interested in reviewing it.
> > I still suspect it might have been better if it didn't copy the
> > terminator.
>
> Sure.
> https://github.com/illumos/illumos-gate/blob/4b44494cae63d4bf0c7d3f34828503e68d1c0e69/usr/src/lib/libc/port/stdio/fgets.c#L44
Thanks!
Let's now consider an alternative world where there was a function with
semantics similar to memccpy(3) with one difference: it wouldn't copy
the delimiter. Let's call such a function memcpyc(). Here's a naive
implementation of memccpy(3):
void *
memccpy(void *dst, const void *src, int c, size_t n)
{
unsigned char *d = dst;
unsigned char *s = src;
for (size_t i = 0; i < n; i++) {
*d++ = *s++;
if (*s == c)
return d;
}
return NULL;
}
Here's a naive implementation of the hypothetical memcpyc():
void *
memcpyc(void *dst, const void *src, int c, size_t n)
{
unsigned char *d = dst;
unsigned char *s = src;
for (size_t i = 0; i < n; i++) {
if (*s == c)
return d;
*d++ = *s++;
}
return NULL;
}
(I've written both of the above quickly, without compiling nor testing;
I might have made a mistake.)
With such a function, the fgets(3) implementation from Illumos gate
would change in this manner:
diff --git i/usr/src/lib/libc/port/stdio/fgets.c w/usr/src/lib/libc/port/stdio/fgets.c
index 9401217410ca..ac1f330ea46e 100644
--- i/usr/src/lib/libc/port/stdio/fgets.c
+++ w/usr/src/lib/libc/port/stdio/fgets.c
@@ -82,9 +82,12 @@ fgets(char *buf, int size, FILE *iop)
break; /* nothing left to read */
}
n = (int)(size < iop->_cnt ? size : iop->_cnt);
- if ((p = memccpy(ptr, (char *)iop->_ptr, '\n',
+ if ((p = memcpyc(ptr, (char *)iop->_ptr, '\n',
(size_t)n)) != NULL)
+ {
+ p = stpcpy(p, "\n");
n = (int)(p - ptr);
+ }
ptr += n;
iop->_cnt -= n;
iop->_ptr += n;
IMO, this wouldn't make it more complex. However, such a hypothetical
memcpyc() would be much more useful elsewhere. For example, it would
simplify the two unique uses of memccpy(3) in FreeBSD:
diff --git i/bin/sh/parser.c w/bin/sh/parser.c
index 0c1b7a91c257..713a35ad8cd8 100644
--- i/bin/sh/parser.c
+++ w/bin/sh/parser.c
@@ -2116,7 +2116,7 @@ getprompt(void *unused __unused)
if (fmt[0] != '}') {
char *end;
- end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
+ end = memcpyc(tfmt, fmt, '}', sizeof(tfmt));
if (end == NULL) {
/*
* Format too long or no '}', so
@@ -2129,7 +2129,7 @@ getprompt(void *unused __unused)
fmt--;
break;
}
- *--end = '\0'; /* Ignore the copy of '}'. */
+ *end = '\0'; /* Ignore the copy of '}'. */
fmt += end - tfmt;
}
now = localtime(&(time_t){time(NULL)});
diff --git i/lib/libc/amd64/string/strncat.c w/lib/libc/amd64/string/strncat.c
index 2c63ab50b3c3..4a1e8c0119ac 100644
--- i/lib/libc/amd64/string/strncat.c
+++ w/lib/libc/amd64/string/strncat.c
@@ -19,13 +19,13 @@ strncat(char *dest, const char *src, size_t n)
char *endptr;
len = strlen(dest);
- endptr = __memccpy(dest + len, src, '\0', n);
+ endptr = memcpyc(dest + len, src, '\0', n);
/* avoid an extra branch */
if (endptr == NULL)
- endptr = dest + len + n + 1;
+ endptr = dest + len + n;
- endptr[-1] = '\0';
+ *endptr = '\0';
return (dest);
}
memccpy(3) is a bad design, which forces complex code, prone to
off-by-one bugs.
> > [...]
> > > > POSIX; memccpy(3)
> > > > POSIX standardized memccpy(3) just because it was in System V.
> > > > POSIX derives from Issue 1 of the SVID. It's not surprising
> > > > that it's there. Especially, this function was always in POSIX,
> > > > and the early revisions of POSIX are known to have strong
> > > > preference for SysV functions, regardless of their quality or
> > > > widespread use.
> > > >
> > > > Interestingly, POSIX mentions that memccpy(3) does not check for
> > > > overflow.
> > > >
> > > > > The memccpy() function does not check for the overflow of the
> > > > > receiving memory area.
> > > >
> > > > This is because the 4th parameter to memccpy(3) is not the size
> > > > of the destination buffer, but the size of the source buffer.
> > > > It is assumed that the destination buffer is large enough.
> > >
> > > That is silly; the maximum size applies to both the source and
> > > destination, like memcpy(). Obviously if one is smaller than the
> > > other, you must not pass in a size that is larger than the smaller of
> > > the two, as with memcpy().
> > >
> > > >
> > > > This hints that the original (System V) authors of the function
> > > > didn't consider copying strings as a use case for this function.
> > >
> > > It's a "mem" function, not a "str" function, so it should be clear
> > > that it is intended for processing byte arrays and not null-terminated
> > > strings. That said, if you wanted to I guess you could use it to
> > > implement a function like your strtcpy() pretty easily:
> > >
> > > if (dsize == 0) abort();
> > > char *p = memccpy(dst, src, '\0', dsize);
> > > return p ? p-dst-1 : (dst[dsize-1] = '\0', -1);
> >
> > FWIW, I wouldn't call this 'pretty easily', but this is a bit
> > subjective. I'd possibly make bugs in the code above.
>
> It is shorter than your "naive" strtcpy() implementation,
I can golf too, but lines of code isn't a good measure of simplicity,
unless you limit to certain common guidelines such as avoiding that
tricky comma operator in the third line.
Ignoring the abort() line, which is identical to my implementation, I'd
write it as this, for a fair comparison:
char *p;
p = memccpy(dst, src, '\0', dsize);
if (p == NULL) {
dst[dsize-1] = '\0';
return -1;
}
return p-dst-1;
That's only slightly shorter than my implementation of strtcpy() with
strnlen(3)+mempcpy(3) (ignoring also errno).
Moreover, it's full of '-1's, which give plenty of opportunities of
making a mistake (adding one '-1' too much, or forgetting to put one),
and resulting in a bug.
> and doesn't
> depend on an undocumented non-C23 function.
You mean mempcpy(3), I guess. I am working on standardizing it. In any
case, you can use good ol' memcpy(3). After all, it's as simple as
this:
void *
mempcpy(void *dest, const void *src, size_t n)
{
return (char *)memcpy(dest, src, n) + n;
}
Here's an implementation using exlusively old POSIX functions:
ssize_t
strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
{
bool trunc;
size_t dlen, slen;
if (dsize == 0) // UB
abort();
slen = strnlen(src, dsize);
trunc = (slen == dsize);
dlen = slen - trunc;
strcpy(memcpy(dst, src, dlen) + dlen, "");
if (trunc) {
errno = E2BIG;
return -1;
}
return slen;
}
Now, let's do something interesting: let's see how this would be
implemented with the memcpyc() I've implemented above.
char *p, *nul;
p = memcpyc(dst, src, '\0', dsize);
nul = p ?: dst+dsize-1;
strcpy(nul, "");
return p ? p-dst : -1;
It still contains one '-1' too much for my taste, but it'd be more
acceptable. Actually, memcpyc() would be better for implementing
stpecpy() --similar to Plan9's strecpy(2)--:
char *
stpecpy(char *dst, const char *end, const char *restrict src)
{
char *p;
if (dst == NULL)
return NULL;
p = memcpyc(dst, src, '\0', end - dst);
strcpy(p ?: end-1, "");
return p;
}
And then this stpecpy() could be used for implementing strtcpy():
ssize_t
strtcpy(char *restrict dst, const char *restrict src, size_t dsize)
{
char *p;
p = stpecpy(dst, dst + dsize, src);
return p ? p-dst : -1;
}
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-29 19:23 ` Alejandro Colomar
@ 2026-07-30 17:58 ` Mark Harris
2026-07-30 19:51 ` Alejandro Colomar
0 siblings, 1 reply; 31+ messages in thread
From: Mark Harris @ 2026-07-30 17:58 UTC (permalink / raw)
To: Alejandro Colomar
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
Alejandro Colomar wrote:
> > Also some systems
> > (e.g. with segmented or banked memory models) have a ptrdiff_t that is
> > larger than size_t.
>
> I'm interested in those systems. Do they support POSIX.1-2024 and/or
> ISO C23? We were discussing the possibility of standardizing ssize_t in
> ISO C, and also adding a requirement that it's as large as size_t, and
> also potentially adding a requirement that it's the same type as
> ptrdiff_t.
>
> It would be good to know if those systems that have a larger ptrdiff_t
> are still alive or can be ignored/forced to adapt.
SDCC (https://sdcc.sourceforge.net/), when building for mcs51
(-mmcs51) or ds390 (-mds390), uses a 16-bit size_t and 32-bit
ptrdiff_t. It appears that Open Watcom C/16 v2
(https://github.com/open-watcom/open-watcom-v2) with the huge memory
model also uses a 16-bit size_t and a 32-bit ptrdiff_t, although I
haven't verified that one. Both compilers have active development and
support some of the new features in C23, but do not have full C23 or
POSIX support. I don't know about any others with ptrdiff_t larger
than size_t.
>
> > > [...]
> > > > > System V; memccpy(3)
> > > > > memccpy(3) was invented in System V (like the other mem*()
> > > > > functions). The System V sources are not public (AFAIK), so
> > > > > it's not known what this function was designed for. It was
> > > > > later added to the BSDs and glibc for compatibility with SysV,
> > > > > but nobody really knew what this function is good for. It
> > > > > doesn't seem ergonomic for any basic functionality.
> > > >
> > > > It is for implementing functions like fgets(), which needs to copy the
> > > > next '\n'-delimited line from a buffer filled by read() (so not a
> > > > null-terminated string).
> > >
> > > Hmmmm, do you have any such code? I'd be interested in reviewing it.
> > > I still suspect it might have been better if it didn't copy the
> > > terminator.
> >
> > Sure.
> > https://github.com/illumos/illumos-gate/blob/4b44494cae63d4bf0c7d3f34828503e68d1c0e69/usr/src/lib/libc/port/stdio/fgets.c#L44
>
> Thanks!
>
> Let's now consider an alternative world where there was a function with
> semantics similar to memccpy(3) with one difference: it wouldn't copy
> the delimiter. Let's call such a function memcpyc(). Here's a naive
> implementation of memccpy(3):
>
> void *
> memccpy(void *dst, const void *src, int c, size_t n)
> {
> unsigned char *d = dst;
> unsigned char *s = src;
>
> for (size_t i = 0; i < n; i++) {
> *d++ = *s++;
> if (*s == c)
> return d;
> }
> return NULL;
> }
>
> Here's a naive implementation of the hypothetical memcpyc():
>
> void *
> memcpyc(void *dst, const void *src, int c, size_t n)
> {
> unsigned char *d = dst;
> unsigned char *s = src;
>
> for (size_t i = 0; i < n; i++) {
> if (*s == c)
> return d;
> *d++ = *s++;
> }
> return NULL;
> }
>
> (I've written both of the above quickly, without compiling nor testing;
> I might have made a mistake.)
>
> With such a function, the fgets(3) implementation from Illumos gate
> would change in this manner:
>
> diff --git i/usr/src/lib/libc/port/stdio/fgets.c w/usr/src/lib/libc/port/stdio/fgets.c
> index 9401217410ca..ac1f330ea46e 100644
> --- i/usr/src/lib/libc/port/stdio/fgets.c
> +++ w/usr/src/lib/libc/port/stdio/fgets.c
> @@ -82,9 +82,12 @@ fgets(char *buf, int size, FILE *iop)
> break; /* nothing left to read */
> }
> n = (int)(size < iop->_cnt ? size : iop->_cnt);
> - if ((p = memccpy(ptr, (char *)iop->_ptr, '\n',
> + if ((p = memcpyc(ptr, (char *)iop->_ptr, '\n',
> (size_t)n)) != NULL)
> + {
> + p = stpcpy(p, "\n");
> n = (int)(p - ptr);
> + }
> ptr += n;
> iop->_cnt -= n;
> iop->_ptr += n;
>
> IMO, this wouldn't make it more complex. However, such a hypothetical
> memcpyc() would be much more useful elsewhere. For example, it would
> simplify the two unique uses of memccpy(3) in FreeBSD:
>
> diff --git i/bin/sh/parser.c w/bin/sh/parser.c
> index 0c1b7a91c257..713a35ad8cd8 100644
> --- i/bin/sh/parser.c
> +++ w/bin/sh/parser.c
> @@ -2116,7 +2116,7 @@ getprompt(void *unused __unused)
> if (fmt[0] != '}') {
> char *end;
>
> - end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
> + end = memcpyc(tfmt, fmt, '}', sizeof(tfmt));
> if (end == NULL) {
> /*
> * Format too long or no '}', so
> @@ -2129,7 +2129,7 @@ getprompt(void *unused __unused)
> fmt--;
> break;
> }
> - *--end = '\0'; /* Ignore the copy of '}'. */
> + *end = '\0'; /* Ignore the copy of '}'. */
> fmt += end - tfmt;
> }
> now = localtime(&(time_t){time(NULL)});
> diff --git i/lib/libc/amd64/string/strncat.c w/lib/libc/amd64/string/strncat.c
> index 2c63ab50b3c3..4a1e8c0119ac 100644
> --- i/lib/libc/amd64/string/strncat.c
> +++ w/lib/libc/amd64/string/strncat.c
> @@ -19,13 +19,13 @@ strncat(char *dest, const char *src, size_t n)
> char *endptr;
>
> len = strlen(dest);
> - endptr = __memccpy(dest + len, src, '\0', n);
> + endptr = memcpyc(dest + len, src, '\0', n);
>
> /* avoid an extra branch */
> if (endptr == NULL)
> - endptr = dest + len + n + 1;
> + endptr = dest + len + n;
>
> - endptr[-1] = '\0';
> + *endptr = '\0';
>
> return (dest);
> }
>
> memccpy(3) is a bad design, which forces complex code, prone to
> off-by-one bugs.
It seems like you've already made up your mind that memccpy() is bad
and are now just looking to justify that decision.
Your modification of fgets() is obviously more complex; it adds a
whole additional function call, and instead of copying all of the
bytes at once from a single source it does two copies from two
different sources, specifying the delimiter twice making it more
error-prone and difficult to change. Both fgets() and strncat() need
the delimiter in the destination buffer, and the one place that
doesn't is only "simplified" by removing a single "--"; certainly not
worth a whole new function. And if simplicity or eliminating the
+/-1s was important to the author of that strncat() implementation,
they could trivially achieve those goals with the existing function:
size_t len = strlen(dest);
if (__memccpy(dest + len, src, '\0', n) == NULL)
dest[len+n] = '\0';
return dest;
Changing that to use memcpyc() would have made it more complex.
I think that copying the delimiter makes sense. For example, you
should be able to use this function directly in a loop, e.g. to
process '\n'-delimited lines. If it stopped before the delimiter it
would need extra code to skip over the delimiter before the next
iteration to avoid getting stuck there. But if the final line didn't
have a '\n' at the end it would have to be sure not to skip over it to
avoid advancing past the end of the buffer.
Even if this hypothetical memcpyc() function was slightly better I
don't see how that matters. memccpy() has been around for decades, is
already in ISO C, is already in POSIX, has already been implemented in
numerous C libraries even before ISO C existed, and is in use in
non-buggy ways, so I don't see it going away in the foreseeable
future. The purpose of the standard is to allow for confident
reliance on a defined base set of features, and removing functions
from that set that are in use and have no fundamental flaws erodes
that confidence. memcpyc() has no existing uses, and also little
expected use, so it would be difficult to imagine there being enough
support to add it to ISO C.
- Mark
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-30 17:58 ` Mark Harris
@ 2026-07-30 19:51 ` Alejandro Colomar
2026-07-30 20:02 ` Alejandro Colomar
0 siblings, 1 reply; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-30 19:51 UTC (permalink / raw)
To: Mark Harris
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 8696 bytes --]
Hi Mark,
> Date: 2026-07-30 10:58:26-0700
> From: Mark Harris <mark.hsj@gmail.com>
>
[...]
> > It would be good to know if those systems that have a larger ptrdiff_t
> > are still alive or can be ignored/forced to adapt.
>
> SDCC (https://sdcc.sourceforge.net/), when building for mcs51
> (-mmcs51) or ds390 (-mds390), uses a 16-bit size_t and 32-bit
> ptrdiff_t. It appears that Open Watcom C/16 v2
> (https://github.com/open-watcom/open-watcom-v2) with the huge memory
> model also uses a 16-bit size_t and a 32-bit ptrdiff_t, although I
> haven't verified that one. Both compilers have active development and
> support some of the new features in C23, but do not have full C23 or
> POSIX support. I don't know about any others with ptrdiff_t larger
> than size_t.
Thanks; I guess that can't be done.
[...]
> > > https://github.com/illumos/illumos-gate/blob/4b44494cae63d4bf0c7d3f34828503e68d1c0e69/usr/src/lib/libc/port/stdio/fgets.c#L44
> >
> > Thanks!
> >
> > Let's now consider an alternative world where there was a function with
> > semantics similar to memccpy(3) with one difference: it wouldn't copy
> > the delimiter. Let's call such a function memcpyc(). Here's a naive
> > implementation of memccpy(3):
> >
> > void *
> > memccpy(void *dst, const void *src, int c, size_t n)
> > {
> > unsigned char *d = dst;
> > unsigned char *s = src;
> >
> > for (size_t i = 0; i < n; i++) {
> > *d++ = *s++;
> > if (*s == c)
> > return d;
> > }
> > return NULL;
> > }
> >
> > Here's a naive implementation of the hypothetical memcpyc():
> >
> > void *
> > memcpyc(void *dst, const void *src, int c, size_t n)
> > {
> > unsigned char *d = dst;
> > unsigned char *s = src;
> >
> > for (size_t i = 0; i < n; i++) {
> > if (*s == c)
> > return d;
> > *d++ = *s++;
> > }
> > return NULL;
> > }
> >
> > (I've written both of the above quickly, without compiling nor testing;
> > I might have made a mistake.)
> >
> > With such a function, the fgets(3) implementation from Illumos gate
> > would change in this manner:
> >
> > diff --git i/usr/src/lib/libc/port/stdio/fgets.c w/usr/src/lib/libc/port/stdio/fgets.c
> > index 9401217410ca..ac1f330ea46e 100644
> > --- i/usr/src/lib/libc/port/stdio/fgets.c
> > +++ w/usr/src/lib/libc/port/stdio/fgets.c
> > @@ -82,9 +82,12 @@ fgets(char *buf, int size, FILE *iop)
> > break; /* nothing left to read */
> > }
> > n = (int)(size < iop->_cnt ? size : iop->_cnt);
> > - if ((p = memccpy(ptr, (char *)iop->_ptr, '\n',
> > + if ((p = memcpyc(ptr, (char *)iop->_ptr, '\n',
> > (size_t)n)) != NULL)
> > + {
> > + p = stpcpy(p, "\n");
> > n = (int)(p - ptr);
> > + }
> > ptr += n;
> > iop->_cnt -= n;
> > iop->_ptr += n;
> >
> > IMO, this wouldn't make it more complex. However, such a hypothetical
> > memcpyc() would be much more useful elsewhere. For example, it would
> > simplify the two unique uses of memccpy(3) in FreeBSD:
> >
> > diff --git i/bin/sh/parser.c w/bin/sh/parser.c
> > index 0c1b7a91c257..713a35ad8cd8 100644
> > --- i/bin/sh/parser.c
> > +++ w/bin/sh/parser.c
> > @@ -2116,7 +2116,7 @@ getprompt(void *unused __unused)
> > if (fmt[0] != '}') {
> > char *end;
> >
> > - end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
> > + end = memcpyc(tfmt, fmt, '}', sizeof(tfmt));
> > if (end == NULL) {
> > /*
> > * Format too long or no '}', so
> > @@ -2129,7 +2129,7 @@ getprompt(void *unused __unused)
> > fmt--;
> > break;
> > }
> > - *--end = '\0'; /* Ignore the copy of '}'. */
> > + *end = '\0'; /* Ignore the copy of '}'. */
> > fmt += end - tfmt;
> > }
> > now = localtime(&(time_t){time(NULL)});
> > diff --git i/lib/libc/amd64/string/strncat.c w/lib/libc/amd64/string/strncat.c
> > index 2c63ab50b3c3..4a1e8c0119ac 100644
> > --- i/lib/libc/amd64/string/strncat.c
> > +++ w/lib/libc/amd64/string/strncat.c
> > @@ -19,13 +19,13 @@ strncat(char *dest, const char *src, size_t n)
> > char *endptr;
> >
> > len = strlen(dest);
> > - endptr = __memccpy(dest + len, src, '\0', n);
> > + endptr = memcpyc(dest + len, src, '\0', n);
> >
> > /* avoid an extra branch */
> > if (endptr == NULL)
> > - endptr = dest + len + n + 1;
> > + endptr = dest + len + n;
> >
> > - endptr[-1] = '\0';
> > + *endptr = '\0';
> >
> > return (dest);
> > }
> >
> > memccpy(3) is a bad design, which forces complex code, prone to
> > off-by-one bugs.
>
> It seems like you've already made up your mind that memccpy() is bad
> and are now just looking to justify that decision.
Not necessarily. I can be convinced with good arguments. :-)
> Your modification of fgets() is obviously more complex; it adds a
> whole additional function call, and instead of copying all of the
> bytes at once from a single source it does two copies from two
> different sources, specifying the delimiter twice making it more
> error-prone and difficult to change. Both fgets() and strncat() need
> the delimiter in the destination buffer, and the one place that
> doesn't is only "simplified" by removing a single "--"; certainly not
> worth a whole new function. And if simplicity or eliminating the
> +/-1s was important to the author of that strncat() implementation,
> they could trivially achieve those goals with the existing function:
>
> size_t len = strlen(dest);
> if (__memccpy(dest + len, src, '\0', n) == NULL)
> dest[len+n] = '\0';
> return dest;
>
> Changing that to use memcpyc() would have made it more complex.
Agreed; the above is a good implementation of strncat(3), and better
than with memcpyc(). Thanks!
> I think that copying the delimiter makes sense. For example, you
> should be able to use this function directly in a loop, e.g. to
> process '\n'-delimited lines. If it stopped before the delimiter it
> would need extra code to skip over the delimiter before the next
> iteration to avoid getting stuck there. But if the final line didn't
> have a '\n' at the end it would have to be sure not to skip over it to
> avoid advancing past the end of the buffer.
Agreed; there are use cases where memccpy(3) is preferrable. You've
convinced me. :)
> Even if this hypothetical memcpyc() function was slightly better I
> don't see how that matters. memccpy() has been around for decades, is
> already in ISO C, is already in POSIX, has already been implemented in
> numerous C libraries even before ISO C existed, and is in use in
> non-buggy ways, so I don't see it going away in the foreseeable
> future. The purpose of the standard is to allow for confident
> reliance on a defined base set of features, and removing functions
> from that set that are in use and have no fundamental flaws erodes
> that confidence. memcpyc() has no existing uses, and also little
> expected use, so it would be difficult to imagine there being enough
> support to add it to ISO C.
Agreed.
Have a lovely night!
Alex
>
>
> - Mark
>
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: alx-0096r1 - string (and nonstring) copying
2026-07-30 19:51 ` Alejandro Colomar
@ 2026-07-30 20:02 ` Alejandro Colomar
0 siblings, 0 replies; 31+ messages in thread
From: Alejandro Colomar @ 2026-07-30 20:02 UTC (permalink / raw)
To: Mark Harris
Cc: sergeh, linux-man, Serge E. Hallyn, G. Branden Robinson,
Douglas McIlroy
[-- Attachment #1: Type: text/plain, Size: 8295 bytes --]
> Date: 2026-07-30 21:51:16+0200
> From: Alejandro Colomar <alx@kernel.org>
>
> Hi Mark,
>
> > Date: 2026-07-30 10:58:26-0700
> > From: Mark Harris <mark.hsj@gmail.com>
> >
> [...]
> > > It would be good to know if those systems that have a larger ptrdiff_t
> > > are still alive or can be ignored/forced to adapt.
> >
> > SDCC (https://sdcc.sourceforge.net/), when building for mcs51
> > (-mmcs51) or ds390 (-mds390), uses a 16-bit size_t and 32-bit
> > ptrdiff_t. It appears that Open Watcom C/16 v2
> > (https://github.com/open-watcom/open-watcom-v2) with the huge memory
> > model also uses a 16-bit size_t and a 32-bit ptrdiff_t, although I
> > haven't verified that one. Both compilers have active development and
> > support some of the new features in C23, but do not have full C23 or
> > POSIX support. I don't know about any others with ptrdiff_t larger
> > than size_t.
>
> Thanks; I guess that can't be done.
>
> [...]
> > > > https://github.com/illumos/illumos-gate/blob/4b44494cae63d4bf0c7d3f34828503e68d1c0e69/usr/src/lib/libc/port/stdio/fgets.c#L44
> > >
> > > Thanks!
> > >
> > > Let's now consider an alternative world where there was a function with
> > > semantics similar to memccpy(3) with one difference: it wouldn't copy
> > > the delimiter. Let's call such a function memcpyc(). Here's a naive
> > > implementation of memccpy(3):
> > >
> > > void *
> > > memccpy(void *dst, const void *src, int c, size_t n)
> > > {
> > > unsigned char *d = dst;
> > > unsigned char *s = src;
> > >
> > > for (size_t i = 0; i < n; i++) {
> > > *d++ = *s++;
> > > if (*s == c)
> > > return d;
> > > }
> > > return NULL;
> > > }
> > >
> > > Here's a naive implementation of the hypothetical memcpyc():
> > >
> > > void *
> > > memcpyc(void *dst, const void *src, int c, size_t n)
> > > {
> > > unsigned char *d = dst;
> > > unsigned char *s = src;
> > >
> > > for (size_t i = 0; i < n; i++) {
> > > if (*s == c)
> > > return d;
> > > *d++ = *s++;
> > > }
> > > return NULL;
> > > }
> > >
> > > (I've written both of the above quickly, without compiling nor testing;
> > > I might have made a mistake.)
> > >
> > > With such a function, the fgets(3) implementation from Illumos gate
> > > would change in this manner:
> > >
> > > diff --git i/usr/src/lib/libc/port/stdio/fgets.c w/usr/src/lib/libc/port/stdio/fgets.c
> > > index 9401217410ca..ac1f330ea46e 100644
> > > --- i/usr/src/lib/libc/port/stdio/fgets.c
> > > +++ w/usr/src/lib/libc/port/stdio/fgets.c
> > > @@ -82,9 +82,12 @@ fgets(char *buf, int size, FILE *iop)
> > > break; /* nothing left to read */
> > > }
> > > n = (int)(size < iop->_cnt ? size : iop->_cnt);
> > > - if ((p = memccpy(ptr, (char *)iop->_ptr, '\n',
> > > + if ((p = memcpyc(ptr, (char *)iop->_ptr, '\n',
> > > (size_t)n)) != NULL)
> > > + {
> > > + p = stpcpy(p, "\n");
> > > n = (int)(p - ptr);
> > > + }
> > > ptr += n;
> > > iop->_cnt -= n;
> > > iop->_ptr += n;
> > >
> > > IMO, this wouldn't make it more complex. However, such a hypothetical
> > > memcpyc() would be much more useful elsewhere. For example, it would
> > > simplify the two unique uses of memccpy(3) in FreeBSD:
> > >
> > > diff --git i/bin/sh/parser.c w/bin/sh/parser.c
> > > index 0c1b7a91c257..713a35ad8cd8 100644
> > > --- i/bin/sh/parser.c
> > > +++ w/bin/sh/parser.c
> > > @@ -2116,7 +2116,7 @@ getprompt(void *unused __unused)
> > > if (fmt[0] != '}') {
> > > char *end;
> > >
> > > - end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
> > > + end = memcpyc(tfmt, fmt, '}', sizeof(tfmt));
> > > if (end == NULL) {
> > > /*
> > > * Format too long or no '}', so
> > > @@ -2129,7 +2129,7 @@ getprompt(void *unused __unused)
> > > fmt--;
> > > break;
> > > }
> > > - *--end = '\0'; /* Ignore the copy of '}'. */
> > > + *end = '\0'; /* Ignore the copy of '}'. */
> > > fmt += end - tfmt;
> > > }
> > > now = localtime(&(time_t){time(NULL)});
> > > diff --git i/lib/libc/amd64/string/strncat.c w/lib/libc/amd64/string/strncat.c
> > > index 2c63ab50b3c3..4a1e8c0119ac 100644
> > > --- i/lib/libc/amd64/string/strncat.c
> > > +++ w/lib/libc/amd64/string/strncat.c
> > > @@ -19,13 +19,13 @@ strncat(char *dest, const char *src, size_t n)
> > > char *endptr;
> > >
> > > len = strlen(dest);
> > > - endptr = __memccpy(dest + len, src, '\0', n);
> > > + endptr = memcpyc(dest + len, src, '\0', n);
> > >
> > > /* avoid an extra branch */
> > > if (endptr == NULL)
> > > - endptr = dest + len + n + 1;
> > > + endptr = dest + len + n;
> > >
> > > - endptr[-1] = '\0';
> > > + *endptr = '\0';
> > >
> > > return (dest);
> > > }
> > >
> > > memccpy(3) is a bad design, which forces complex code, prone to
> > > off-by-one bugs.
> >
> > It seems like you've already made up your mind that memccpy() is bad
> > and are now just looking to justify that decision.
>
> Not necessarily. I can be convinced with good arguments. :-)
>
> > Your modification of fgets() is obviously more complex; it adds a
> > whole additional function call, and instead of copying all of the
FWIW, that additional function call is optimized out by GCC even on low
optimization levels, since it's a two-byte copy. You could perfectly
use a single-byte manual copy:
diff --git i/usr/src/lib/libc/port/stdio/fgets.c w/usr/src/lib/libc/port/stdio/fgets.c
index 9401217410ca..ac1f330ea46e 100644
--- i/usr/src/lib/libc/port/stdio/fgets.c
+++ w/usr/src/lib/libc/port/stdio/fgets.c
@@ -82,9 +82,12 @@ fgets(char *buf, int size, FILE *iop)
break; /* nothing left to read */
}
n = (int)(size < iop->_cnt ? size : iop->_cnt);
- if ((p = memccpy(ptr, (char *)iop->_ptr, '\n',
+ if ((p = memcpyc(ptr, (char *)iop->_ptr, '\n',
(size_t)n)) != NULL)
+ {
+ *p++ = '\n';
n = (int)(p - ptr);
+ }
ptr += n;
iop->_cnt -= n;
iop->_ptr += n;
Nevertheless, with your examples of fgets(3) and strncat(3), I'm now
convinced that memccpy(3) is good for this niche use case.
> > bytes at once from a single source it does two copies from two
> > different sources, specifying the delimiter twice making it more
> > error-prone and difficult to change.
This is true, though.
Cheers,
Alex
--
<https://www.alejandro-colomar.es>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2026-07-30 20:02 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-02 12:33 [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Alejandro Colomar
2026-07-02 12:33 ` [PATCH v1 2/2] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
2026-07-03 5:03 ` [PATCH v1 1/2] man/man3/str*.3: NAME: Explain the names Mark Harris
2026-07-03 11:16 ` Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 0/4] str*.3, mem*.3: " Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 1/4] man/man3/str*.3: " Alejandro Colomar
2026-07-24 18:52 ` sergeh
2026-07-25 22:09 ` Alejandro Colomar
2026-07-26 23:14 ` alx-0096r1 - string (and nonstring) copying Alejandro Colomar
2026-07-28 12:05 ` Mark Harris
2026-07-28 15:04 ` Alejandro Colomar
2026-07-28 15:58 ` Alejandro Colomar
2026-07-29 17:27 ` Mark Harris
2026-07-29 19:23 ` Alejandro Colomar
2026-07-30 17:58 ` Mark Harris
2026-07-30 19:51 ` Alejandro Colomar
2026-07-30 20:02 ` Alejandro Colomar
2026-07-27 2:39 ` [PATCH v2 1/4] man/man3/str*.3: NAME: Explain the names Serge E. Hallyn
2026-07-27 7:51 ` Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
2026-07-24 18:53 ` sergeh
2026-07-25 22:10 ` Alejandro Colomar
2026-07-08 15:09 ` [PATCH v2 3/4] man/man3/mem*.3: NAME: Explain the names Alejandro Colomar
2026-07-24 18:55 ` sergeh
2026-07-08 15:09 ` [PATCH v2 4/4] man/man3/[b]string.3: Rewrite and merge Alejandro Colomar
2026-07-25 23:49 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
2026-07-25 23:49 ` [PATCH v3 1/4] man/man3/str*.3: " Alejandro Colomar
2026-07-25 23:50 ` [PATCH v3 2/4] man/man3/strpbrk.3: BUGS: Clarify the NAME Alejandro Colomar
2026-07-25 23:50 ` [PATCH v3 3/4] man/man3/mem*.3: NAME: Explain the names Alejandro Colomar
2026-07-25 23:50 ` [PATCH v3 4/4] man/man3/[b]string.3: Rewrite and merge Alejandro Colomar
2026-07-29 15:15 ` [PATCH v3 0/4] str*.3, mem*.3: NAME: Explain the names Alejandro Colomar
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.