Skip to content

Add strncpytail_a(), and use it instead of its pattern#1304

Open
alejandro-colomar wants to merge 3 commits intoshadow-maint:masterfrom
alejandro-colomar:strncpytail
Open

Add strncpytail_a(), and use it instead of its pattern#1304
alejandro-colomar wants to merge 3 commits intoshadow-maint:masterfrom
alejandro-colomar:strncpytail

Conversation

@alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Jul 18, 2025

Cc: @Karlson2k


Revisions:

v1b
  • Rebase
$ git range-diff 17a3bb51df92^..gh/strncpytail shadow/master..strncpytail 
1:  17a3bb51 = 1:  52ed2fb5 lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
2:  0747fecc = 2:  25ed13db lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
v1c
  • Rebase
$ git rd 
1:  52ed2fb5 = 1:  a9207ca3 lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
2:  25ed13db ! 2:  22ce0a47 lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
    @@ lib/utmp.c
      #include "string/strdup/xstrdup.h"
      #include "string/strdup/xstrndup.h"
     @@ lib/utmp.c: prepare_utmp(const char *name, const char *line, const char *host,
    -   if (NULL != ut) {
    +       && ('\0' != ut->ut_id[0])) {
                STRNCPY(utent->ut_id, ut->ut_id);
        } else {
     -          STRNCPY(utent->ut_id, strnul(line) - MIN(strlen(line), countof(utent->ut_id)));
v1d
  • Rebase
$ git range-diff a9207ca3^..22ce0a47 5f7630f8^..6b0a0ac0
1:  a9207ca3 = 1:  5f7630f8 lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
2:  22ce0a47 = 2:  6b0a0ac0 lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
v1e
  • Rebase
$ git rd 
1:  5f7630f8 = 1:  4fc3f696 lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
2:  6b0a0ac0 = 2:  df8b604d lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
v1f
  • Rebase
$ git rd 
1:  4fc3f696 = 1:  163d8535 lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
2:  df8b604d ! 2:  95ab3cb6 lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
    @@ Commit message
     
      ## lib/utmp.c ##
     @@
    - #include "string/strcmp/streq.h"
    + #include "string/strcmp/strneq.h"
      #include "string/strcmp/strprefix.h"
      #include "string/strcpy/strncpy.h"
     +#include "string/strcpy/strncpytail.h"
v1g
  • Rebase
$ git rd 
1:  163d8535 ! 1:  4638da01 lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
    @@ lib/Makefile.am: libshadow_la_SOURCES = \
     +  string/strcpy/strncpytail.h \
        string/strcpy/strtcpy.c \
        string/strcpy/strtcpy.h \
    -   string/strdup/strndupa.c \
    +   string/strdup/strdup.c \
     
      ## lib/string/strcpy/strncpytail.c (new) ##
     @@
2:  95ab3cb6 ! 2:  cd8af171 lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
    @@ lib/utmp.c
      #include "string/strcpy/strncpy.h"
     +#include "string/strcpy/strncpytail.h"
      #include "string/strcpy/strtcpy.h"
    - #include "string/strdup/xstrdup.h"
    - #include "string/strdup/xstrndup.h"
    + #include "string/strdup/strdup.h"
    + #include "string/strdup/strndup.h"
     @@ lib/utmp.c: prepare_utmp(const char *name, const char *line, const char *host,
            && ('\0' != ut->ut_id[0])) {
                STRNCPY(utent->ut_id, ut->ut_id);
v2
  • Rename s/STRNCPYTAIL/strncpytail_a/.
  • Improve comments.
$ git rd 
1:  4638da014 ! 1:  f498a3873 lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    lib/string/strcpy/: strncpytail(), STRNCPYTAIL(): Add APIs
    +    lib/string/strcpy/: strncpytail[_a](): Add APIs
     
         This works similar to strncpy(3), except that it truncates from the
         start of the string if the string doesn't fit.  It is useful for utmp(5)
    @@ lib/string/strcpy/strncpytail.h (new)
     +#include "string/strchr/strnul.h"
     +
     +
    -+#define STRNCPYTAIL(dst, src)  strncpytail(dst, src, countof(dst))
    ++// strncpytail_a - nonstring copy tail-of-string array
    ++#define strncpytail_a(dst, src)  strncpytail(dst, src, countof(dst))
     +
     +
     +ATTR_STRING(2)
    @@ lib/string/strcpy/strncpytail.h (new)
     +    size_t dsize);
     +
     +
    -+// nonstring copy tail-of-string
    ++// strncpytail - nonstring copy tail-of-string
     +inline char *
     +strncpytail(char *restrict dst, const char *restrict src, size_t dsize)
     +{
2:  cd8af171e ! 2:  36c4c50ef lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    lib/utmp.c: Use STRNCPYTAIL() instead of its pattern
    +    lib/utmp.c: Use strncpytail_a() instead of its pattern
     
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
    @@ lib/utmp.c: prepare_utmp(const char *name, const char *line, const char *host,
                STRNCPY(utent->ut_id, ut->ut_id);
        } else {
     -          STRNCPY(utent->ut_id, strnul(line) - MIN(strlen(line), countof(utent->ut_id)));
    -+          STRNCPYTAIL(utent->ut_id, line);
    ++          strncpytail_a(utent->ut_id, line);
        }
      #if defined(HAVE_STRUCT_UTMPX_UT_NAME)
        STRNCPY(utent->ut_name, name);
v2b
  • Update lib/string/README.
$ git rd 
1:  f498a3873 ! 1:  593027522 lib/string/strcpy/: strncpytail[_a](): Add APIs
    @@ lib/Makefile.am: libshadow_la_SOURCES = \
        string/strcpy/strtcpy.h \
        string/strdup/strdup.c \
     
    + ## lib/string/README ##
    +@@ lib/string/README: strcpy/ - String copying
    +     strncpytail()
    +   Like strncpy(), but when truncating, the tail of the string is
    +   kept instead of the beginning.  This is useful for ut_id.
    +-    STRNCPYTAIL()
    ++    strncpytail_a()
    +   Like strncpytail, but takes an array.
    + 
    +     STRNCAT()  // To be removed
    +
      ## lib/string/strcpy/strncpytail.c (new) ##
     @@
     +// SPDX-FileCopyrightText: 2025, Alejandro Colomar <alx@kernel.org>
2:  36c4c50ef = 2:  982f944a5 lib/utmp.c: Use strncpytail_a() instead of its pattern
v2c
  • Rebase
$ git rd 
1:  593027522 = 1:  4404b232e lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  982f944a5 ! 2:  7d6800182 lib/utmp.c: Use strncpytail_a() instead of its pattern
    @@ lib/utmp.c
      #include "string/strcmp/strprefix.h"
      #include "string/strcpy/strncpy.h"
     +#include "string/strcpy/strncpytail.h"
    - #include "string/strcpy/strtcpy.h"
      #include "string/strdup/strdup.h"
      #include "string/strdup/strndup.h"
    + 
     @@ lib/utmp.c: prepare_utmp(const char *name, const char *line, const char *host,
            && ('\0' != ut->ut_id[0])) {
                STRNCPY(utent->ut_id, ut->ut_id);
v2d
  • Rebase
$ git rd 
1:  4404b232e = 1:  e2c0cd3d7 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  7d6800182 = 2:  e78027182 lib/utmp.c: Use strncpytail_a() instead of its pattern
v2e
  • Rebase
$ git rd 
1:  e2c0cd3d7 ! 1:  485973f1a lib/string/strcpy/: strncpytail[_a](): Add APIs
    @@ lib/string/README: strcpy/ - String copying
     +    strncpytail_a()
        Like strncpytail, but takes an array.
      
    -     STRNCAT()  // To be removed
    +     strncat_a()  // To be removed
     
      ## lib/string/strcpy/strncpytail.c (new) ##
     @@
2:  e78027182 ! 2:  4ffb5bc27 lib/utmp.c: Use strncpytail_a() instead of its pattern
    @@ lib/utmp.c
      
     @@ lib/utmp.c: prepare_utmp(const char *name, const char *line, const char *host,
            && ('\0' != ut->ut_id[0])) {
    -           STRNCPY(utent->ut_id, ut->ut_id);
    +           strncpy_a(utent->ut_id, ut->ut_id);
        } else {
    --          STRNCPY(utent->ut_id, strnul(line) - MIN(strlen(line), countof(utent->ut_id)));
    +-          strncpy_a(utent->ut_id, strnul(line) - MIN(strlen(line), countof(utent->ut_id)));
     +          strncpytail_a(utent->ut_id, line);
        }
      #if defined(HAVE_STRUCT_UTMPX_UT_NAME)
    -   STRNCPY(utent->ut_name, name);
    +   strncpy_a(utent->ut_name, name);
v2f
  • Rebase
$ git rd 
1:  485973f1a = 1:  0d001eb2b lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  4ffb5bc27 = 2:  5bccdef18 lib/utmp.c: Use strncpytail_a() instead of its pattern
v2g
  • Rebase
$ git rd 
1:  0d001eb2b = 1:  559ac55c6 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  5bccdef18 = 2:  cf1424804 lib/utmp.c: Use strncpytail_a() instead of its pattern
v2h
  • Rebase
$ git rd 
1:  559ac55c6 = 1:  5c06da618 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  cf1424804 = 2:  2f4793faa lib/utmp.c: Use strncpytail_a() instead of its pattern
v2i
  • Rebase
$ git rd 
1:  5c06da618 = 1:  63d4e4ac0 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  2f4793faa = 2:  22b11999a lib/utmp.c: Use strncpytail_a() instead of its pattern
v2j
  • Rebase
$ git rd 
1:  63d4e4ac0 = 1:  136b63d95 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  22b11999a = 2:  c33c3d5c0 lib/utmp.c: Use strncpytail_a() instead of its pattern
v3
  • Use strneq_a() instead of its pattern.
$ git range-diff shadow/master c33c3d5c0044 HEAD
1:  136b63d95 = 1:  136b63d95 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  c33c3d5c0 = 2:  c33c3d5c0 lib/utmp.c: Use strncpytail_a() instead of its pattern
-:  --------- > 3:  e950aa7b1 lib/utmp.c: Use strneq_a() instead of its pattern
v3b
  • Rebase
$ git rd 
1:  136b63d95 = 1:  f102f5586 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  c33c3d5c0 = 2:  dc7d35898 lib/utmp.c: Use strncpytail_a() instead of its pattern
3:  e950aa7b1 = 3:  6a97a418b lib/utmp.c: Use strneq_a() instead of its pattern
v3c
  • Rebase
$ git rd 
1:  f102f5586 = 1:  7163e0365 lib/string/strcpy/: strncpytail[_a](): Add APIs
2:  dc7d35898 = 2:  9d24cf8a3 lib/utmp.c: Use strncpytail_a() instead of its pattern
3:  6a97a418b = 3:  760f6b1db lib/utmp.c: Use strneq_a() instead of its pattern

@alejandro-colomar alejandro-colomar force-pushed the strncpytail branch 2 times, most recently from 9acb3e0 to 0747fec Compare July 18, 2025 13:31
@Karlson2k
Copy link
Contributor

Nice.
Improves code readability.

@Karlson2k
Copy link
Contributor

Karlson2k commented Jul 18, 2025

For the records.
The next code could be faster:

inline char *
strncpytail(char *restrict dst, const char *restrict src, size_t dsize)
{
  size_t src_len;
  src_len = strlen(src);

  if (dsize > src_len)
    return memcpy (dst, src, src_len + 1);

  return memcpy (dst, src + (src_len - dsize), dsize);
}

Large strings / buffers can be processed much faster.

But if performance is not an issue, the code in the PR will work fine.

Note: the binary is still a few bytes larger.

@alejandro-colomar
Copy link
Collaborator Author

For the records. The next code could be faster:

inline char *
strncpytail(char *restrict dst, const char *restrict src, size_t dsize)
{
  size_t src_len;
  src_len = strlen(src);

  if (dsize > src_len)
    return memcpy (dst, src, src_len + 1);

  return memcpy (dst, src + (src_len - dsize), dsize);
}

Large strings / buffers can be processed much faster.

But if performance is not an issue, the code in the PR will work fine.

Note: the binary is still a few bytes larger.

Indeed, since this API is designed for short strings (ut_id), I'll optimize for readability for now.

@alejandro-colomar alejandro-colomar added the Simpler A good issue for a new beginner label Jul 20, 2025
@alejandro-colomar
Copy link
Collaborator Author

@hallyn The term nonstring comes from https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-nonstring-variable-attribute. (To avoid you having to read the lengthy discussion in the other thread.)

@hallyn
Copy link
Member

hallyn commented Aug 11, 2025

@hallyn The term nonstring comes from https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-nonstring-variable-attribute. (To avoid you having to read the lengthy discussion in the other thread.)

Thanks

@Karlson2k

This comment was marked as off-topic.

@hallyn
Copy link
Member

hallyn commented Aug 12, 2025

@hallyn The term nonstring comes from https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-nonstring-variable-attribute. (To avoid you having to read the lengthy discussion in the other thread.)

Also need to mention that GCC means by this term "do not use strlen() and other string-based function because the data MAY be not NUL-terminated or could be binary data", but here, in this project, it means different: "it is a text data in fixed size buffer/array, which terminates at the end of the array without NUL-termination or terminated early by first NUL".

Hm, do we need to define a new term?

@Karlson2k
Copy link
Contributor

Karlson2k commented Aug 12, 2025

@hallyn The term nonstring comes from https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-nonstring-variable-attribute. (To avoid you having to read the lengthy discussion in the other thread.)

Also need to mention that GCC means by this term "do not use strlen() and other string-based function because the data MAY be not NUL-terminated or could be binary data", but here, in this project, it means different: "it is a text data in fixed size buffer/array, which terminates at the end of the array without NUL-termination or terminated early by first NUL".

Hm, do we need to define a new term?

Probably.
@alejandro-colomar insisting that it is perfectly described by "fixed size array with zero-padded data". It also assumed that zero-padding must not be checked and data reading must be stopped at the first zero. Also assumed that the data cannot have zeros because zero is used for padding.
I'm trying to convince Alejandro that what is described is not zero-padding, but is just optional early zero-termination.

I any case, I think both terms "non-string" and "zero-padded array" are confusing and not fully describe this kind of entities.

@Karlson2k

This comment was marked as off-topic.

@alejandro-colomar
Copy link
Collaborator Author

Please, do not continue the discussion unthreaded. That makes it impossible to follow the page. If you want to have a lengthy discussion, open a comment on any line of code, which can be hidden easily.

@alejandro-colomar alejandro-colomar force-pushed the strncpytail branch 2 times, most recently from cd8af17 to 36c4c50 Compare October 31, 2025 09:23
@alejandro-colomar alejandro-colomar changed the title Add STRNCPYTAIL(), and use it instead of its pattern Add strncpytail_a(), and use it instead of its pattern Oct 31, 2025
@alejandro-colomar alejandro-colomar force-pushed the strncpytail branch 2 times, most recently from 982f944 to 7d68001 Compare November 4, 2025 12:05
@Karlson2k
Copy link
Contributor

Besides used terms, the code is OK.

@alejandro-colomar
Copy link
Collaborator Author

Besides used terms, the code is OK.

Thanks!

This works similar to strncpy(3), except that it truncates from the
start of the string if the string doesn't fit.  It is useful for utmp(5)
ut_id, where the tail of the string is more useful and distinctive.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
@alejandro-colomar
Copy link
Collaborator Author

Cc: @kees

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

New API Simpler A good issue for a new beginner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments