On Tuesday 02 September 2008 16:01:13 you wrote:
> Would you please amend/squash the patch below into your patch and
> adjust the line lengths of the log message to be <= 72, so that
> the generated ChangeLog lines don't wrap?
No problem, here is (I hope) complete patch. Also thanks for the regexp, it 
was helpful for the AWK based test as well.


Kamil
From d402ecc9c1c1657cd5b213758968efd880364831 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <[EMAIL PROTECTED]>
Date: Wed, 3 Sep 2008 10:33:06 +0200
Subject: [PATCH] df: new option: --total to produce grand total

* src/df.c (add_uint_with_neg_flag): New function to add two integral
values with separate negation flag.
(show_dev): New parameter force_fsu to display numbers directly. Collect
summary statistics on each printed device.
(usage): Mention new option --total in --help.
(main): Initialize summary on program start. Handle new option --total.
* tests/df/total: Dummy test case for new --total option.
* tests/df/total-awk: Better test case for new --total option (requires
awk).
* doc/coreutils.texi: Mention new parameter --total.
* NEWS: Mention the change.
* TODO: Removed completed task.
---
 NEWS               |    3 ++
 TODO               |    2 -
 doc/coreutils.texi |    7 ++++
 src/df.c           |   81 ++++++++++++++++++++++++++++++++++++++++++++++++----
 tests/Makefile.am  |    2 +
 tests/check.mk     |    1 +
 tests/df/total     |   42 +++++++++++++++++++++++++++
 tests/df/total-awk |   78 ++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 208 insertions(+), 8 deletions(-)
 create mode 100755 tests/df/total
 create mode 100755 tests/df/total-awk

diff --git a/NEWS b/NEWS
index 4979dd5..97e6313 100644
--- a/NEWS
+++ b/NEWS
@@ -42,6 +42,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   sort accepts a new option --version-sort (-V, --sort=version),
   specifying that ordering is to be based on strverscmp(3).
 
+  df accepts a new option --total, which produces a grand total of all
+  arguments after all arguments have been processed.
+
 ** Bug fixes
 
   chcon --verbose now prints a newline after each message
diff --git a/TODO b/TODO
index c7b095c..c964933 100644
--- a/TODO
+++ b/TODO
@@ -66,8 +66,6 @@ Should printf '\0123' print "\n3"?
 
 printf: consider adapting builtins/printf.def from bash
 
-df: add `--total' option, suggested here http://bugs.debian.org/186007
-
 tail: don't use xlseek; it *exits*.
   Instead, maybe use a macro and return nonzero.
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 3a04176..be59ae9 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -9692,6 +9692,13 @@ pseudo-file-systems, such as automounter entries.
 Scale sizes by @var{size} before printing them (@pxref{Block size}).
 For example, @option{-BG} prints sizes in units of 1,073,741,824 bytes.
 
[EMAIL PROTECTED] --total
[EMAIL PROTECTED] --total
[EMAIL PROTECTED] grand total of disk size, usage and available space
+Print a grand total of all arguments after all arguments have
+been processed.  This can be used to find out the total disk size, usage
+and available space of all listed devices.
+
 @optHumanReadable
 
 @item -H
diff --git a/src/df.c b/src/df.c
index 0769a1e..0bb3b1e 100644
--- a/src/df.c
+++ b/src/df.c
@@ -108,6 +108,12 @@ static struct mount_entry *mount_list;
 /* If true, print file system type as well.  */
 static bool print_type;
 
+/* If true, print a grand total at the end.  */
+static bool print_grand_total;
+
+/* Grand total data. */
+static struct fs_usage grand_fsu;
+
 /* For long options that have no equivalent short option, use a
    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
 enum
@@ -129,6 +135,7 @@ static struct option const long_options[] =
   {"print-type", no_argument, NULL, 'T'},
   {"sync", no_argument, NULL, SYNC_OPTION},
   {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
+  {"total", no_argument, NULL, 'c'},
   {"type", required_argument, NULL, 't'},
   {"exclude-type", required_argument, NULL, 'x'},
   {GETOPT_HELP_OPTION_DECL},
@@ -247,6 +254,41 @@ df_readable (bool negative, uintmax_t n, char *buf,
     }
 }
 
+/* Logical equivalence */
+#define LOG_EQ(a, b) (!(a) == !(b))
+
+/* Add integral value while using uintmax_t for value part and separate
+   negation flag. It adds value of SRC and SRC_NEG to DEST and DEST_NEG.
+   The result will be in DEST and DEST_NEG.  See df_readable to understand
+   how the negation flag is used.  */
+static void
+add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg,
+			uintmax_t src, bool src_neg)
+{
+  if (LOG_EQ (*dest_neg, src_neg))
+    {
+      *dest += src;
+      return;
+    }
+
+  if (*dest_neg)
+    *dest = -*dest;
+
+  if (src_neg)
+    src = -src;
+
+  if (src < *dest)
+    *dest -= src;
+  else
+    {
+      *dest = src - *dest;
+      *dest_neg = src_neg;
+    }
+
+  if (*dest_neg)
+    *dest = -*dest;
+}
+
 /* Display a space listing for the disk device with absolute file name DISK.
    If MOUNT_POINT is non-NULL, it is the name of the root of the
    file system on DISK.
@@ -263,7 +305,8 @@ df_readable (bool negative, uintmax_t n, char *buf,
 static void
 show_dev (char const *disk, char const *mount_point,
 	  char const *stat_file, char const *fstype,
-	  bool me_dummy, bool me_remote)
+	  bool me_dummy, bool me_remote,
+	  const struct fs_usage *force_fsu)
 {
   struct fs_usage fsu;
   char buf[3][LONGEST_HUMAN_READABLE + 2];
@@ -296,7 +339,9 @@ show_dev (char const *disk, char const *mount_point,
   if (!stat_file)
     stat_file = mount_point ? mount_point : disk;
 
-  if (get_fs_usage (stat_file, disk, &fsu))
+  if (force_fsu)
+    fsu = *force_fsu;
+  else if (get_fs_usage (stat_file, disk, &fsu))
     {
       error (0, errno, "%s", quote (stat_file));
       exit_status = EXIT_FAILURE;
@@ -347,6 +392,9 @@ show_dev (char const *disk, char const *mount_point,
       available = fsu.fsu_ffree;
       negate_available = false;
       available_to_root = available;
+
+      grand_fsu.fsu_files += total;
+      grand_fsu.fsu_ffree += available;
     }
   else
     {
@@ -373,6 +421,12 @@ show_dev (char const *disk, char const *mount_point,
       negate_available = (fsu.fsu_bavail_top_bit_set
 			  & (available != UINTMAX_MAX));
       available_to_root = fsu.fsu_bfree;
+
+      grand_fsu.fsu_blocks += input_units * total;
+      grand_fsu.fsu_bfree  += input_units * available_to_root;
+      add_uint_with_neg_flag (&grand_fsu.fsu_bavail,
+			      &grand_fsu.fsu_bavail_top_bit_set,
+			      input_units * available, negate_available);
     }
 
   used = UINTMAX_MAX;
@@ -550,7 +604,7 @@ show_disk (char const *disk)
     {
       show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
 		best_match->me_type, best_match->me_dummy,
-		best_match->me_remote);
+		best_match->me_remote, NULL);
       return true;
     }
 
@@ -654,7 +708,8 @@ show_point (const char *point, const struct stat *statp)
 
   if (best_match)
     show_dev (best_match->me_devname, best_match->me_mountdir, point,
-	      best_match->me_type, best_match->me_dummy, best_match->me_remote);
+	      best_match->me_type, best_match->me_dummy, best_match->me_remote,
+	      NULL);
   else
     {
       /* We couldn't find the mount entry corresponding to POINT.  Go ahead and
@@ -665,7 +720,7 @@ show_point (const char *point, const struct stat *statp)
       char *mp = find_mount_point (point, statp);
       if (mp)
 	{
-	  show_dev (NULL, mp, NULL, NULL, false, false);
+	  show_dev (NULL, mp, NULL, NULL, false, false, NULL);
 	  free (mp);
 	}
     }
@@ -694,7 +749,7 @@ show_all_entries (void)
 
   for (me = mount_list; me; me = me->me_next)
     show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
-	      me->me_dummy, me->me_remote);
+	      me->me_dummy, me->me_remote, NULL);
 }
 
 /* Add FSTYPE to the list of file system types to display. */
@@ -743,6 +798,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
       fputs (_("\
   -a, --all             include dummy file systems\n\
   -B, --block-size=SIZE  use SIZE-byte blocks\n\
+      --total           produce a grand total\n\
   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\n\
   -H, --si              likewise, but use powers of 1000 not 1024\n\
 "), stdout);
@@ -794,6 +850,8 @@ main (int argc, char **argv)
   file_systems_processed = false;
   posix_format = false;
   exit_status = EXIT_SUCCESS;
+  print_grand_total = false;
+  grand_fsu.fsu_blocksize = 1;
 
   for (;;)
     {
@@ -864,6 +922,10 @@ main (int argc, char **argv)
 	  add_excluded_fs_type (optarg);
 	  break;
 
+	case 'c':
+	  print_grand_total = true;
+	  break;
+
 	case_GETOPT_HELP_CHAR;
 	case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
 
@@ -959,6 +1021,13 @@ main (int argc, char **argv)
   else
     show_all_entries ();
 
+  if (print_grand_total)
+    {
+      if (inode_format)
+	grand_fsu.fsu_blocks = 1;
+      show_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu);
+    }
+
   if (! file_systems_processed)
     error (EXIT_FAILURE, 0, _("no file systems processed"));
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5a57ca9..59d1e48 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -284,6 +284,8 @@ TESTS =						\
   dd/skip-seek					\
   dd/skip-seek2					\
   dd/unblock-sync				\
+  df/total					\
+  df/total-awk					\
   du/2g						\
   du/8gb					\
   du/basic					\
diff --git a/tests/check.mk b/tests/check.mk
index 4fca283..03b89dc 100644
--- a/tests/check.mk
+++ b/tests/check.mk
@@ -79,6 +79,7 @@ TESTS_ENVIRONMENT =				\
   top_srcdir='$(top_srcdir)'			\
   CONFIG_HEADER='$(abs_top_builddir)/lib/config.h' \
   CU_TEST_NAME=`basename '$(abs_srcdir)'`,$$tst	\
+  AWK='$(AWK)'					\
   EGREP='$(EGREP)'				\
   EXEEXT='$(EXEEXT)'				\
   MAKE=$(MAKE)					\
diff --git a/tests/df/total b/tests/df/total
new file mode 100755
index 0000000..186bf8d
--- /dev/null
+++ b/tests/df/total
@@ -0,0 +1,42 @@
+#!/bin/sh
+# Ensure "df --total" produces /^total.../ line
+
+# Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  ls --version
+fi
+
+. $srcdir/test-lib.sh
+
+fail=0
+
+# Don't let a different umask perturb the results.
+umask 22
+
+RE_TOTAL='^total( +(-?[0-9]+|-)){3} +-?[0-9]+%$'
+
+df > tmp || fail=1
+$EGREP "$RE_TOTAL" tmp && fail=1
+
+df -i > tmp || fail=1
+$EGREP "$RE_TOTAL" tmp && fail=1
+
+df --total | $EGREP "$RE_TOTAL" || fail=1
+df -i --total | $EGREP "$RE_TOTAL" || fail=1
+
+(exit $fail); exit $fail
diff --git a/tests/df/total-awk b/tests/df/total-awk
new file mode 100755
index 0000000..632e945
--- /dev/null
+++ b/tests/df/total-awk
@@ -0,0 +1,78 @@
+#!/bin/sh
+# Ensure "df --total" computes well summary statistics
+
+# Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  ls --version
+fi
+
+. $srcdir/test-lib.sh
+
+fail=0
+
+# Don't let a different umask perturb the results.
+umask 22
+
+echo '
+BEGIN {
+  total     = 0
+  used      = 0
+  available = 0
+}
+{
+  if (NR==1 || $0==$1 || $0~/^total +(-?[0-9]+|-) +(-?[0-9]+|-) +(-?[0-9]+|-) +-?[0-9]+%$/)
+    next
+  if ($1~/^[0-9]/)
+    {
+      total     += $1
+      used      += $2
+      available += $3
+    }
+  else
+    {
+      total     += $2
+      used      += $3
+      available += $4
+    }
+}
+END {
+  print total
+  print used
+  print available
+}
+' > compute_sum.awk || fail=1
+
+echo '
+/^total +(-?[0-9]+|-) +(-?[0-9]+|-) +(-?[0-9]+|-) +-?[0-9]+%$/ {
+  print $2;
+  print $3;
+  print $4
+}
+' > parse_total.awk || fail=1
+
+df --total > tmp || fail=1
+$AWK -f compute_sum.awk tmp > out1 || fail=1
+$AWK -f parse_total.awk tmp > out2 || fail=1
+compare out1 out2 || fail=1
+
+df -i --total > tmp || fail=1
+$AWK -f compute_sum.awk tmp > out1 || fail=1
+$AWK -f parse_total.awk tmp > out2 || fail=1
+compare out1 out2 || fail=1
+
+(exit $fail); exit $fail
-- 
1.5.4.1

_______________________________________________
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils

Reply via email to