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

Reply via email to