]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
last: Use int for string precision modifier
authorTobias Stoeckmann <tobias@stoeckmann.org>
Thu, 9 Apr 2026 19:23:05 +0000 (21:23 +0200)
committerTobias Stoeckmann <tobias@stoeckmann.org>
Thu, 9 Apr 2026 22:31:52 +0000 (00:31 +0200)
The %.*s modifier requires an int as precision argument, not an unsigned
int. Instead of just turning the domain_len and name_len fields into
int, this refactoring makes it easier to verify that only two fixed
values are ever used and avoids many int/size_t casts:

Since fullnames_mode field already specifies which values domain_len and
name_len will take, these fields can be turned into local variables.

No special checks required when parsing 'w' argument.

Setting domain_len and name_len as close as possible to their usages as
%.*s modifier arguments shows that the relevant if-else-blocks are always
evaluated and only allow two values.

This leads to smaller binary size because less instructions are needed
(e.g. these variables do not need pointer dereferences anymore).
In total, around 160 bytes on x86_64 are saved.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
login-utils/last.c

index 0afdf9dd4e647da884437c7056b145207f31eae2..51743e5b49e2ad9b81cd3438ea114aa3b9ff5d70 100644 (file)
@@ -83,8 +83,6 @@ struct last_control {
             usedns,    /* Use DNS to lookup the hostname */
             useip;     /* Print IP address in number format */
 
-       unsigned int name_len;  /* Number of login name characters to print */
-       unsigned int domain_len; /* Number of domain name characters to print */
        unsigned int maxrecs;   /* Maximum number of records to list */
 
        char **show;            /* Match search list */
@@ -401,6 +399,7 @@ static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_t
        int             mins, hours, days;
        int             r, len;
        struct last_timefmt *fmt;
+       int             name_len;
 
        /*
         *      uucp and ftp have special-type entries
@@ -526,32 +525,43 @@ static int list(const struct last_control *ctl, struct utmpx *p, time_t logout_t
         * set last displayed character to an asterisk when
         * user/domain/ip fields are to be truncated in non-fullnames mode
         */
-       if (!ctl->fullnames_mode && (strnlen(p->ut_user, sizeof(p->ut_user)) > ctl->name_len))
-               p->ut_user[ctl->name_len-1] = '*';
+       if (ctl->fullnames_mode)
+               name_len = (int)sizeof_member(struct utmpx, ut_user);
+       else {
+               name_len = LAST_LOGIN_LEN;
+               if (strnlen(p->ut_user, sizeof(p->ut_user)) > LAST_LOGIN_LEN)
+                       p->ut_user[LAST_LOGIN_LEN - 1] = '*';
+       }
 
        if (ctl->showhost) {
                if (!ctl->altlist) {
-
-                       if (!ctl->fullnames_mode && (strnlen(domain, sizeof(domain)) > ctl->domain_len))
-                               domain[ctl->domain_len-1] = '*';
+                       int domain_len;
+
+                       if (ctl->fullnames_mode)
+                               domain_len = (int)sizeof_member(struct utmpx, ut_host);
+                       else {
+                               domain_len = LAST_DOMAIN_LEN;
+                               if (strnlen(domain, sizeof(domain)) > LAST_DOMAIN_LEN)
+                                       domain[LAST_DOMAIN_LEN - 1] = '*';
+                       }
 
                        len = snprintf(final, sizeof(final),
                                "%-8.*s%c%-12.12s%c%-16.*s%c%-*.*s%c%-*.*s%c%s\n",
-                               ctl->name_len, p->ut_user, ctl->separator, utline, ctl->separator,
-                               ctl->domain_len, domain, ctl->separator,
+                               name_len, p->ut_user, ctl->separator, utline, ctl->separator,
+                               domain_len, domain, ctl->separator,
                                fmt->in_len, fmt->in_len, logintime, ctl->separator, fmt->out_len, fmt->out_len,
                                logouttime, ctl->separator, length);
                } else {
                        len = snprintf(final, sizeof(final),
                                "%-8.*s%c%-12.12s%c%-*.*s%c%-*.*s%c%-12.12s%c%s\n",
-                               ctl->name_len, p->ut_user, ctl->separator, utline, ctl->separator,
+                               name_len, p->ut_user, ctl->separator, utline, ctl->separator,
                                fmt->in_len, fmt->in_len, logintime, ctl->separator, fmt->out_len, fmt->out_len,
                                logouttime, ctl->separator, length, ctl->separator, domain);
                }
        } else
                len = snprintf(final, sizeof(final),
                        "%-8.*s%c%-12.12s%c%-*.*s%c%-*.*s%c%s\n",
-                       ctl->name_len, p->ut_user, ctl->separator, utline, ctl->separator,
+                       name_len, p->ut_user, ctl->separator, utline, ctl->separator,
                        fmt->in_len, fmt->in_len, logintime, ctl->separator, fmt->out_len, fmt->out_len,
                        logouttime, ctl->separator, length);
 
@@ -940,9 +950,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
        struct last_control ctl = {
                .showhost = TRUE,
-               .name_len = LAST_LOGIN_LEN,
                .time_fmt = LAST_TIMEFTM_SHORT,
-               .domain_len = LAST_DOMAIN_LEN,
                .boot_time = {
                        .tv_sec = 1595978419,
                        .tv_usec = 816074
@@ -989,9 +997,7 @@ int main(int argc, char **argv)
 {
        struct last_control ctl = {
                .showhost = TRUE,
-               .name_len = LAST_LOGIN_LEN,
                .time_fmt = LAST_TIMEFTM_SHORT,
-               .domain_len = LAST_DOMAIN_LEN,
                .fullnames_mode = false,
        };
        char **files = NULL;
@@ -1096,10 +1102,6 @@ int main(int argc, char **argv)
                        break;
                case 'w':
                        ctl.fullnames_mode = true;
-                       if (ctl.name_len < sizeof_member(struct utmpx, ut_user))
-                               ctl.name_len = sizeof_member(struct utmpx, ut_user);
-                       if (ctl.domain_len < sizeof_member(struct utmpx, ut_host))
-                               ctl.domain_len = sizeof_member(struct utmpx, ut_host);
                        break;
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':