Subject: bug#48960: stat v8.30 - device number in decimal shown as 16bit number instead of to converted 8bit

From: Pádraig Brady <[email-masked]>
Date: Sun, 20 Jun 2021 15:21:54 +0100

On 12/06/2021 16:19, Pádraig Brady wrote:
> We could take the FreeBSD approach which is:
> 
>     %Hd  major device number in decimal (st_dev)
>     %Ld  minor device number in decimal (st_dev)
>     %Hr  major device type in decimal (st_rdev)
>     %Lr  minor device type in decimal (st_rdev)
> 
> Note I'd be inclined to not have a space between major,minor in default stat output,
> as there should be no ambiguity with locale formatted numbers, which could be
> the case with ls. For example, my ls alias uses thousands grouping with:
> alias ls="BLOCK_SIZE=\'1 ls --color=auto".
> I.e. the default format would from stat would be
> Device: %Hd,%Ld  ..... Device type: %Hr,%Lr
> 
> For consistency if we provided the above we should also probably provide:
> 
>    %r     (composed) device type in decimal (st_rdev)
>    %R     (composed) device type in hex (st_rdev)

The attached adds the new formats.
I'll follow up with another to adjust the default output accordingly.

cheers,
Pádraig
>From 013ba07f19a209685331cac3f8202591b5b90dad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Sun, 20 Jun 2021 15:16:49 +0100
Subject: [PATCH] stat: support more device number representations

In preparation for changing the default device number
representation (to decomposed decimal), provide more
formatting options for device numbers.

These new (FreeBSD compat) formatting options are added:

   %Hd  major device number in decimal (st_dev)
   %Ld  minor device number in decimal (st_dev)
   %Hr  major device type in decimal (st_rdev)
   %Lr  minor device type in decimal (st_rdev)
   %r   (composed) device type in decimal (st_rdev)
   %R   (composed) device type in hex (st_rdev)

* doc/coreutils.texi (stat invocation): Document new formats.
* src/stat.c (print_it): Handle the new %H and %L modifiers.
(print_stat): Handle any modifiers and the new 'r' format.
(usage): Document the new formats.
Addresses https://bugs.gnu.org/48960
---
 doc/coreutils.texi | 14 ++++++++----
 src/stat.c         | 57 ++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 60 insertions(+), 11 deletions(-)

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 3e3aedb0f..2b69e1a2a 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -12506,8 +12506,10 @@ The valid @var{format} directives for files with @option{--format} and
 @item %b - Number of blocks allocated (see @samp{%B})
 @item %B - The size in bytes of each block reported by @samp{%b}
 @item %C - The SELinux security context of a file, if available
-@item %d - Device number in decimal
-@item %D - Device number in hex
+@item %d - Device number in decimal (st_dev)
+@item %D - Device number in hex (st_dev)
+@item %Hd - Major device number in decimal
+@item %Ld - Minor device number in decimal
 @item %f - Raw mode in hex
 @item %F - File type
 @item %g - Group ID of owner
@@ -12519,6 +12521,10 @@ The valid @var{format} directives for files with @option{--format} and
 @item %N - Quoted file name with dereference if symbolic link (see below)
 @item %o - Optimal I/O transfer size hint
 @item %s - Total size, in bytes
+@item %r - Device type in decimal (st_rdev)
+@item %R - Device type in hex (st_rdev)
+@item %Hr - Major device type in decimal (see below)
+@item %Lr - Minor device type in decimal (see below)
 @item %t - Major device type in hex (see below)
 @item %T - Minor device type in hex (see below)
 @item %u - User ID of owner
@@ -12543,8 +12549,8 @@ The @samp{%N} format can be set with the environment variable
 the default value is @samp{shell-escape-always}.  Valid quoting styles are:
 @quotingStyles
 
-The @samp{%t} and @samp{%T} formats operate on the st_rdev member of
-the stat(2) structure, and are only defined for character and block
+The @samp{r}, @samp{R}, @samp{%t}, and @samp{%T} formats operate on the st_rdev
+member of the stat(2) structure, and are only defined for character and block
 special files.  On some systems or file types, st_rdev may be used to
 represent other quantities.
 
diff --git a/src/stat.c b/src/stat.c
index 56e4867b9..8c9c69e7c 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -1144,11 +1144,12 @@ print_it (char const *format, int fd, char const *filename,
         case '%':
           {
             size_t len = format_code_offset (b);
-            char const *fmt_char = b + len;
+            char fmt_char = *(b + len);
+            char mod_char = 0;
             memcpy (dest, b, len);
             b += len;
 
-            switch (*fmt_char)
+            switch (fmt_char)
               {
               case '\0':
                 --b;
@@ -1156,15 +1157,32 @@ print_it (char const *format, int fd, char const *filename,
               case '%':
                 if (1 < len)
                   {
-                    dest[len] = *fmt_char;
+                    dest[len] = fmt_char;
                     dest[len + 1] = '\0';
                     die (EXIT_FAILURE, 0, _("%s: invalid directive"),
                          quote (dest));
                   }
                 putchar ('%');
                 break;
+              case 'H':
+              case 'L':
+                mod_char = fmt_char;
+                fmt_char = *(b + 1);
+                if (print_func == print_stat
+                    && (fmt_char == 'd' || fmt_char == 'r'))
+                  {
+                    b++;
+                  }
+                else
+                  {
+                    fmt_char = mod_char;
+                    mod_char = 0;
+                  }
+                FALLTHROUGH;
               default:
-                fail |= print_func (dest, len, to_uchar (*fmt_char),
+                fail |= print_func (dest, len,
+                                    (to_uchar (mod_char) << CHAR_BIT)
+                                     + to_uchar (fmt_char),
                                     fd, filename, data);
                 break;
               }
@@ -1470,6 +1488,9 @@ print_stat (char *pformat, size_t prefix_len, unsigned int m,
   struct group *gw_ent;
   bool fail = false;
 
+  char mod_char = m >> CHAR_BIT;
+  m &= UCHAR_MAX;
+
   switch (m)
     {
     case 'n':
@@ -1492,7 +1513,12 @@ print_stat (char *pformat, size_t prefix_len, unsigned int m,
         }
       break;
     case 'd':
-      out_uint (pformat, prefix_len, statbuf->st_dev);
+      if (mod_char == 'H')
+        out_uint (pformat, prefix_len, major (statbuf->st_dev));
+      else if (mod_char == 'L')
+        out_uint (pformat, prefix_len, minor (statbuf->st_dev));
+      else
+        out_uint (pformat, prefix_len, statbuf->st_dev);
       break;
     case 'D':
       out_uint_x (pformat, prefix_len, statbuf->st_dev);
@@ -1537,6 +1563,17 @@ print_stat (char *pformat, size_t prefix_len, unsigned int m,
     case 's':
       out_int (pformat, prefix_len, statbuf->st_size);
       break;
+    case 'r':
+      if (mod_char == 'H')
+        out_uint (pformat, prefix_len, major (statbuf->st_rdev));
+      else if (mod_char == 'L')
+        out_uint (pformat, prefix_len, minor (statbuf->st_rdev));
+      else
+        out_uint (pformat, prefix_len, statbuf->st_rdev);
+      break;
+    case 'R':
+      out_uint_x (pformat, prefix_len, statbuf->st_rdev);
+      break;
     case 't':
       out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
       break;
@@ -1739,8 +1776,10 @@ The valid format sequences for files (without --file-system):\n\
   %C   SELinux security context string\n\
 "), stdout);
       fputs (_("\
-  %d   device number in decimal\n\
-  %D   device number in hex\n\
+  %d   device number in decimal (st_dev)\n\
+  %D   device number in hex (st_dev)\n\
+  %Hd  major device number in decimal\n\
+  %Ld  minor device number in decimal\n\
   %f   raw mode in hex\n\
   %F   file type\n\
   %g   group ID of owner\n\
@@ -1754,6 +1793,10 @@ The valid format sequences for files (without --file-system):\n\
   %N   quoted file name with dereference if symbolic link\n\
   %o   optimal I/O transfer size hint\n\
   %s   total size, in bytes\n\
+  %r   device type in decimal (st_rdev)\n\
+  %R   device type in hex (st_rdev)\n\
+  %Hr  major device type in decimal, for character/block device special files\n\
+  %Lr  minor device type in decimal, for character/block device special files\n\
   %t   major device type in hex, for character/block device special files\n\
   %T   minor device type in hex, for character/block device special files\n\
 "), stdout);
-- 
2.26.2