]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
id: add -z, --zero option
authorBernhard Voelker <mail@bernhard-voelker.de>
Sat, 21 Sep 2013 12:15:45 +0000 (14:15 +0200)
committerBernhard Voelker <mail@bernhard-voelker.de>
Sat, 21 Sep 2013 12:15:45 +0000 (14:15 +0200)
* src/group-list.h (print_group_list): Add a parameter for the
delimiter of type char.
* src/group-list.c (print_group_list): Likewise, and use it instead
of a white space character to delimit the group entries.
* src/groups.c (main): Pass white space character to print_group_list().
* src/id.c (longopts):  Add array element for the new long option.
(usage): Document the new option.  While at it, fix the alignment
of the descriptions to match that of HELP_OPTION_DESCRIPTION.
(main): Define the bool flag opt_zero indicating the use of the
new option.  In the getopt_long loop, handle it.
Output an error diagnostic in the case the --zero option has been
specified together with the default format.
In the case of -gG, pass either a NUL or a white space character to
print_group_list() - depending on the above new flag.
Likewise change the printing of the final newline character: output
a NUL instead if the --zero option has been specified.
* doc/coreutils.texi (id invocation): Document the new option.
While at it, move the @exitstatus macro down after the macro
@primaryAndSupplementaryGroups in order to be consistent with
other texinfo documents.
(groups invocation): Move @exitstatus down after the macro
@primaryAndSupplementaryGroups here, too.
* tests/misc/id-zero.sh: Add new test exercising the new option.
* tests/local.mk (all_tests): Reference it.
* NEWS (New features): Mention the new option.
Fixes http://bugs.gnu.org/9987

NEWS
doc/coreutils.texi
src/group-list.c
src/group-list.h
src/groups.c
src/id.c
tests/local.mk
tests/misc/id-zero.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index d26722ddfbdadf78a42bf69d4f808fe5a0e0acef..23c7b54fd146dafbe72992af527662b3392e3f3e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   du accepts a new option: --inodes to show the number of inodes instead
   of the blocks used.
 
+  id accepts a new option: --zero (-z) to delimit the output entries by
+  a NUL instead of a white space character.
+
   id and ls with -Z report the SMACK security context where available.
   mkdir, mkfifo and mknod with -Z set the SMACK context where available.
 
index 21216b4be7ef643bde8d8adbffa9bbb615fb0c7c..d022c4560c5f7673ce808a8fd151ce003237a9d8 100644 (file)
@@ -14516,9 +14516,20 @@ Print only the security context of the current user.
 If SELinux is disabled then print a warning and
 set the exit status to 1.
 
-@end table
+@item -z
+@itemx --zero
+@opindex -z
+@opindex --zero
+Delimit output items with NUL characters.
+This option is not permitted when using the default format.
 
-@exitstatus
+Example:
+@example
+$ id -Gn --zero
+users <NUL> devs <NUL>
+@end example
+
+@end table
 
 @macro primaryAndSupplementaryGroups{cmd,arg}
 Primary and supplementary groups for a process are normally inherited
@@ -14530,6 +14541,8 @@ database to be consulted afresh, and so will give a different result.
 @end macro
 @primaryAndSupplementaryGroups{id,user argument}
 
+@exitstatus
+
 @node logname invocation
 @section @command{logname}: Print current login name
 
@@ -14587,13 +14600,12 @@ groups [@var{username}]@dots{}
 
 The group lists are equivalent to the output of the command @samp{id -Gn}.
 
-@primaryAndSupplementaryGroups{groups,list of users}
-
 The only options are @option{--help} and @option{--version}.  @xref{Common
 options}.
 
-@exitstatus
+@primaryAndSupplementaryGroups{groups,list of users}
 
+@exitstatus
 
 @node users invocation
 @section @command{users}: Print login names of users currently logged in
index 7d4995b5c07a5f152f4facb5d3fddce8fea0ec6b..d54b0577699df7292b3ca220ec27b6f7a2d6c3c5 100644 (file)
@@ -35,7 +35,7 @@
 extern bool
 print_group_list (const char *username,
                   uid_t ruid, gid_t rgid, gid_t egid,
-                  bool use_names)
+                  bool use_names, char delim)
 {
   bool ok = true;
   struct passwd *pwd = NULL;
@@ -52,7 +52,7 @@ print_group_list (const char *username,
 
   if (egid != rgid)
     {
-      putchar (' ');
+      putchar (delim);
       if (!print_group (egid, use_names))
         ok = false;
     }
@@ -79,7 +79,7 @@ print_group_list (const char *username,
     for (i = 0; i < n_groups; i++)
       if (groups[i] != rgid && groups[i] != egid)
         {
-          putchar (' ');
+          putchar (delim);
           if (!print_group (groups[i], use_names))
             ok = false;
         }
index 3fac8871959706e013bf550ce5588807c4a8ee5a..573de1def8931843be2616ad175622a0d4657064 100644 (file)
@@ -16,4 +16,4 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 bool print_group (gid_t, bool);
-bool print_group_list (const char *, uid_t, gid_t, gid_t, bool);
+bool print_group_list (const char *, uid_t, gid_t, gid_t, bool, char);
index 53332d56b34ab70543b08eb62dafe64f0d2bbdaf..d30c9fb4148ea71c58e5bfec61ca4a1b9f065907 100644 (file)
@@ -114,13 +114,13 @@ main (int argc, char **argv)
       if (rgid == NO_GID && errno)
         error (EXIT_FAILURE, errno, _("cannot get real GID"));
 
-      if (!print_group_list (NULL, ruid, rgid, egid, true))
+      if (!print_group_list (NULL, ruid, rgid, egid, true, ' '))
         ok = false;
       putchar ('\n');
     }
   else
     {
-      /* At least one argument.  Divulge the details of the specified users. */
+      /* At least one argument.  Divulge the details of the specified users.  */
       while (optind < argc)
         {
           struct passwd *pwd = getpwnam (argv[optind]);
@@ -130,7 +130,7 @@ main (int argc, char **argv)
           rgid = egid = pwd->pw_gid;
 
           printf ("%s : ", argv[optind]);
-          if (!print_group_list (argv[optind++], ruid, rgid, egid, true))
+          if (!print_group_list (argv[optind++], ruid, rgid, egid, true, ' '))
             ok = false;
           putchar ('\n');
         }
index 3e7016f7b34235bbf5e744f7065b5640752befb7..a0334ba737fdc7ec6a13f99549c495ea4d1d61e4 100644 (file)
--- a/src/id.c
+++ b/src/id.c
@@ -67,6 +67,7 @@ static struct option const longopts[] =
   {"name", no_argument, NULL, 'n'},
   {"real", no_argument, NULL, 'r'},
   {"user", no_argument, NULL, 'u'},
+  {"zero", no_argument, NULL, 'z'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
@@ -83,14 +84,18 @@ usage (int status)
       fputs (_("\
 Print user and group information for the specified USERNAME,\n\
 or (when USERNAME omitted) for the current user.\n\
-\n\
-  -a              ignore, for compatibility with other versions\n\
-  -Z, --context   print only the security context of the current user\n\
-  -g, --group     print only the effective group ID\n\
-  -G, --groups    print all group IDs\n\
-  -n, --name      print a name instead of a number, for -ugG\n\
-  -r, --real      print the real ID instead of the effective ID, with -ugG\n\
-  -u, --user      print only the effective user ID\n\
+\n"),
+             stdout);
+      fputs (_("\
+  -a             ignore, for compatibility with other versions\n\
+  -Z, --context  print only the security context of the current user\n\
+  -g, --group    print only the effective group ID\n\
+  -G, --groups   print all group IDs\n\
+  -n, --name     print a name instead of a number, for -ugG\n\
+  -r, --real     print the real ID instead of the effective ID, with -ugG\n\
+  -u, --user     print only the effective user ID\n\
+  -z, --zero     delimit entries with NUL characters, not whitespace;\n\
+                   not permitted in default format\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -109,6 +114,7 @@ main (int argc, char **argv)
   int optc;
   int selinux_enabled = (is_selinux_enabled () > 0);
   bool smack_enabled = is_smack_enabled ();
+  bool opt_zero = false;
 
   /* If true, output the list of all group IDs. -G */
   bool just_group_list = false;
@@ -127,7 +133,7 @@ main (int argc, char **argv)
 
   atexit (close_stdout);
 
-  while ((optc = getopt_long (argc, argv, "agnruGZ", longopts, NULL)) != -1)
+  while ((optc = getopt_long (argc, argv, "agnruzGZ", longopts, NULL)) != -1)
     {
       switch (optc)
         {
@@ -162,6 +168,9 @@ main (int argc, char **argv)
         case 'u':
           just_user = true;
           break;
+        case 'z':
+          opt_zero = true;
+          break;
         case 'G':
           just_group_list = true;
           break;
@@ -193,6 +202,10 @@ main (int argc, char **argv)
     error (EXIT_FAILURE, 0,
            _("cannot print only names or real IDs in default format"));
 
+  if (default_format && opt_zero)
+    error (EXIT_FAILURE, 0,
+           _("option --zero not permitted in default format"));
+
   /* If we are on a SELinux/SMACK-enabled kernel, no user is specified, and
      either --context is specified or none of (-u,-g,-G) is specified,
      and we're not in POSIXLY_CORRECT mode, get our context.  Otherwise,
@@ -269,7 +282,8 @@ main (int argc, char **argv)
     }
   else if (just_group_list)
     {
-      if (!print_group_list (argv[optind], ruid, rgid, egid, use_name))
+      if (!print_group_list (argv[optind], ruid, rgid, egid, use_name,
+                             opt_zero ? '\0' : ' '))
         ok = false;
     }
   else if (just_context)
@@ -280,7 +294,7 @@ main (int argc, char **argv)
     {
       print_full_info (argv[optind]);
     }
-  putchar ('\n');
+  putchar (opt_zero ? '\0' : '\n');
 
   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
 }
index b00ff5958a3331ffe3015311bac464f6aa7d2985..8f76b23713f449e84ddc203b726d07b9c36bc6b0 100644 (file)
@@ -277,6 +277,7 @@ all_tests =                                 \
   tests/misc/id-context.sh                     \
   tests/misc/id-groups.sh                      \
   tests/misc/id-setgid.sh                      \
+  tests/misc/id-zero.sh                        \
   tests/misc/md5sum.pl                         \
   tests/misc/md5sum-bsd.sh                     \
   tests/misc/md5sum-newline.pl                 \
diff --git a/tests/misc/id-zero.sh b/tests/misc/id-zero.sh
new file mode 100755 (executable)
index 0000000..cef5672
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+# Exercise "id --zero".
+
+# Copyright (C) 2013 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ id
+
+u="$( id -nu )"
+id || fail=1
+id "$u" || fail=1
+
+# id(1) should refuse --zero in default format.
+echo 'id: option --zero not permitted in default format' > err-exp \
+  || framework_failure_
+id --zero > out 2>err && fail=1
+compare /dev/null out || fail=1
+compare err-exp   err || fail=1
+
+# Create a nice list of users.
+# Add $USER to ensure we have at least one explicit entry.
+users="$u"
+# Add a few typical users to test single group and multiple groups.
+for u in root man postfix sshd nobody ; do
+  id $u >/dev/null 2>&1 && users="$users $u"
+done
+# Add $users and '' (implicit $USER) to list to process.
+printf '%s\n' $users '' >> users || framework_failure_
+
+# Exercise "id -z" with various options.
+printf '\n' > exp || framework_failure_
+:> out || framework_failure_
+
+while read u ; do
+  for o in g gr G Gr u ur ; do
+    for n in '' n ; do
+      printf   '%s: ' "id -${o}${n}[z] $u" >> exp || framework_failure_
+      printf '\n%s: ' "id -${o}${n}[z] $u" >> out || framework_failure_
+      id -${o}${n}  $u >> exp || fail=1
+      id -${o}${n}z $u  > tmp || fail=1
+      head -c-1 < tmp >> out || framework_failure_
+    done
+  done
+done < users
+printf '\n' >> out || framework_failure_
+tr '\0' ' ' < out > out2 || framework_failure_
+compare exp out2 || fail=1
+
+Exit $fail