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

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

I was directed a few days ago to a post about a string copy function, which IMO improves the commonly known string copy functions, including strlcpy(3BSD), strlcat(3BSD), and strscpy(9). It define...

2 answers  ·  posted 3y ago by alx‭  ·  last activity 2y ago by alx‭

#16: Post edited by user avatar alx‭ · 2022-12-09T00:30:17Z (almost 2 years ago)
  • stpecpy(): Design a better string copy function
  • stpecpy(): Design a better string copy function that truncates
#15: Nominated for promotion by user avatar Alexei‭ · 2022-03-06T08:16:52Z (over 2 years ago)
#14: Post edited by user avatar alx‭ · 2022-02-17T21:11:24Z (almost 3 years ago)
ffix
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • To be used as:
  • ```
  • end = buf + sizeof(buf) - 1;
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == sizeof(buf)) {
  • len--;
  • handle_trunc();
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end + 1 - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ``` c
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ``` c
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • To be used as:
  • ``` c
  • end = buf + sizeof(buf) - 1;
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == sizeof(buf)) {
  • len--;
  • handle_trunc();
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ``` c
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ``` c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ``` c
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end + 1 - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
#13: Post edited by user avatar alx‭ · 2022-02-15T18:31:41Z (almost 3 years ago)
show len usage
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • To be used as:
  • ```
  • end = buf + sizeof(buf) - 1;
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == sizeof(buf))
  • handle_trunc();
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end + 1 - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • To be used as:
  • ```
  • end = buf + sizeof(buf) - 1;
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == sizeof(buf)) {
  • len--;
  • handle_trunc();
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end + 1 - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
#12: Post edited by user avatar alx‭ · 2022-02-15T18:30:51Z (almost 3 years ago)
Add usage
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end + 1 - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • To be used as:
  • ```
  • end = buf + sizeof(buf) - 1;
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == sizeof(buf))
  • handle_trunc();
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end + 1 - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
#11: Post edited by user avatar alx‭ · 2022-02-15T18:07:45Z (almost 3 years ago)
Symmetry
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end - dst + 1);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end + 1 - dst);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
#10: Post edited by user avatar alx‭ · 2022-02-14T01:44:54Z (almost 3 years ago)
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end - dst + 1);
  • if (p != NULL)
  • return p - 1;
  • *end = '\0';
  • return end + 1;
  • }
  • ```
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end - dst + 1);
  • if (p != NULL)
  • return p - 1;
  • /* truncation detected */
  • *end = '\0';
  • return end + 1;
  • }
  • ```
#9: Post edited by user avatar alx‭ · 2022-02-14T01:42:52Z (almost 3 years ago)
Add implementation in terms of memccpy(3)
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • ____
  • **EDIT**
  • Add implementation in terms of simple libc functions ([memccpy(3)](https://www.man7.org/linux/man-pages/man3/memccpy.3.html)):
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst,
  • char *_Nonnull restrict src,
  • char *_Nonnull end)
  • {
  • char *p;
  • p = memccpy(dst, src, '\0', end - dst + 1);
  • if (p != NULL)
  • return p - 1;
  • *end = '\0';
  • return end + 1;
  • }
  • ```
#8: Post edited by user avatar alx‭ · 2022-02-13T17:56:42Z (almost 3 years ago)
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions, the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions (`_Nonnull`), the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
#7: Post edited by user avatar alx‭ · 2022-02-13T17:53:14Z (almost 3 years ago)
const, restrict, _Nonnull
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • ____
  • **EDIT**
  • After applying Lundin's advice, and also extending it with useful Clang extensions, the prototype of the function would be:
  • ```
  • char *_Nonnull
  • stpecpy(char *_Nonnull dst, const char *_Nonnull restrict src, char *_Nonnull end);
  • ```
#6: Post edited by user avatar alx‭ · 2022-02-13T17:40:01Z (almost 3 years ago)
Don't call stpsecpy() as st[rp]ncpy(3), which would be misleading.
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpsecpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
#5: Post edited by user avatar alx‭ · 2022-02-13T00:34:28Z (almost 3 years ago)
clarify that stpencpy() creates a string
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), but that's out of this code review.
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), while ensuring that a true string is created in _dst_, but that's out of this code review.
#4: Post edited by user avatar alx‭ · 2022-02-13T00:30:07Z (almost 3 years ago)
tfix
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), but that's out of this code review.
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), but that's out of this code review.
#3: Post edited by user avatar alx‭ · 2022-02-12T23:58:59Z (almost 3 years ago)
Remove spurious 'static' from tests
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • static char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), but that's out of this code review.
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), but that's out of this code review.
#2: Post edited by user avatar alx‭ · 2022-02-12T23:55:49Z (almost 3 years ago)
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • static char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).
  • It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.
  • It is to be used as:
  • ```
  • past_end = buf + sizeof(buf);
  • len = strecopy(buf, "Hello", past_end), " world", past_end) - buf;
  • ```
  • It improves chaining compared to the other mentioned functions.
  • However, I found some details that I think could be improved in that function. It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).
  • So, I designed the following function, which I'd like to get reviewed. I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).
  • ```
  • static char *
  • stpecpy(char *dst, char *src, char *end)
  • {
  • for (/* void */; dst <= end; dst++) {
  • *dst = *src++;
  • if (!*dst)
  • return dst;
  • }
  • /* truncation detected */
  • *end = '\0';
  • return dst;
  • }
  • ```
  • _dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions. _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).
  • It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).
  • When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:
  • - Don't need to add a check for `NULL` at the beginning of the function.
  • - Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`. `NULL - buf` is Undefined Behavior.
  • However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some... But I think it's still best to do that.
  • A test for the function:
  • ```
  • int
  • main(void)
  • {
  • ptrdiff_t size = 10;
  • char buf[size];
  • char *end;
  • ptrdiff_t len;
  • end = &buf[size - 1];
  • len = stpecpy(stpecpy(buf, "Hello", end), " world", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " baar", end), "", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
  • if (len == size) {
  • len--;
  • puts("Following string is truncated.");
  • }
  • printf("%ti: %s\n", len, buf);
  • }
  • ```
  • Which prints:
  • ```
  • $ ./a.out
  • Following string is truncated.
  • 9: Hello wor
  • 9: Hello foo
  • Following string is truncated.
  • 9: Hello baa
  • 2: HW
  • ```
  • So, what are your thoughts about this copy function?
  • As a similar function, `stpencpy()` could be also added, if it is needed, for copying from non-strings (character arrays), but that's out of this code review.
#1: Initial revision by user avatar alx‭ · 2022-02-12T23:52:35Z (almost 3 years ago)
stpecpy(): Design a better string copy function
I was directed a few days ago to a [post](https://www.symas.com/post/the-sad-state-of-c-strings) about a string copy function, which IMO improves the commonly known string copy functions, including [_strlcpy(3BSD)_](https://man.openbsd.org/strlcat), [_strlcat(3BSD)_](https://man.openbsd.org/strlcpy), and [_strscpy(9)_](https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html).

It defines a function, `char *strecopy(char *dst, char *src, char *end)`, where _end_ really means **one past the end** of the array, and the function always returns the pointer to the **NUL** byte of the _dst_ string.

It is to be used as:
```
past_end = buf + sizeof(buf);
len = strecopy(buf, "Hello", past_end), " world", past_end) - buf;
```

It improves chaining compared to the other mentioned functions.

However, I found some details that I think could be improved in that function.  It can't detect overflow (one needs to calculate it as `len != (strlen("Hello") + strlen(" world"))`).

So, I designed the following function, which I'd like to get reviewed.  I named it _stpecpy()_; the 'p' as in [_stpcpy(3)_](https://www.man7.org/linux/man-pages/man3/stpcpy.3.html), since both return a **p**ointer to the end of the string (except for truncation); the 'e' as in [_strecopy()_](https://www.symas.com/post/the-sad-state-of-c-strings), after the extra parameter for the _**e**nd_ of the array (except that this one really means the end of the array).

```
static char *
stpecpy(char *dst, char *src, char *end)
{
	for (/* void */; dst <= end; dst++) {
		*dst = *src++;
		if (!*dst)
			return dst;
	}
	/* truncation detected */
	*end = '\0';
	return dst;
}
```

_dst_ and _src_ are the same as in [_strcpy(3)_](https://www.man7.org/linux/man-pages/man3/strcpy.3.html) and all other string copy functions.  _end_ is a pointer to the end of the array (i.e., to the last element of the array pointed to by _dst_).

It allows chaining without needing to recalculate the length of the string after each call (improvement already present in _strecopy()_), and allows for an easy way to detect truncation (the function returns `end + 1`).

When designing it, I had some doubts if I should return `NULL` or `end + 1`, but I decided to use `end + 1` for the following reasons:

- Don't need to add a check for `NULL` at the beginning of the function.

- Callers can cause undefined behavior easily if the function returns `NULL`: `len = stpecpy(dst, src, end) - buf;`.  `NULL - buf` is Undefined Behavior.

However, it has a minor problem: the returned pointer minus the beginning of the buffer is not always the length of the string (it is `len + 1` in case of truncation); that may be unintuitive for some...  But I think it's still best to do that.


A test for the function:

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

	end = &buf[size - 1];

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

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

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

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

Which prints:

```
$ ./a.out 
Following string is truncated.
9: Hello wor
9: Hello foo
Following string is truncated.
9: Hello baa
2: HW
```

So, what are your thoughts about this copy function?