Skip to content

Conversation

@alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Aug 13, 2025

Queued after:

Revisions:


v1b
  • Rebase
$ git rd 
1:  34bbd688 = 1:  b70d59b7 lib/string/strcpy/: strtcat(), STRTCAT(): Add APIs
2:  79541620 = 2:  1b15cf5d lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
v2
  • Reimplement in terms of stpecpy(). This is much simpler, and thus safer. It also allows us using size_t instead of ssize_t in the input, which is more common (it was weird to need ssize_t in the input, just to avoid compiler diangostics).
$ git range-diff shadow/master gh/strtcat strtcat 
1:  b70d59b7 ! 1:  3f2d3a8e lib/string/strcpy/: strtcat(), STRTCAT(): Add APIs
    @@ lib/string/strcpy/strtcat.c (new)
     +
     +#include "string/strcpy/strtcat.h"
     +
    -+#include <sys/types.h>
    ++#include <stddef.h>
     +
     +
     +extern inline ssize_t strtcat(char *restrict dst, const char *restrict src,
    -+    ssize_t dsize);
    ++    size_t dsize);
     
      ## lib/string/strcpy/strtcat.h (new) ##
     @@
    @@ lib/string/strcpy/strtcat.h (new)
     +
     +#include "config.h"
     +
    -+#include <string.h>
    -+#include <sys/types.h>
    ++#include <stddef.h>
     +
     +#include "attr.h"
     +#include "sizeof.h"
    -+#include "string/strcpy/strtcpy.h"
    ++#include "string/strchr/strnul.h"
    ++#include "string/strcpy/stpecpy.h"
     +
     +
     +#define STRTCAT(dst, src)  strtcat(dst, src, countof(dst))
    @@ lib/string/strcpy/strtcat.h (new)
     +
     +ATTR_STRING(2)
     +inline ssize_t strtcat(char *restrict dst, const char *restrict src,
    -+    ssize_t dsize);
    ++    size_t dsize);
     +
     +
     +// string truncate catenate
     +// Returns new length, or -1 on truncation.
     +inline ssize_t
    -+strtcat(char *restrict dst, const char *restrict src, ssize_t dsize)
    ++strtcat(char *restrict dst, const char *restrict src, size_t dsize)
     +{
    -+  ssize_t  oldlen, n;
    ++  char  *p, *end;
     +
    -+  oldlen = strlen(dst);
    ++  end = dst + dsize;
     +
    -+  n = strtcpy(dst + oldlen, src, dsize - oldlen);
    -+  if (n == -1)
    ++  p = stpecpy(strnul(dst), end, src);
    ++  if (p == NULL)
     +          return -1;
     +
    -+  return oldlen + n;
    ++  return p - dst;
     +}
     +
     +
2:  1b15cf5d = 2:  ae41236f lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
v2b
  • Update lib/string/README.
$ git range-diff shadow/master gh/strtcat strtcat 
1:  3f2d3a8e ! 1:  8fd61b28 lib/string/strcpy/: strtcat(), STRTCAT(): Add APIs
    @@ lib/Makefile.am: libshadow_la_SOURCES = \
        string/strcpy/strtcpy.h \
        string/strdup/strndupa.c \
     
    + ## lib/string/README ##
    +@@ lib/string/README: strcpy/ - String copying
    +     STRTCPY()
    +   Like strtcpy(), but takes an array.
    + 
    +-    strtcat()  // Unimplemented
    ++    strtcat()
    +   Catenate a string after an existing string, with truncation.
    +   Rarely useful.  Consider using stpecpy() instead.
    +-    STRTCAT()  // Unimplemented
    ++    STRTCAT()
    +   Like strtcat(), but takes an array.
    + 
    +     stpecpy()
    +
      ## lib/string/strcpy/strtcat.c (new) ##
     @@
     +// SPDX-FileCopyrightText: 2025, Alejandro Colomar <[email protected]>
2:  ae41236f = 2:  72e56458 lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
v2c
  • Rebase
$ git rd 
1:  8fd61b28 = 1:  f229c84c lib/string/strcpy/: strtcat(), STRTCAT(): Add APIs
2:  72e56458 = 2:  7700f0fc lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
v2d
  • Rebase
$ git rd 
1:  f229c84c ! 1:  e25872f9 lib/string/strcpy/: strtcat(), STRTCAT(): Add APIs
    @@ lib/Makefile.am: libshadow_la_SOURCES = \
     +  string/strcpy/strtcat.h \
        string/strcpy/strtcpy.c \
        string/strcpy/strtcpy.h \
    -   string/strdup/strndupa.c \
    +   string/strdup/strdup.c \
     
      ## lib/string/README ##
     @@ lib/string/README: strcpy/ - String copying
2:  7700f0fc = 2:  bf414636 lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
v3
  • s/STRTCAT/strtcat_a/
$ git rd 
1:  e25872f90 ! 1:  01e32cebf lib/string/strcpy/: strtcat(), STRTCAT(): Add APIs
    @@ Metadata
     Author: Alejandro Colomar <[email protected]>
     
      ## Commit message ##
    -    lib/string/strcpy/: strtcat(), STRTCAT(): Add APIs
    +    lib/string/strcpy/: strtcat[_a](): Add APIs
     
         Signed-off-by: Alejandro Colomar <[email protected]>
     
    @@ lib/string/README: strcpy/ - String copying
        Catenate a string after an existing string, with truncation.
        Rarely useful.  Consider using stpecpy() instead.
     -    STRTCAT()  // Unimplemented
    -+    STRTCAT()
    ++    strtcat_a()
        Like strtcat(), but takes an array.
      
          stpecpy()
    @@ lib/string/strcpy/strtcat.h (new)
     +#include "string/strcpy/stpecpy.h"
     +
     +
    -+#define STRTCAT(dst, src)  strtcat(dst, src, countof(dst))
    ++// strtcat_a - string truncate catenate array
    ++#define strtcat_a(dst, src)  strtcat(dst, src, countof(dst))
     +
     +
     +ATTR_STRING(2)
    @@ lib/string/strcpy/strtcat.h (new)
     +    size_t dsize);
     +
     +
    -+// string truncate catenate
    ++// strtcat - string truncate catenate
     +// Returns new length, or -1 on truncation.
     +inline ssize_t
     +strtcat(char *restrict dst, const char *restrict src, size_t dsize)
2:  bf4146369 ! 2:  b1906dffc lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
    @@ lib/salt.c: static /*@observer@*/const char *gensalt (size_t salt_size)
     -  /* Concatenate a pseudo random salt. */
     -  strncat (result, gensalt (salt_len),
     -           GENSALT_SETTING_SIZE - strlen (result) - 1);
    -+  assert(STRTCAT(result, gensalt(salt_len)) != -1);
    ++  assert(strtcat_a(result, gensalt(salt_len)) != -1);
      
        return result;
      #endif /* USE_XCRYPT_GENSALT */
v3b
  • Fix commit message.
$ git rd 
1:  01e32cebf = 1:  01e32cebf lib/string/strcpy/: strtcat[_a](): Add APIs
2:  b1906dffc ! 2:  1f2e22317 lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
    @@ Metadata
     Author: Alejandro Colomar <[email protected]>
     
      ## Commit message ##
    -    lib/salt.c: gensalt(): Use STRTCPY() instead of its pattern
    +    lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
     
         Signed-off-by: Alejandro Colomar <[email protected]>
     
v3c
  • Rebase
$ git rd 
1:  01e32cebf = 1:  10a4fe3f9 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  1f2e22317 = 2:  f7faeaab3 lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3d
  • Rebase
$ git rd 
1:  10a4fe3f9 = 1:  370109241 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  f7faeaab3 = 2:  5c3dce3fc lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3c
$ git range-diff shadow/master..gh/strtcat gh/se..strtcat 
1:  370109241 = 1:  df05c46c3 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  5c3dce3fc ! 2:  14f666539 lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
    @@ Commit message
     
      ## lib/salt.c ##
     @@
    - #include "prototypes.h"
      #include "shadowlog.h"
    + #include "string/sprintf/stprintf.h"
      #include "string/strcmp/streq.h"
     +#include "string/strcpy/strtcat.h"
      
v3d
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  df05c46c3 = 1:  96af59655 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  14f666539 = 2:  3234b0822 lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3e
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  96af59655 ! 1:  6bde48e54 lib/string/strcpy/: strtcat[_a](): Add APIs
    @@ lib/Makefile.am: libshadow_la_SOURCES = \
     
      ## lib/string/README ##
     @@ lib/string/README: strcpy/ - String copying
    -     STRTCPY()
    +     strtcpy_a()
        Like strtcpy(), but takes an array.
      
     -    strtcat()  // Unimplemented
2:  3234b0822 = 2:  b3c064f50 lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3f
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  6bde48e54 = 1:  d155aa3d6 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  b3c064f50 = 2:  0fa703e0e lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3g
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  d155aa3d6 = 1:  eee6da9d9 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  0fa703e0e = 2:  cabae6c07 lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3h
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  eee6da9d9 = 1:  a2879def9 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  cabae6c07 = 2:  9d2fd3930 lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3i
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  a2879def9 = 1:  116ed8874 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  9d2fd3930 = 2:  2b97b7d6f lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3j
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  116ed8874 = 1:  306cb0f7a lib/string/strcpy/: strtcat[_a](): Add APIs
2:  2b97b7d6f = 2:  9ea22bd2a lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3k
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  306cb0f7a = 1:  e950460b0 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  9ea22bd2a = 2:  02028247e lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3l
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat
1:  e950460b0 = 1:  4e36fd47b lib/string/strcpy/: strtcat[_a](): Add APIs
2:  02028247e = 2:  2d2211bac lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3m
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  4e36fd47b = 1:  7e547a42e lib/string/strcpy/: strtcat[_a](): Add APIs
2:  2d2211bac = 2:  9e302ce04 lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern
v3n
  • Rebase
$ git range-diff gh/se..gh/strtcat se..strtcat 
1:  7e547a42e = 1:  4514dc9a8 lib/string/strcpy/: strtcat[_a](): Add APIs
2:  9e302ce04 = 2:  9b4e3e3bf lib/salt.c: gensalt(): Use strtcat_a() instead of its pattern

@alejandro-colomar alejandro-colomar force-pushed the strtcat branch 2 times, most recently from d5a6502 to 7954162 Compare August 13, 2025 11:22
@alejandro-colomar alejandro-colomar marked this pull request as ready for review August 13, 2025 11:33
@alejandro-colomar alejandro-colomar force-pushed the strtcat branch 3 times, most recently from ae41236 to 72e5645 Compare September 30, 2025 18:26
@alejandro-colomar alejandro-colomar marked this pull request as draft October 7, 2025 19:26
@alejandro-colomar alejandro-colomar force-pushed the strtcat branch 2 times, most recently from bf41463 to b1906df Compare October 28, 2025 09:26
@alejandro-colomar alejandro-colomar changed the title Add strtcat(), STRTCAT(), and use it instead of its pattern Add strtcat[_a](), and use it instead of its pattern Oct 28, 2025
@alejandro-colomar alejandro-colomar force-pushed the strtcat branch 4 times, most recently from 5c3dce3 to 14f6665 Compare November 7, 2025 14:51
@alejandro-colomar alejandro-colomar force-pushed the strtcat branch 6 times, most recently from 9d2fd39 to 2b97b7d Compare December 5, 2025 13:53
@alejandro-colomar alejandro-colomar force-pushed the strtcat branch 2 times, most recently from 9ea22bd to 0202824 Compare December 5, 2025 20:08
@alejandro-colomar alejandro-colomar force-pushed the strtcat branch 2 times, most recently from 2d2211b to 9e302ce Compare December 6, 2025 11:52
vsnprintf(3) returns an int.  By using ssize_t, which is also signed, we
avoid the need for a cast.

Cc: Serge Hallyn <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
There's nothing better that can be done.

Cc: Serge Hallyn <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Make them report truncation via errno and NULL.

Instead of having three possible returns (a pointer to the NUL byte, the
end of the array, or NULL), reduce it to two possible ones: one for
success, and one for error.

Use errno, which is a common way to signal the specific error, and thus
treat truncation as any other error.  This simplifies error handling
after these calls.  Also, if one misuses a pointer after truncation, the
results are better if the pointer is NULL: the program will easily
abort.  If we returned 'end', the program could more easily produce a
buffer overrun.

Suggested-by: Douglas McIlroy <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
In the case of [v]snprintf_(), anticipate the rename in the next commit.

Signed-off-by: Alejandro Colomar <[email protected]>
For consistency with strTcpy(), call it sTprintf().

Signed-off-by: Alejandro Colomar <[email protected]>
The old name was too complex, and is inconsistent with all other
sprintf(3)-based APIs having just one letter for differentiation.
This allows breaking less lines.

The original name was chosen for differentiation with the buggy Plan9
API seprint(2).  However, 9front (the current fork where Plan9 is mainly
developed) has acknowledged the bug.  There's still no decision on
fixing the bug or not, due to the age of their code base, and the
projects depending on their library.  It is under consideration
inventing something like a seprint2(2) in 9front for replacement of
seprint(2), but there's no decision yet either.

Considering that 9front acknowledges their bug, and that they *may*
release a fixed API with a similar name, we may as well claim that our
seprintf() is also a fixed version of Plan9's seprint(2).  It has a
different name, after all (we terminate in 'f').

This commit was partially scripted with

	$ find * -type f \
	| xargs grep -l stpeprintf \
	| xargs sed -i 's/stpeprintf/seprintf/g';

Signed-off-by: Alejandro Colomar <[email protected]>
Reported-by: Christopher Bazley <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant