>From 4217f8a0fc62fa142752e8fc57cd3824ee7b7641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Wed, 12 Dec 2012 19:54:12 +0000 Subject: [PATCH] readlink: support multiple command line arguments This allows efficient processing of processing 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. * NEWS: Mention the improvement. * THANKS.in: Suggested by Aaron Davies. --- NEWS | 2 + THANKS.in | 1 + doc/coreutils.texi | 14 ++++++++--- src/readlink.c | 59 +++++++++++++++++++++++------------------------ tests/local.mk | 1 + tests/readlink/multi.sh | 34 +++++++++++++++++++++++++++ 6 files changed, 77 insertions(+), 34 deletions(-) create mode 100755 tests/readlink/multi.sh diff --git a/NEWS b/NEWS index 1ee2c17..7843f3c 100644 --- a/NEWS +++ b/NEWS @@ -61,6 +61,8 @@ GNU coreutils NEWS -*- outline -*- ** Improvements + readlink now supports multiple arguments. + 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 9009795..c2651e7 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). ??? address@hidden A Costa address@hidden +Aaron Davies address@hidden Aaron Hawley address@hidden Achim Blumensath address@hidden Adam Jimerson address@hidden diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 5f8fad7..d393063 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,7 @@ as a directory. @itemx --no-newline @opindex -n @opindex --no-newline -Do not output the trailing newline. +Do not print the output delimiter. @item -s @itemx -q @@ -9807,6 +9807,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/src/readlink.c b/src/readlink.c index e025bf9..de8211f 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 a delimiter after each item\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,26 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } - fname = argv[optind++]; - - if (optind < argc) - { - error (0, 0, _("extra operand %s"), quote (argv[optind])); - usage (EXIT_FAILURE); - } - - 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 5eeddd5..efdd896 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 0000000..b28a705 --- /dev/null +++ b/tests/readlink/multi.sh @@ -0,0 +1,34 @@ +#!/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 +readlink -m --zero /1 /1 > out +compare exp out || fail=1 + +Exit $fail -- 1.7.6.4