From a05a326b0cd7c4146e60ea4d66440d4a28dfb41a Mon Sep 17 00:00:00 2001 From: Pádraig Brady Date: Wed, 12 Dec 2012 19:54:12 +0000 Subject: readlink: support multiple command line arguments This allows efficient processing of multiple files, while also increasing compatibility with BSD's readlink(1). We also add the -z, --zero option to delimit output items with the NUL character which disambiguates output in the presence of '\n' characters. * src/readlink.c (usage): Add the --zero description, and also adjust the description of --no-newline accordingly. (main): Handle the -z option and iterate over multiple arguments. Also as in commit v8.15-24-g9d46b25 we use fputs() and putchar() rather than printf() for performance reasons. * doc/coreutils.texi (readlink invocation): Document the new --zero option, adjust the --no-newline description, and tweak the general info to indicate multiple files are supported. * tests/readlink/multi.sh: A new test for the new functionality. * tests/local.mk: Reference the new test. * man/readlink.x: Adjust the summary and also reference realpath. * NEWS: Mention the improvement. * THANKS.in: Suggested by Aaron Davies. --- NEWS | 3 +++ THANKS.in | 1 + doc/coreutils.texi | 15 +++++++++---- man/readlink.x | 4 ++-- src/readlink.c | 60 +++++++++++++++++++++++++++---------------------- tests/local.mk | 1 + tests/readlink/multi.sh | 46 +++++++++++++++++++++++++++++++++++++ 7 files changed, 97 insertions(+), 33 deletions(-) create mode 100755 tests/readlink/multi.sh diff --git a/NEWS b/NEWS index 1ee2c17a8..e4472dfac 100644 --- a/NEWS +++ b/NEWS @@ -61,6 +61,9 @@ GNU coreutils NEWS -*- outline -*- ** Improvements + readlink now supports multiple arguments, and a complementary + -z, --zero option to delimit output items with the NUL character. + stat and tail now know about CEPH. stat -f --format=%T now reports the file system type, and tail -f uses polling for files on CEPH file systems. diff --git a/THANKS.in b/THANKS.in index 900979517..c2651e7ee 100644 --- a/THANKS.in +++ b/THANKS.in @@ -14,6 +14,7 @@ note to the bug-report mailing list (as seen at end of e.g., cp --help). ??? kytek@cybercomm.net A Costa agcosta@gis.net +Aaron Davies aaron.davies@gmail.com Aaron Hawley ashawley@uvm.edu Achim Blumensath blume@corona.oche.de Adam Jimerson vendion@charter.net diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 5f8fad774..0646f8242 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -9737,20 +9737,20 @@ Set the default SELinux security context to be used for created files. @item Readlink mode -@command{readlink} outputs the value of the given symbolic link. +@command{readlink} outputs the value of the given symbolic links. If @command{readlink} is invoked with an argument other than the name of a symbolic link, it produces no output and exits with a nonzero exit code. @item Canonicalize mode -@command{readlink} outputs the absolute name of the given file which contains +@command{readlink} outputs the absolute name of the given files which contain no @file{.}, @file{..} components nor any repeated separators (@file{/}) or symbolic links. @end table @example -readlink [@var{option}] @var{file} +readlink [@var{option}]@dots{} @var{file}@dots{} @end example By default, @command{readlink} operates in readlink mode. @@ -9789,7 +9789,8 @@ as a directory. @itemx --no-newline @opindex -n @opindex --no-newline -Do not output the trailing newline. +Do not print the output delimiter, when a single @var{file} is specified. +Print a warning if specified along with multiple @var{file}s. @item -s @itemx -q @@ -9807,6 +9808,12 @@ Suppress most error messages. @opindex --verbose Report error messages. +@item -z +@itemx --zero +@opindex -z +@opindex --zero +Separate output items with @sc{nul} characters. + @end table The @command{readlink} utility first appeared in OpenBSD 2.1. diff --git a/man/readlink.x b/man/readlink.x index 79ba75853..6b28bca47 100644 --- a/man/readlink.x +++ b/man/readlink.x @@ -1,6 +1,6 @@ [NAME] -readlink \- print value of a symbolic link or canonical file name +readlink \- print resolved symbolic links or canonical file names [DESCRIPTION] .\" Add any additional description here [SEE ALSO] -readlink(2) +readlink(2), realpath(1), realpath(3) diff --git a/src/readlink.c b/src/readlink.c index e025bf916..ff7d67fa7 100644 --- a/src/readlink.c +++ b/src/readlink.c @@ -25,7 +25,6 @@ #include "canonicalize.h" #include "error.h" #include "areadlink.h" -#include "quote.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "readlink" @@ -47,6 +46,7 @@ static struct option const longopts[] = {"quiet", no_argument, NULL, 'q'}, {"silent", no_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, + {"zero", no_argument, NULL, 'z'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -59,7 +59,7 @@ usage (int status) emit_try_help (); else { - printf (_("Usage: %s [OPTION]... FILE\n"), program_name); + printf (_("Usage: %s [OPTION]... FILE...\n"), program_name); fputs (_("Print value of a symbolic link or canonical file name\n\n"), stdout); fputs (_("\ @@ -77,10 +77,11 @@ usage (int status) every component of the given name recursively,\ \n\ without requirements on components existence\n\ - -n, --no-newline do not output the trailing newline\n\ + -n, --no-newline do not output the trailing delimiter\n\ -q, --quiet,\n\ -s, --silent suppress most error messages\n\ -v, --verbose report error messages\n\ + -z, --zero separate output with NUL rather than newline\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -94,14 +95,9 @@ main (int argc, char **argv) { /* If not -1, use this method to canonicalize. */ int can_mode = -1; - - /* File name to canonicalize. */ - const char *fname; - - /* Result of canonicalize. */ - char *value; - + int status = EXIT_SUCCESS; int optc; + bool use_nuls = false; initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -111,7 +107,7 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "efmnqsv", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "efmnqsvz", longopts, NULL)) != -1) { switch (optc) { @@ -134,6 +130,9 @@ main (int argc, char **argv) case 'v': verbose = true; break; + case 'z': + use_nuls = true; + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -147,26 +146,33 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } - fname = argv[optind++]; - - if (optind < argc) + if (argc - optind > 1) { - error (0, 0, _("extra operand %s"), quote (argv[optind])); - usage (EXIT_FAILURE); + if (no_newline) + error (0, 0, _("ignoring --no-newline with multiple arguments")); + no_newline = false; } - value = (can_mode != -1 - ? canonicalize_filename_mode (fname, can_mode) - : areadlink_with_size (fname, 63)); - if (value) + for (; optind < argc; ++optind) { - printf ("%s%s", value, (no_newline ? "" : "\n")); - free (value); - return EXIT_SUCCESS; + const char *fname = argv[optind]; + char *value = (can_mode != -1 + ? canonicalize_filename_mode (fname, can_mode) + : areadlink_with_size (fname, 63)); + if (value) + { + fputs (value, stdout); + if (! no_newline) + putchar (use_nuls ? '\0' : '\n'); + free (value); + } + else + { + status = EXIT_FAILURE; + if (verbose) + error (0, errno, "%s", fname); + } } - if (verbose) - error (EXIT_FAILURE, errno, "%s", fname); - - return EXIT_FAILURE; + return status; } diff --git a/tests/local.mk b/tests/local.mk index 5eeddd53b..efdd896bf 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -602,6 +602,7 @@ all_tests = \ tests/readlink/can-e.sh \ tests/readlink/can-f.sh \ tests/readlink/can-m.sh \ + tests/readlink/multi.sh \ tests/readlink/rl-1.sh \ tests/rmdir/fail-perm.sh \ tests/rmdir/ignore.sh \ diff --git a/tests/readlink/multi.sh b/tests/readlink/multi.sh new file mode 100755 index 000000000..877cce6b3 --- /dev/null +++ b/tests/readlink/multi.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# test multiple argument handling. + +# Copyright (C) 2012 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 . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ readlink + +touch regfile || framework_failure_ +ln -s regfile link1 || framework_failure_ + +readlink link1 link1 || fail=1 +readlink link1 link2 && fail=1 +readlink link1 link2 link1 && fail=1 +readlink -m link1 link2 || fail=1 + +printf '/1\0/1\0' > exp || framework_failure_ +readlink -m --zero /1 /1 > out || fail=1 +compare exp out || fail=1 + +# The largely redundant --no-newline option is ignored with multiple args. +# Note BSD's readlink suppresses all delimiters, even with multiple args, +# but that functionality was not thought useful. +readlink -n -m --zero /1 /1 > out || fail=1 +compare exp out || fail=1 + +# Note the edge case that the last xargs run may not have a delimiter +rm out || framework_failure_ +printf '/1\0/1\0/1' > exp || framework_failure_ +printf '/1 /1 /1' | xargs -n2 readlink -n -m --zero >> out || fail=1 +compare exp out || fail=1 + +Exit $fail -- cgit v1.2.3-54-g00ecf