[PATCH 4/4] stpncpy(3) fixes

Paul Eggert
2023-11-12T23:52:33Z

Don't say "width" when "size" was meant.
Prefer "byte" to the confusing word "character".
Don't say that the source is a string; it need not be a string.
Don't imply the result always has some null padding.
Prefer standalone terminology.
Fix indenting of prototype.
Improve sample implementation by using memset rather than
the less-standard bzero, and by not overwriting part of
the destination more than once which is confusing.
Simplify example without losing its lessons.
Use fwrite instead of printf to avoid assuming buffer length fits in int;
although obviously this buffer length does fit, it's better if the sample
code is general.
---
man3/stpncpy.3 | 82 ++++++++++++++++++++++++--------------------------
1 file changed, 40 insertions(+), 42 deletions(-)

diff --git a/man3/stpncpy.3 b/man3/stpncpy.3
index 3cf4eb371..afe230307 100644
--- a/man3/stpncpy.3
+++ b/man3/stpncpy.3
@@ -7,8 +7,8 @@
.SH NAME
stpncpy, strncpy
\-
-fill a fixed-width buffer with characters from a string
-and pad with null bytes
+fill a fixed-size buffer with non-null bytes from a source array,
+padding with null bytes as needed
.SH LIBRARY
Standard C library
.RI ( libc ", " \-lc )
@@ -18,10 +18,10 @@ Standard C library
.P
.BI "char *strncpy(char " dst "[restrict ." sz "], \
const char *restrict " src ,
-.BI " size_t " sz );
+.BI " size_t " sz );
.BI "char *stpncpy(char " dst "[restrict ." sz "], \
const char *restrict " src ,
-.BI " size_t " sz );
+.BI " size_t " sz );
.fi
.P
.RS -4
@@ -37,15 +37,18 @@ Feature Test Macro Requirements for glibc (see
_GNU_SOURCE
.fi
.SH DESCRIPTION
-These functions copy characters from the string pointed to by
+These functions copy non-null bytes from the array pointed to by
.I src
-into a character sequence at the fixed-width buffer pointed to by
-.IR dst ,
-and pad with null bytes.
-If the destination buffer,
-limited by its size,
-isn't large enough to hold the copy,
-the resulting character sequence is truncated.
+into the array that is pointed to by
+.I dst
+and that contains
+.I sz
+bytes.
+If the source has too few non-null bytes to fill the destination,
+the functions pad the destination with trailing null bytes;
+if it has too many non-null bytes, the functions copy only the first
+.I sz
+bytes and do not append any null by5tes.
For the difference between the two functions, see RETURN VALUE.
.P
An implementation of these functions might be:
@@ -62,8 +65,8 @@ strncpy(char *restrict dst, const char *restrict src, size_t sz)
char *
stpncpy(char *restrict dst, const char *restrict src, size_t sz)
{
- bzero(dst, sz);
- return mempcpy(dst, src, strnlen(src, sz));
+ size_t n = strnlen(src, sz);
+ return memset(mempcpy(dst, src, n), 0, sz - n);
}
.EE
.in
@@ -75,7 +78,7 @@ returns
.TP
.BR stpncpy ()
returns a pointer to
-one after the last character in the destination character sequence.
+one after the last byte in the destination byte sequence.
.SH ATTRIBUTES
For an explanation of the terms used in this section, see
.BR attributes (7).
@@ -107,9 +110,10 @@ C89, POSIX.1-2001, SVr4, 4.3BSD.
glibc 1.07.
POSIX.1-2008.
.SH CAVEATS
-The name of these functions is confusing.
-These functions produce a null-padded character sequence,
-not a string (see
+The names of these functions are confusing.
+Because these functions append null bytes only if the source is a
+string with length less than the destination size,
+they might not create a string in their destination (see
.BR string_copying (7)).
For example:
.P
@@ -122,14 +126,12 @@ strncpy(buf, "123456", 5); // { \[aq]1\[aq], \[aq]2\[aq], \[aq]3\[aq], \[aq]4\[
.EE
.in
.P
-It's impossible to distinguish truncation by the result of the call,
-from a character sequence that just fits the destination buffer;
-truncation should be detected by
-comparing the length of the input string
-with the size of the destination buffer.
+Although these functions can be used with strings,
+it is the caller's responsibility to detect whether they produce a string,
+e.g., by checking whether the result buffer ends in a null byte.
.P
-If you're going to use this function in chained calls,
-it would be useful to develop a similar function that accepts
+To use these functions in chained calls,
+it might be useful to develop wrapper functions that accept
a pointer to the end (one after the last element) of the destination buffer
instead of its size.
.SH EXAMPLES
@@ -141,30 +143,26 @@ instead of its size.
#include <string.h>
\&
int
-main(void)
+main(int argc, char **argv)
{
- char *p;
- char buf1[20];
- char buf2[20];
- size_t len;
+ char buf[20];
\&
- if (sizeof(buf2) < strlen("Hello world!"))
- warnx("strncpy: truncating character sequence");
- strncpy(buf2, "Hello world!", sizeof(buf2));
- len = strnlen(buf2, sizeof(buf2));
+ if (strncpy(buf, argv[0], sizeof buf)[sizeof buf - 1])
+ warnx("strncpy: destination buffer is not a string");
+ size_t len = strnlen(buf, sizeof buf);
\&
printf("[len = %zu]: ", len);
- printf("%.*s\en", (int) len, buf2); // "Hello world!"
+ fwrite(buf, 1, len, stdout);
+ putchar(\[aq]\en\[aq]);
\&
- if (sizeof(buf1) < strlen("Hello world!"))
- warnx("stpncpy: truncating character sequence");
- p = stpncpy(buf1, "Hello world!", sizeof(buf1));
- len = p \- buf1;
+ char *p = stpncpy(buf, argv[0], sizeof buf);
+ if (p == buf + sizeof buf)
+ warnx("stpncpy: destination buffer is not a string");
+ len = p \- buf;
\&
printf("[len = %zu]: ", len);
- printf("%.*s\en", (int) len, buf1); // "Hello world!"
-\&
- exit(EXIT_SUCCESS);
+ fwrite(buf, 1, len, stdout);
+ putchar(\[aq]\en\[aq]);
}
.EE
.\" SRC END
--
2.41.0