Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Code Reviews

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

50%
+1 −1
Code Reviews stpecpy(): Design a better string copy function that truncates

After addressing @Lundin 's suggestions: Implemented in terms of libc functions for performance. const correctness style improvements I have a few more that I realized after trying to repla...

posted 2y ago by alx‭  ·  edited 2y ago by alx‭

Answer
#10: Post edited by user avatar alx‭ · 2022-12-10T22:14:28Z (almost 2 years ago)
ffix
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have a few more that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • It's more readable at call site (due to alignment) when the `past_end` pointer is the second parameter to the function.
  • So, I tweaked it to have the following definition:
  • ```c
  • char *
  • stpecpy(char *dst, char past_end[0],
  • const char *restrict src)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " world");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " foo");
  • p = stpecpy(p, past_end, "");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " baar");
  • p = stpecpy(p, past_end, "");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "H");
  • p = stpecpy(p, past_end, "W");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have a few more that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • It's more readable at call site (due to alignment) when the `past_end` pointer is the second parameter to the function.
  • So, I tweaked it to have the following definition:
  • ```c
  • char *
  • stpecpy(char *dst, char past_end[0], const char *restrict src)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " world");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " foo");
  • p = stpecpy(p, past_end, "");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " baar");
  • p = stpecpy(p, past_end, "");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "H");
  • p = stpecpy(p, past_end, "W");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
#9: Post edited by user avatar alx‭ · 2022-12-10T22:13:06Z (almost 2 years ago)
reorder params
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one more that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked it to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have a few more that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • It's more readable at call site (due to alignment) when the `past_end` pointer is the second parameter to the function.
  • So, I tweaked it to have the following definition:
  • ```c
  • char *
  • stpecpy(char *dst, char past_end[0],
  • const char *restrict src)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " world");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " foo");
  • p = stpecpy(p, past_end, "");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "Hello");
  • p = stpecpy(p, past_end, " baar");
  • p = stpecpy(p, past_end, "");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • p = buf;
  • p = stpecpy(p, past_end, "H");
  • p = stpecpy(p, past_end, "W");
  • if (p == past_end) {
  • p--;
  • puts("Following string is truncated.");
  • }
  • len = p - buf;
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
#8: Post edited by user avatar alx‭ · 2022-12-06T01:26:18Z (almost 2 years ago)
revert last change. ***cat() functions have the common meaning of appending after finding a NUL; this one doesn't.
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have a couple that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • And, strlcpy(3) is fine for copying; this function only improves the status quo for concatenating, so it's better to name it appropriately: `stpecat()`.
  • So, I tweaked it to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecat(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cat() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecat(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecat(stpecat(stpecat(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecat(stpecat(stpecat(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecat(stpecat(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one more that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked it to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
#7: Post edited by user avatar alx‭ · 2022-12-06T00:03:53Z (almost 2 years ago)
Rename to stpecat()
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have a couple that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • And, strlcpy(3) is fine for copying; this function only improves the status quo for concatenating, so it's better to name it appropriately: `stpecat()`.
  • So, I tweaked it to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecat(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cat() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecat(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecat(stpecat(stpecat(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecat(stpecat(stpecat(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecat(stpecat(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
#6: Post edited by user avatar alx‭ · 2022-12-01T17:39:02Z (almost 2 years ago)
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```c
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
#5: Post edited by user avatar alx‭ · 2022-12-01T17:35:26Z (almost 2 years ago)
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", past_end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
#4: Post edited by user avatar alx‭ · 2022-12-01T17:29:14Z (almost 2 years ago)
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte. It's only the last parameter that has changed to be a pointer one-past-the-array, which is nicer as a sentinel.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte on success, or one-past-the-array on truncation. It's only the last parameter that has changed to be a pointer one-past-the-array, which is simpler to get for users.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
#3: Post edited by user avatar alx‭ · 2022-12-01T17:28:14Z (almost 2 years ago)
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • To be clear, the return pointer is still the same: the pointer to the NUL byte. It's only the last parameter that has changed to be a pointer one-past-the-array, which is nicer as a sentinel.
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
#2: Post edited by user avatar alx‭ · 2022-12-01T17:26:22Z (almost 2 years ago)
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • len = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", past_end), "W", past_end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • After addressing @Lundin 's suggestions:
  • - Implemented in terms of libc functions for performance.
  • - const correctness
  • - style improvements
  • I have one that I realized after trying to replace some existing code:
  • Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`). That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.
  • So, I tweaked `stpecpy()` to have the following definition:
  • ```c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • const char *_Nonnull restrict src,
  • char *_Nonnull past_end)
  • {
  • char *p;
  • if (dst == past_end)
  • return past_end;
  • p = memccpy(dst, src, '\0', past_end - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • past_end[-1] = '\0';
  • return past_end;
  • }
  • ```
  • The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.
  • Now the use is slightly simpler:
  • ```c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *past_end, *p;
  • ptrdiff_t len;
  • past_end = &buf[size];
  • p = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • p = stpecpy(stpecpy(buf, "H", past_end), "W", past_end);
  • len = p - buf;
  • if (p == past_end) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
#1: Initial revision by user avatar alx‭ · 2022-12-01T17:20:10Z (almost 2 years ago)
After addressing @Lundin 's suggestions:

- Implemented in terms of libc functions for performance.
- const correctness
- style improvements

I have one that I realized after trying to replace some existing code:

Much code out there has `char *end = buf + nitems(buf);` (although that should really be called `past_end`).  That's certainly easier to calculate than the real `end` that I used here, since you don't need to stick that `-1` all around, and therefore has less chance of an accident.

So, I tweaked `stpecpy()` to have the following definition:

```c
char *_Nonnull
stpecpy(char *_Nonnull dst,
        const char *_Nonnull restrict src,
        char *_Nonnull past_end)
{
	char *p;

	if (dst == past_end)
		return past_end;

	p = memccpy(dst, src, '\0', past_end - dst);
	if (p != NULL)
		return p - 1;

	/* truncation detected */
	past_end[-1] = '\0';
	return past_end;
}
```

The name of the function st**pe**cpy() seems to also have nice mnemonics for `past_end`.

Now the use is slightly simpler:

```c
int
main(void)
{
	ptrdiff_t  size = 10;
	char       buf[size];
	char      *past_end;
	ptrdiff_t  len;

	past_end = &buf[size];

	len = stpecpy(stpecpy(buf, "Hello", past_end), " world", past_end) - buf;
	if (len == size) {
		len--;
		puts("Following string is truncated.");
	}
	printf("%ti: %s\n", len, buf);

	len = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " foo", end), "", past_end) - buf;
	if (len == size) {
		len--;
		puts("Following string is truncated.");
	}
	printf("%ti: %s\n", len, buf);

	len = stpecpy(stpecpy(stpecpy(buf, "Hello", past_end), " baar", end), "", past_end) - buf;
	if (len == size) {
		len--;
		puts("Following string is truncated.");
	}
	printf("%ti: %s\n", len, buf);

	len = stpecpy(stpecpy(buf, "H", past_end), "W", past_end) - buf;
	if (len == size) {
		len--;
		puts("Following string is truncated.");
	}
	printf("%ti: %s\n", len, buf);
}
```