From 02db67380215d0b789f0854609772230776ead14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Sun, 26 Jun 2022 00:27:06 +0100 Subject: [PATCH] wc: add --total={auto,never,always,only} option 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 zero could 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 | 3 +++ THANKS.in | 1 + doc/coreutils.texi | 33 ++++++++++++++++++++++++++++---- src/wc.c | 43 ++++++++++++++++++++++++++++++++++++++---- tests/local.mk | 1 + tests/misc/wc-total.sh | 43 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 8 deletions(-) create mode 100755 tests/misc/wc-total.sh diff --git a/NEWS b/NEWS index d1c400e67..561587a77 100644 --- 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 diff --git a/THANKS.in b/THANKS.in index b3827a22c..a3d179a55 100644 --- 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 diff --git a/doc/coreutils.texi b/doc/coreutils.texi index f4be26b44..699948a8b 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -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} diff --git a/src/wc.c b/src/wc.c index dee8233a6..bc52a8c0e 100644 --- a/src/wc.c +++ b/src/wc.c @@ -27,6 +27,7 @@ #include #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); diff --git a/tests/local.mk b/tests/local.mk index 0496c2873..895797bf9 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -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 index 000000000..aaaad0580 --- /dev/null +++ b/tests/misc/wc-total.sh @@ -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 . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ wc + +echo 2 > 2b || framework_failure_ +echo 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 -- 2.26.2