Fix bug: GNU 'ls' didn't count columns correctly if user or group
authorJim Meyering <jim@meyering.net>
Tue, 22 Jun 2004 12:50:01 +0000 (12:50 +0000)
committerJim Meyering <jim@meyering.net>
Tue, 22 Jun 2004 12:50:01 +0000 (12:50 +0000)
names contained multibyte characters where the column count
differed from the byte count.  This patch also corrects
some comments.

(format_user_or_group): New function, which counts columns correctly.
(format_user, format_group): Use it.
(format_user_or_group_width): New function, which counts columns correctly.
(format_user_width, format_group_width): Use it.

src/ls.c

index b986e68c1489d58a42ef8bf8c1c429b59af8faa2..d8fe9222f63614bf5ca869ee4aa2f246433a2e85 100644 (file)
--- a/src/ls.c
+++ b/src/ls.c
@@ -330,7 +330,7 @@ static struct pending *pending_dirs;
 static time_t current_time = TYPE_MINIMUM (time_t);
 static int current_time_ns = -1;
 
-/* The number of bytes to use for columns containing inode numbers,
+/* The number of columns to use for columns containing inode numbers,
    block sizes, link counts, owners, groups, authors, major device
    numbers, minor device numbers, and file sizes, respectively.  */
 
@@ -804,14 +804,14 @@ static struct column_info *column_info;
 /* Maximum number of columns ever possible for this display.  */
 static size_t max_idx;
 
-/* The minimum width of a colum is 3: 1 character for the name and 2
+/* The minimum width of a column is 3: 1 character for the name and 2
    for the separating white space.  */
 #define MIN_COLUMN_WIDTH       3
 
 
 /* This zero-based index is used solely with the --dired option.
    When that option is in effect, this counter is incremented for each
-   character of output generated by this program so that the beginning
+   byte of output generated by this program so that the beginning
    and ending indices (in that output) of every file name can be recorded
    and later output themselves.  */
 static size_t dired_pos;
@@ -3055,19 +3055,44 @@ get_current_time (void)
   current_time_ns = 999999999;
 }
 
+/* Print the user or group name NAME, with numeric id ID, using a
+   print width of WIDTH columns.  */
+
+static void
+format_user_or_group (char const *name, unsigned long int id, int width)
+{
+  size_t len;
+
+  if (name)
+    {
+      /* The output column count may differ from the byte count.
+        Adjust for this, but don't output garbage if integer overflow
+        occurs during adjustment.  */
+      len = strlen (name);
+      width -= mbswidth (name, 0);
+      width += len;
+      if (width < 0)
+       width = 0;
+      printf ("%-*s ", width, name);
+      if (len < width)
+       len = width;
+    }
+  else
+    {
+      printf ("%*lu ", width, id);
+      len = width;
+    }
+
+  dired_pos += len + 1;
+}
+
 /* Print the name or id of the user with id U, using a print width of
    WIDTH.  */
 
 static void
 format_user (uid_t u, int width)
 {
-  char const *name = (numeric_ids ? NULL : getuser (u));
-  if (name)
-    printf ("%-*s ", width, name);
-  else
-    printf ("%*lu ", width, (unsigned long int) u);
-  dired_pos += width;
-  dired_pos++;
+  format_user_or_group (numeric_ids ? NULL : getuser (u), u, width);
 }
 
 /* Likewise, for groups.  */
@@ -3075,34 +3100,33 @@ format_user (uid_t u, int width)
 static void
 format_group (gid_t g, int width)
 {
-  char const *name = (numeric_ids ? NULL : getgroup (g));
-  if (name)
-    printf ("%-*s ", width, name);
-  else
-    printf ("%*lu ", width, (unsigned long int) g);
-  dired_pos += width;
-  dired_pos++;
+  format_user_or_group (numeric_ids ? NULL : getgroup (g), g, width);
 }
 
-/* Return the number of bytes that format_user will print.  */
+/* Return the number of columns that format_user_or_group will print.  */
 
 static int
-format_user_width (uid_t u)
+format_user_or_group_width (char const *name, unsigned long int id)
 {
-  char const *name = (numeric_ids ? NULL : getuser (u));
-  char buf[INT_BUFSIZE_BOUND (unsigned long int)];
-  size_t len;
-
-  if (! name)
+  if (name)
+    {
+      int len = mbswidth (name, 0);
+      return MAX (0, len);
+    }
+  else
     {
-      sprintf (buf, "%lu", (unsigned long int) u);
-      name = buf;
+      char buf[INT_BUFSIZE_BOUND (unsigned long int)];
+      sprintf (buf, "%lu", id);
+      return strlen (buf);
     }
+}
 
-  len = strlen (name);
-  if (INT_MAX < len)
-    error (EXIT_FAILURE, 0, _("User name too long"));
-  return len;
+/* Return the number of columns that format_user will print.  */
+
+static int
+format_user_width (uid_t u)
+{
+  return format_user_or_group_width (numeric_ids ? NULL : getuser (u), u);
 }
 
 /* Likewise, for groups.  */
@@ -3110,20 +3134,7 @@ format_user_width (uid_t u)
 static int
 format_group_width (gid_t g)
 {
-  char const *name = (numeric_ids ? NULL : getgroup (g));
-  char buf[INT_BUFSIZE_BOUND (unsigned long int)];
-  size_t len;
-
-  if (! name)
-    {
-      sprintf (buf, "%lu", (unsigned long int) g);
-      name = buf;
-    }
-
-  len = strlen (name);
-  if (INT_MAX < len)
-    error (EXIT_FAILURE, 0, _("Group name too long"));
-  return len;
+  return format_user_or_group_width (numeric_ids ? NULL : getgroup (g), g);
 }