Hi, Todd and Theo!

I wrote this function this week, based on strlcpy(3) and strecopy()[1],
which are the best string copy functions I've seen so far.  But I came
to improvements that bring one function that I think is better than both
of those.

I posted the function for review on codidact.com[2].
I also discussed it a bit on the groff@ mailing list[3].

See this manual page I wrote for it.  Since you designed strlcpy(3BSD)
and strlcat(3BSD), I'm interested in knowing your opinion on it.  If you
like it, feel free to add it to OpenBSD. ;-)

Cheers,

Alex


[1]: <https://www.symas.com/post/the-sad-state-of-c-strings>
[2]: <https://software.codidact.com/posts/285946/285952#answer-285952>
[3]: <https://lists.gnu.org/archive/html/groff/2022-02/msg00050.html>


---
stpecpy(3)          Linux Programmer’s Manual         stpecpy(3)

NAME
       stpecpy - bounded string copy and concatenation

SYNOPSIS
       #include <string.h>

       char *_Nonnull stpecpy(char *_Nonnull dest,
                              const char *_Nonnull restrict src,
                              char *_Nonnull end);

DESCRIPTION
       stpecpy()  copies the string pointed to by src (including
       the terminating null byte ('\0')) to the array pointed to
       by dest.  The strings may  not  overlap.   This  function
       doesn’t  overflow  the destination buffer, and requires a
       pointer to the end of it for that purpose.

       end is a pointer to the last element in the dest  buffer.
       It should be calculated in the following way:

           char dest[SIZE];
           char *end;

           end = &dest[SIZE - 1];
           stpecpy(dest, "Hello world", end);

       This  function  only  operates on true "C" strings, which
       means that src must be terminated by  a  null  byte,  and
       dest  is guaranteed to be terminated with a null byte af‐
       ter the call (as long as the buffer  has  a  size  larger
       than 0).

       This function is designed to be a safer, more consistent,
       less  error  prone,  and  simpler  replacement  for other
       string copy and concatenation functions.

   Compared to other string copy and concatenation functions
       strlcat(3BSD)
              *cat()  functions  have  an  inherent  performance
              problem:  each  *cat()  call has to find again the
              teminating null byte in  the  dest  string.   Read
              about    Shlemiel    the    painter’s    algorithm
              ⟨https://www.joelonsoftware.com/2001/12/11/back-
              to-basics/⟩.

       strlcpy(3BSD)
       strscpy(9)
       memccpy(3)
              One  can   use   strlcpy(3BSD),   strscpy(9),   or
              memccpy(3)  to concatenate, without strlcat(3BSD),
              but it’s error prone.  They require  the  user  to
              recalculate  the  remaining  buffer size for every
              call.

       stpcpy(3)
              It can’t detect overflow.  However,  in  case  the
              programmer  knows  overflow can’t occur, then it’s
              simpler to use it.

       stpncpy(3)
       strncpy(3)
       strncat(3)
              They don’t work with "C" strings.   The  resulting
              string  is  not guaranteed to be terminated with a
              null byte.  They also write unnecessary zeros  af‐
              ter the terminating byte.

       strcpy(3)
              It  doesn’t detect overflow, as stpcpy(3), plus it
              can’t be chained; requires strcat(3).

       strcat(3)
              It can’t detect overflow, as  strcpy(3),  plus  it
              has performance problems, as strlcat(3BSD).

       snprintf(3)
              It  has considerable overhead, which can be orders
              of magnitude slower.

RETURN VALUE
       stpecpy() returns a pointer to the terminating null  byte
       in  dest,  except  if  the  string has been truncated, in
       which case it returns end + 1.

EXAMPLES
   Trivial (not optimized) implementation:
       char *_Nonnull
       stpecpy(char *_Nonnull dst, char *_Nonnull restrict src,
               char *_Nonnull end)
       {
           for (/* void */; dst <= end; dst++, src++) {
               *dst = *src;
               if (*dst == '\0')
                   return dst;
           }
           /* truncation detected */
           *end = '\0';
           return dst;
       }

   Example of usage
       The following program produces this output:

           $ ./a.out
           Trunc: 9: Hello wor
           9: Hello foo
           Trunc: 9: Hello baa
           2: HW

       Source code:

           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--;
                   printf("Trunc: ");
               }
               printf("%ti: %s\n", len, buf);

               len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), " foo",
end),
                             "", end) - buf;
               if (len == size) {
                   len--;
                   printf("Trunc: ");
               }
               printf("%ti: %s\n", len, buf);

               len = stpecpy(stpecpy(stpecpy(buf, "Hello", end), "
baar", end),
                             "", end) - buf;
               if (len == size) {
                   len--;
                   printf("Trunc: ");
               }
               printf("%ti: %s\n", len, buf);

               len = stpecpy(stpecpy(buf, "H", end), "W", end) - buf;
               if (len == size) {
                   len--;
                   printf("Trunc: ");
               }
               printf("%ti: %s\n", len, buf);
           }

SEE ALSO
       snprintf(3), strncat(3), strncpy(3), stpsecpy(3)

alx                        2022-02-13                 stpecpy(3)


-- 
Alejandro Colomar
Linux man-pages comaintainer; https://www.kernel.org/doc/man-pages/
http://www.alejandro-colomar.es/

Reply via email to