>From d6d980cc3a9b8f5b4df7a6e284944bfe5f905b19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <address@hidden>
Date: Sun, 23 Sep 2018 20:50:07 -0700
Subject: [PATCH] id: adjustments to multiuser patch

will merge with original
---
 NEWS               |  4 +++
 doc/coreutils.texi |  5 +++-
 src/id.c           | 79 ++++++++++++++++++++++++++++--------------------------
 tests/id/uid.sh    |  5 ++++
 tests/id/zero.sh   | 13 +++++----
 5 files changed, 62 insertions(+), 44 deletions(-)

diff --git a/NEWS b/NEWS
index aa3b4f9..77c26df 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,10 @@ GNU coreutils NEWS                                    -*- outline -*-
 
   df no longer corrupts displayed multibyte characters on macOS.
 
+** New features
+
+  id now supports specifying multiple users.
+
 
 * Noteworthy changes in release 8.30 (2018-07-01) [stable]
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 6eaeea0..e148e88 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -15273,8 +15273,11 @@ set the exit status to 1.
 @itemx --zero
 @opindex -z
 @opindex --zero
-Delimit output items with NUL characters.
+Delimit output items with ASCII NUL characters.
 This option is not permitted when using the default format.
+When multiple users are specified, and the @option{--groups} option
+is also in effect, groups are delimited with a single NUL character,
+while users are delimited with two NUL characters.
 
 Example:
 @example
diff --git a/src/id.c b/src/id.c
index e608038..2f89736 100644
--- a/src/id.c
+++ b/src/id.c
@@ -44,15 +44,15 @@
 /* If nonzero, output only the SELinux context.  */
 static bool just_context = 0;
 /* If true, delimit entries with NUL characters, not whitespace */
-bool opt_zero = false;
+static bool opt_zero = false;
 /* If true, output the list of all group IDs. -G */
-bool just_group_list = false;
+static bool just_group_list = false;
 /* If true, output only the group ID(s). -g */
-bool just_group = false;
+static bool just_group = false;
 /* If true, output real UID/GID instead of default effective UID/GID. -r */
-bool use_real = false;
+static bool use_real = false;
 /* If true, output only the user ID(s). -u */
-bool just_user = false;
+static bool just_user = false;
 /* True unless errors have been encountered.  */
 static bool ok = true;
 /* If true, we are using multiple users. Terminate -G with double NUL. */
@@ -70,7 +70,7 @@ static char *context = NULL;
 
 static void print_user (uid_t uid);
 static void print_full_info (const char *username);
-static void print_stuff(const char *pw_name);
+static void print_stuff (const char *pw_name);
 
 static struct option const longopts[] =
 {
@@ -95,7 +95,7 @@ usage (int status)
     {
       printf (_("Usage: %s [OPTION]... [USER]...\n"), program_name);
       fputs (_("\
-Print user and group information for the each specified USER,\n\
+Print user and group information for each specified USER,\n\
 or (when USER omitted) for the current user.\n\
 \n"),
              stdout);
@@ -225,43 +225,44 @@ main (int argc, char **argv)
     }
 
   if (n_ids >= 1)
-  {
+    {
       multiple_users = n_ids > 1 ? true : false;
       /* Changing the value of n_ids to the last index in the array where we
          have the last possible user id. This helps us because we don't have
-         to declare a different variable to keep a track of where the last username
-         lies in argv[]. */
+         to declare a different variable to keep a track of where the
+         last username lies in argv[].  */
       n_ids += optind;
       /* For each username/userid to get its pw_name field */
       for (; optind < n_ids; optind++)
-      {
+        {
           struct passwd *pwd = NULL;
           const char *spec = argv[optind];
           /* Disallow an empty spec here as parse_user_spec() doesn't
              give an error for that as it seems it's a valid way to
              specify a noop or "reset special bits" depending on the system.  */
-          if (*spec) {
-              if (parse_user_spec(spec, &euid, NULL, NULL, NULL) == NULL)
-              {
+          if (*spec)
+            {
+              if (parse_user_spec (spec, &euid, NULL, NULL, NULL) == NULL)
+                {
                   /* parse_user_spec will only extract a numeric spec,
                      so we lookup that here to verify and also retrieve
                      the PW_NAME used subsequently in group lookup.  */
-                  pwd = getpwuid(euid);
-              }
-          }
+                  pwd = getpwuid (euid);
+                }
+            }
           if (pwd == NULL)
-          {
-            error (0, errno, _("%s: no such user"), quote (argv[optind]));
-            ok &= false;
-            continue;
-          }
-          pw_name = xstrdup(pwd->pw_name);
+            {
+              error (0, errno, _("%s: no such user"), quote (argv[optind]));
+              ok &= false;
+              continue;
+            }
+          pw_name = xstrdup (pwd->pw_name);
           ruid = euid = pwd->pw_uid;
           rgid = egid = pwd->pw_gid;
-          print_stuff(pw_name);
-          IF_LINT (free (pw_name));
-      }
-  }
+          print_stuff (pw_name);
+          free (pw_name);
+        }
+    }
   else
     {
       /* POSIX says identification functions (getuid, getgid, and
@@ -300,8 +301,7 @@ main (int argc, char **argv)
           if (rgid == NO_GID && errno)
             die (EXIT_FAILURE, errno, _("cannot get real GID"));
         }
-        print_stuff(pw_name);
-        IF_LINT (free (pw_name));
+        print_stuff (pw_name);
     }
 
   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
@@ -429,7 +429,7 @@ print_full_info (const char *username)
 /* Print information about the user based on the arguments passed. */
 
 static void
-print_stuff(const char *pw_name)
+print_stuff (const char *pw_name)
 {
   if (just_user)
       print_user (use_real ? ruid : euid);
@@ -440,22 +440,25 @@ print_stuff(const char *pw_name)
      users faced a problem in these functions. This value of 'ok' is later used
      to understand what status program should exit with. */
   else if (just_group)
-      ok &= print_group (use_real ? rgid : egid, use_name);
+    ok &= print_group (use_real ? rgid : egid, use_name);
   else if (just_group_list)
-      ok &= print_group_list (pw_name, ruid, rgid, egid,
-               use_name, opt_zero ? '\0' : ' ');
+    ok &= print_group_list (pw_name, ruid, rgid, egid,
+                            use_name, opt_zero ? '\0' : ' ');
   else if (just_context)
-      fputs (context, stdout);
+    fputs (context, stdout);
   else
-      print_full_info (pw_name);
+    print_full_info (pw_name);
+
   /* When printing records for more than 1 user, at the end of groups
      of each user terminate the record with two consequent NUL characters
      to make parsing and distinguishing between two records possible. */
- if (opt_zero && just_group_list && multiple_users)
+  if (opt_zero && just_group_list && multiple_users)
     {
-      putchar('\0');
-      putchar('\0');
+      putchar ('\0');
+      putchar ('\0');
     }
   else
+    {
       putchar (opt_zero ? '\0' : '\n');
+    }
 }
diff --git a/tests/id/uid.sh b/tests/id/uid.sh
index 61d3137..9d85643 100755
--- a/tests/id/uid.sh
+++ b/tests/id/uid.sh
@@ -24,6 +24,11 @@ user=$(id -nu) || fail=1
 # Ensure the empty user spec is discarded
 returns_ 1 id '' || fail=1
 
+# Ensure we don't exit early, and process all users
+id $user > user_out || fail=1
+returns_ 1 id '' $user >multi_user_out || fail=1
+compare user_out multi_user_out || fail=1
+
 for mode in '' '-G' '-g'; do
   id $mode $user > user_out || fail=1 # lookup name for comparison
 
diff --git a/tests/id/zero.sh b/tests/id/zero.sh
index 7a94edd..d0cf44c 100755
--- a/tests/id/zero.sh
+++ b/tests/id/zero.sh
@@ -78,9 +78,12 @@ for o in g gr u ur ; do
 done
 compare tmp1 tmp3 || fail=1
 
-# Seperate checks when we are testing for multiple users && -G.
-# This is done because we terminate the records with two consequent
-# NULs instead of the regular single NUL.
+# Separate checks when we are testing for multiple users && -G.
+# This is done because we terminate the records with two NULs
+# instead of a regular single NUL.
+
+NL='
+'
 
 for o in G Gr ; do
   for n in '' n ; do
@@ -89,10 +92,10 @@ for o in G Gr ; do
     id -${o}${n}z $users  > gtmp2 ||
       { test $? -ne 1 || test -z "$n" && fail=1; }
     # we replace all NULs with spaces, the result we get is there are two
-    # consequent spaces instead of two consequent NUL's, we pass this to sed
+    # consecutive spaces instead of two NUL's, we pass this to sed
     # to replace more than 1 space with a newline. This is ideally where a new
     # line should be. This should make the output similar to without -z.
-    tr '\0' ' ' < gtmp2 | sed 's/ \+ /\n/g' >> gtmp3
+    tr '\0' ' ' < gtmp2 | sed "s/  /\\$NL/g" >> gtmp3
   done
 done
 compare gtmp1 gtmp3 || fail=1
-- 
2.9.3