Subject: Re: bug#79347: Bad formatting for ls with block-size 'k

From: Pádraig Brady <[email-masked]>
Date: Sun, 31 Aug 2025 14:36:06 +0100

On 30/08/2025 09:15, Peter Laan wrote:
> Hi,
> 
> I'm a GNU/Linux noob so maybe I'm doing something wrong. But see the
> attached image for badly formatted output from ls -s1 --block-size=\'k. The
> columns are not always aligned. This only happens when you have large files
> in the directory. Everything looks fine with --block-size=k.
> 
> I'm running Arch with the latest updates. ls version 9.7.

The issue is that printf("*s", width, multi_byte_string) doesn't count
width appropriately. In your sv_SE.UTF-8 locale, the number grouping
character is 3 UTF8 bytes which messes up the alignment.

The attached should address this,
which I'll push later today.

Marking this as done.

thanks,
Padraig

From 673ffd38184cd21e04e56f4b0ead2b6ef74c9f2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Sun, 31 Aug 2025 14:29:56 +0100
Subject: [PATCH] ls: fix alignment with locale formatted --size

Fix allocated size alignment in locales with multi-byte grouping chars.
Tested with: LC_ALL=sv_SE.utf8 ls --size --block-size=\'k

* src/ls.c (print_file_name_and_frills): Don't rely on
printf("%*s", width, string) to pad multi-byte strings appropriately.
Instead work out the padding required and use:
printf("%*s%s", padding, "", string) to pad multi-byte appropriately.
* NEWS: Mention the bug fix.
---
 NEWS     |  4 ++++
 src/ls.c | 16 ++++++++++++----
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index 988cb96a8..24430cedb 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   a confusing error about changing permissions.
   [This bug was present in "the beginning".]
 
+  "ls --size --block-size=\'k" could misalign output in locales
+  with multi-byte thousands grouping characters.
+  [This bug was present in "the beginning".]
+
   'od --strings' with '-N' now works correctly.  Previously od might
   write a NUL byte after a heap buffer, or output invalid addresses.
   [These bugs were present in "the beginning".]
diff --git a/src/ls.c b/src/ls.c
index d9faddee4..882752fe1 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -4869,10 +4869,18 @@ print_file_name_and_frills (const struct fileinfo *f, size_t start_col)
             format_inode (buf, f));
 
   if (print_block_size)
-    printf ("%*s ", format == with_commas ? 0 : block_size_width,
-            ! f->stat_ok ? "?"
-            : human_readable (STP_NBLOCKS (&f->stat), buf, human_output_opts,
-                              ST_NBLOCKSIZE, output_block_size));
+    {
+      char const *blocks =
+        (! f->stat_ok
+         ? "?"
+         : human_readable (STP_NBLOCKS (&f->stat), buf, human_output_opts,
+                           ST_NBLOCKSIZE, output_block_size));
+      int blocks_width = mbswidth (blocks, MBSWIDTH_FLAGS);
+      int pad = 0;
+      if (0 <= blocks_width && format != with_commas)
+        pad = block_size_width - blocks_width;
+      printf("%*s%s ", pad, "", blocks);
+    }
 
   if (print_scontext)
     printf ("%*s ", format == with_commas ? 0 : scontext_width, f->scontext);
-- 
2.50.1