On Mon, 19 Apr 2021, Carl Edquist wrote:
I'm submitting for your consideration here a patch to add the standard
'--files0-from=FILE' option to ls.
Oops! Somehow I ended up attaching an old version of the patch - this one
includes some cleanups and help output.
Thanks,
Carl
From 30fb711962bbd432aea4268baf93d7ba17aae4bc Mon Sep 17 00:00:00 2001
From: Carl Edquist <edqu...@cs.wisc.edu>
Date: Fri, 10 May 2019 17:05:47 -0500
Subject: [PATCH] ls: add --files0-from=FILE option
Useful for things like
find [...] -type f -print0 | ls -lrSh --files0-from=-
where an attempt to do the same with 'find -print0 | xargs -0' or
'find -exec' would fail to fit all the file names onto a single command
line, and thus ls would not sort all the input items together, nor
align the columns from -l across all items.
* src/ls.c (files_from): New var for input filename.
(long_options): Add new long option.
(read_files0): Add helper function to consume files0 input.
(main): Add logic for files_from handling.
(decode_switches): Handle FILES0_FROM_OPTION.
* tests/ls/files0-from-option.sh: Excercise new option.
* tests/local.mk: Include new test.
* doc/coreutils.texi: Document --files0-from=FILE option.
* NEWS: Mention the new feature.
---
NEWS | 3 ++
doc/coreutils.texi | 2 ++
src/ls.c | 69 +++++++++++++++++++++++++++++++++++++++---
tests/local.mk | 1 +
tests/ls/files0-from-option.sh | 40 ++++++++++++++++++++++++
5 files changed, 111 insertions(+), 4 deletions(-)
create mode 100755 tests/ls/files0-from-option.sh
diff --git a/NEWS b/NEWS
index 090fbc7..7f7f86b 100644
--- a/NEWS
+++ b/NEWS
@@ -73,6 +73,9 @@ GNU coreutils NEWS -*- outline -*-
ls now accepts the --sort=width option, to sort by file name width.
This is useful to more compactly organize the default vertical column output.
+ ls now accepts the --files0-from=FILE option, where FILE contains a
+ list of NUL-terminated file names.
+
nl --line-increment can now take a negative number to decrement the count.
** Improvements
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 3e3aedb..e008746 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -7467,6 +7467,8 @@ option has been specified (@option{--classify} (@option{-F}),
@option{--dereference} (@option{-L}), or
@option{--dereference-command-line} (@option{-H})).
+@filesZeroFromOption{ls,,sorted output (and aligned columns in -l mode)}
+
@item --group-directories-first
@opindex --group-directories-first
Group all the directories before the files and then sort the
diff --git a/src/ls.c b/src/ls.c
index 4586b5e..0d0678b 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -102,6 +102,7 @@
#include "mpsort.h"
#include "obstack.h"
#include "quote.h"
+#include "readtokens0.h"
#include "smack.h"
#include "stat-size.h"
#include "stat-time.h"
@@ -356,6 +357,9 @@ static bool align_variable_outer_quotes;
static void **sorted_file;
static size_t sorted_file_alloc;
+/* input filename for --files0-from */
+static char *files_from;
+
/* When true, in a color listing, color each symlink name according to the
type of file it points to. Otherwise, color them according to the 'ln'
directive in LS_COLORS. Dangling (orphan) symlinks are treated specially,
@@ -833,6 +837,7 @@ enum
COLOR_OPTION,
DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION,
FILE_TYPE_INDICATOR_OPTION,
+ FILES0_FROM_OPTION,
FORMAT_OPTION,
FULL_TIME_OPTION,
GROUP_DIRECTORIES_FIRST_OPTION,
@@ -892,6 +897,7 @@ static struct option const long_options[] =
{"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
{"context", no_argument, 0, 'Z'},
{"author", no_argument, NULL, AUTHOR_OPTION},
+ {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -1626,11 +1632,38 @@ signal_restore (void)
signal_setup (false);
}
+static void
+read_files0(struct Tokens *tokp)
+{
+ FILE *stream;
+ if (STREQ (files_from, "-"))
+ stream = stdin;
+ else
+ {
+ stream = fopen (files_from, "r");
+ if (stream == NULL)
+ die (EXIT_FAILURE, errno, _("cannot open %s for reading"),
+ quoteaf (files_from));
+ }
+
+ readtokens0_init (tokp);
+
+ if (! readtokens0 (stream, tokp) || fclose (stream) != 0)
+ die (LS_FAILURE, 0, _("cannot read file names from %s"),
+ quoteaf (files_from));
+
+ if (! tokp->n_tok)
+ die (LS_FAILURE, 0, _("no input from %s"),
+ quoteaf (files_from));
+}
+
int
main (int argc, char **argv)
{
int i;
struct pending *thispend;
+ struct Tokens tok;
+ char **files;
int n_files;
initialize_main (&argc, &argv);
@@ -1727,6 +1760,23 @@ main (int argc, char **argv)
clear_files ();
n_files = argc - i;
+ files = argv + i;
+
+ if (files_from)
+ {
+ if (n_files > 0)
+ {
+ error (0, 0, _("extra operand %s"), quoteaf (argv[i]));
+ fprintf (stderr, "%s\n",
+ _("file operands cannot be combined with --files0-from"));
+ usage (LS_FAILURE);
+ }
+
+ read_files0(&tok);
+
+ files = tok.tok;
+ n_files = tok.n_tok;
+ }
if (n_files <= 0)
{
@@ -1736,9 +1786,8 @@ main (int argc, char **argv)
queue_directory (".", NULL, true);
}
else
- do
- gobble_file (argv[i++], unknown, NOT_AN_INODE_NUMBER, true, "");
- while (i < argc);
+ for (i = 0; i < n_files; i++)
+ gobble_file (files[i], unknown, NOT_AN_INODE_NUMBER, true, "");
if (cwd_n_used)
{
@@ -1833,6 +1882,9 @@ main (int argc, char **argv)
hash_free (active_dir_set);
}
+ if (files_from)
+ readtokens0_free (&tok);
+
return exit_status;
}
@@ -2201,6 +2253,10 @@ decode_switches (int argc, char **argv)
time_type = XARGMATCH ("--time", optarg, time_args, time_types);
break;
+ case FILES0_FROM_OPTION:
+ files_from = optarg;
+ break;
+
case FORMAT_OPTION:
format = XARGMATCH ("--format", optarg, format_args, format_types);
break;
@@ -5382,7 +5438,10 @@ usage (int status)
emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
+ printf (_("Usage: %s [OPTION]... [FILE]...\n"
+ " or: %s [OPTION]... --files0-from=F\n"),
+ program_name, program_name);
+
fputs (_("\
List information about the FILEs (the current directory by default).\n\
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\
@@ -5422,6 +5481,8 @@ Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\
WHEN can be 'always' (default if omitted),\n\
'auto', or 'never'\n\
--file-type likewise, except do not append '*'\n\
+ --files0-from=F read NUL-terminated file names from file F;\n\
+ if F is -, then read names from standard input\n\
--format=WORD across -x, commas -m, horizontal -x, long -l,\n\
single-column -1, verbose -l, vertical -C\n\
--full-time like -l --time-style=full-iso\n\
diff --git a/tests/local.mk b/tests/local.mk
index a44feca..7bf028a 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -605,6 +605,7 @@ all_tests = \
tests/ls/dangle.sh \
tests/ls/dired.sh \
tests/ls/file-type.sh \
+ tests/ls/files0-from-option.sh \
tests/ls/follow-slink.sh \
tests/ls/getxattr-speedup.sh \
tests/ls/group-dirs.sh \
diff --git a/tests/ls/files0-from-option.sh b/tests/ls/files0-from-option.sh
new file mode 100755
index 0000000..03c987c
--- /dev/null
+++ b/tests/ls/files0-from-option.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Exercise the --files0-from option.
+
+# Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ls
+
+touch abc || framework_failure_
+touch def || framework_failure_
+touch xyz || framework_failure_
+
+printf '%s\0' abc def xyz > names || framework_failure_
+
+ls --files0-from=names > out1 || fail=1
+cat names | ls --files0-from=- > out2 || fail=1
+
+cat <<\EOF > exp || framework_failure_
+abc
+def
+xyz
+EOF
+
+compare exp out1 || fail=1
+compare exp out2 || fail=1
+
+Exit $fail
--
2.9.0