]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
wc: add --total={auto,never,always,only} option
authorPádraig Brady <P@draigBrady.com>
Sat, 25 Jun 2022 23:27:06 +0000 (00:27 +0100)
committerPádraig Brady <P@draigBrady.com>
Mon, 26 Sep 2022 14:04:56 +0000 (15:04 +0100)
without this option, control of when the total is output
is quite awkward. Consider trying to suppress the total line,
which could be achieved with something like:

   wc-no-total() { wc "$@" /dev/null | head -n-2; }

As well as being non obvious, it's also non general.
It would give a non failure, but zero count if passed a file on stdin.
Also it doesn't work in conjunction with the --files0-from option,
which would need to be handled differently with something like:

   { find files -print0; printf '%s\0' /dev/null; } |
   wc --files0-from=- |
   head -n2

Also getting just the total can be awkward as file names
are only suppressed when processing stdin, and
also a total line is only printed if processing more than one file.
For completness this might be achieved currently with:

  wc-only-total() {
    wc "$@" |
    tail -n1 |
    sed 's/^ *//; s/ [^ 0-9]*$//'
  }

* src/wc.c: Add new --total option.
* tests/misc/wc-total.sh: New test suite for the new option.
* tests/local.mk: Reference the new test.
* doc/coreutils.texi (wc invocation): Document the new option.
* THANKS.in: Add suggestor.
* NEWS: Mention the new feature.

NEWS
THANKS.in
doc/coreutils.texi
src/wc.c
tests/local.mk
tests/misc/wc-total.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 7f4eab921c5608689d44b3f18ba6675edd5efcdb..fe3c3137aea217473b50ecffe415c2bbb2949eb1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -55,6 +55,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   ls now supports the --time=modification option, to explicitly
   select the default mtime timestamp for display and sorting.
 
+  wc now accepts the --total={auto,never,always,only} option
+  to give explicit control over when the total is output.
+
 ** Improvements
 
   date --debug now diagnoses if multiple --date or --set options are
index b3827a22caba9dfe7d374da848f000b8240f3021..a3d179a5572afe4c9b654e64eb753cc972f816de 100644 (file)
--- a/THANKS.in
+++ b/THANKS.in
@@ -160,6 +160,7 @@ David Luyer                         david_luyer@pacific.net.au
 David Madore                        david.madore@ens.fr
 David Malone                        dwmalone@cnri.dit.ie
 David Matei                         matei@cs.toronto.edu
+David Pinto                         carandraug+dev@gmail.com
 Davide Canova                       kc.canova@gmail.com
 Dawson Engler                       engler@stanford.edu
 Dean Gaudet                         dean-savannah@arctic.org
index f4be26b445f5685f0e2bf7539c344dfc89daa721..699948a8b0f763286a5b9dd8af036391a1f2c58e 100644 (file)
@@ -3769,10 +3769,12 @@ wc [@var{option}]@dots{} [@var{file}]@dots{}
 
 @cindex total counts
 @command{wc} prints one line of counts for each file, and if the file was
-given as an argument, it prints the file name following the counts.  If
-more than one @var{file} is given, @command{wc} prints a final line
-containing the cumulative counts, with the file name @file{total}.  The
-counts are printed in this order: newlines, words, characters, bytes,
+given as an argument, it prints the file name following the counts. By default
+if more than one @var{file} is given, @command{wc} prints a final line
+containing the cumulative counts, with the file name @file{total}.
+This @samp{total} line can be controlled with the @option{--total} option,
+which is a GNU extension.
+The counts are printed in this order: newlines, words, characters, bytes,
 maximum line length.
 Each count is printed right-justified in a field with at least one
 space between fields so that the numbers and file names normally line
@@ -3839,6 +3841,29 @@ Tabs are set at every 8th column.
 Display widths of wide characters are considered.
 Non-printable characters are given 0 width.
 
+@item --total=@var{when}
+@opindex --total=@var{when}
+Control when and how the final line with cumulative counts is printed.
+@var{when} is one of:
+@itemize @bullet
+@item auto
+@vindex auto @r{total option}
+- This is the default mode of @command{wc} when no @option{--total}
+option is specified.  Output a total line if more than one @var{file}
+is specified.
+@item always
+@vindex always @r{total option}
+- Always output a total line, irrespective of the number of files processed.
+@item only
+@vindex only @r{total option}
+- Only output total counts.  I.e., don't print individual file counts,
+suppress any leading spaces, and don't print the @samp{total} word itself,
+to simplify subsequent processing.
+@item never
+@vindex none @r{total option}
+- Never output a total line.
+@end itemize
+
 @macro filesZeroFromOption{cmd,withTotalOption,subListOutput}
 @item --files0-from=@var{file}
 @opindex --files0-from=@var{file}
index dee8233a606408ac46ad2bf785fa7572d7d7ad41..bc52a8c0ea59c3708d5d06193a03404df33738a7 100644 (file)
--- a/src/wc.c
+++ b/src/wc.c
@@ -27,6 +27,7 @@
 #include <wctype.h>
 
 #include "system.h"
+#include "argmatch.h"
 #include "argv-iter.h"
 #include "die.h"
 #include "error.h"
@@ -112,6 +113,7 @@ enum
 {
   DEBUG_PROGRAM_OPTION = CHAR_MAX + 1,
   FILES0_FROM_OPTION,
+  TOTAL_OPTION,
 };
 
 static struct option const longopts[] =
@@ -123,11 +125,30 @@ static struct option const longopts[] =
   {"debug", no_argument, NULL, DEBUG_PROGRAM_OPTION},
   {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
   {"max-line-length", no_argument, NULL, 'L'},
+  {"total", required_argument, NULL, TOTAL_OPTION},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
 };
 
+enum total_type
+  {
+    total_auto,         /* 0: default or --total=auto */
+    total_always,       /* 1: --total=always */
+    total_only,         /* 2: --total=only */
+    total_never         /* 3: --total=never */
+  };
+static char const *const total_args[] =
+{
+  "auto", "always", "only", "never", NULL
+};
+static enum total_type const total_types[] =
+{
+  total_auto, total_always, total_only, total_never
+};
+ARGMATCH_VERIFY (total_args, total_types);
+static enum total_type total_mode = total_auto;
+
 #ifdef USE_AVX2_WC_LINECOUNT
 static bool
 avx2_supported (void)
@@ -215,6 +236,10 @@ the following order: newline, word, character, byte, maximum line length.\n\
                            If F is - then read names from standard input\n\
   -L, --max-line-length  print the maximum display width\n\
   -w, --words            print the word counts\n\
+"), stdout);
+      fputs (_("\
+      --total=WHEN       when to print a line with total counts;\n\
+                           WHEN can be: auto, always, only, never\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -676,7 +701,8 @@ wc (int fd, char const *file_x, struct fstatus *fstatus, off_t current_pos)
   if (count_chars < print_chars)
     chars = bytes;
 
-  write_counts (lines, words, chars, bytes, linelength, file_x);
+  if (total_mode != total_only)
+    write_counts (lines, words, chars, bytes, linelength, file_x);
   total_lines += lines;
   total_words += words;
   total_chars += chars;
@@ -840,6 +866,10 @@ main (int argc, char **argv)
         files_from = optarg;
         break;
 
+      case TOTAL_OPTION:
+        total_mode = XARGMATCH ("--total", optarg, total_args, total_types);
+        break;
+
       case_GETOPT_HELP_CHAR;
 
       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -913,7 +943,10 @@ main (int argc, char **argv)
     xalloc_die ();
 
   fstatus = get_input_fstatus (nfiles, files);
-  number_width = compute_number_width (nfiles, fstatus);
+  if (total_mode == total_only)
+    number_width = 1;  /* No extra padding, since no alignment requirement.  */
+  else
+    number_width = compute_number_width (nfiles, fstatus);
 
   ok = true;
   for (int i = 0; /* */; i++)
@@ -987,9 +1020,11 @@ main (int argc, char **argv)
   if (read_tokens)
     readtokens0_free (&tok);
 
-  if (1 < argv_iter_n_args (ai))
+  if (total_mode != total_never
+      && (total_mode != total_auto || 1 < argv_iter_n_args (ai)))
     write_counts (total_lines, total_words, total_chars, total_bytes,
-                  max_line_length, _("total"));
+                  max_line_length,
+                  total_mode != total_only ? _("total") : NULL);
 
   argv_iter_free (ai);
 
index 0496c2873ac268a7021729af3b973bbf6e8a7dea..895797bf9039465e7f1e756c27687f248f48c865 100644 (file)
@@ -280,6 +280,7 @@ all_tests =                                 \
   tests/misc/wc-nbsp.sh                                \
   tests/misc/wc-parallel.sh                    \
   tests/misc/wc-proc.sh                                \
+  tests/misc/wc-total.sh                       \
   tests/misc/cat-E.sh                          \
   tests/misc/cat-proc.sh                       \
   tests/misc/cat-buf.sh                                \
diff --git a/tests/misc/wc-total.sh b/tests/misc/wc-total.sh
new file mode 100755 (executable)
index 0000000..cc6123a
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+# Show that wc's --total option works.
+
+# Copyright (C) 2022 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_ wc
+
+printf '%s\n' '2' > 2b || framework_failure_
+printf '%s\n' '2 words' > 2w || framework_failure_
+
+returns_ 1 wc --total 2b 2w > out || fail=1
+
+wc --total=never 2b 2w > out || fail=1
+cat <<\EOF > exp || framework_failure_
+ 1  1  2 2b
+ 1  2  8 2w
+EOF
+compare exp out || fail=1
+
+wc --total=only 2b 2w > out || fail=1
+cat <<\EOF > exp || framework_failure_
+2 3 10
+EOF
+compare exp out || fail=1
+
+wc --total=always 2b > out || fail=1
+test "$(wc -l < out)" = 2 || fail=1
+
+Exit $fail