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
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...
#16: Post edited
stpecpy(): Design a better string copy function
- stpecpy(): Design a better string copy function that truncates
#14: Post edited
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
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?