On Tuesday 02 September 2008 11:52:57 you wrote:
> You'll want to print totals with --inodes (-i), too.
Good point, fixed...

> Please adjust formatting to use spaces before each open parenthesis
Sorry for this detail, I always forget. Note that this is not acceptable for 
macro definition with arguments (e.g. LOG_EQ).

> and drop the short-named "-c" option.  There is a strong disincentive
> to adding new short-named options: they can conflict with other-vendor
> versions of df.
Well, dropped...

> It'd be nice to add a test to exercise the code
> (with and without -i) and at least check for a final line
> matching /^total.../.
I wrote two test cases. The 1st is dummy and check only /^total.../ line 
presence and the 2nd computes the summary itself and compare result with 
df --total. The second one requires awk.

> > +#define LOG_EQ(a,b) (((a)&&(b))||(!(a)&&!(b)))
>
> This can be written more simply as !((a) ^ (b))
Replaced with (!(a) == !(b)), thanks James!

> Please split long lines so as not to exceed max length of 80.
Ok, fixed, new patch in attachment...


Kamil
From dcd79cfceeb8442af4403ddb82df3bcb322cde10 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <[EMAIL PROTECTED]>
Date: Tue, 2 Sep 2008 14:44:38 +0200
Subject: [PATCH] df: new option: --total to produce grand total (in the same way as du)

* 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/df/total     |   40 +++++++++++++++++++++++++
 tests/df/total-awk |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 7 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..1ba7ff3 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 is the negation flag 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/df/total b/tests/df/total
new file mode 100755
index 0000000..be4bc19
--- /dev/null
+++ b/tests/df/total
@@ -0,0 +1,40 @@
+#!/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
+
+df > tmp || fail=1
+grep ^total tmp && fail=1
+
+df -i > tmp || fail=1
+grep ^total tmp && fail=1
+
+df --total | grep ^total || fail=1
+df -i --total | grep ^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..e98b8ac
--- /dev/null
+++ b/tests/df/total-awk
@@ -0,0 +1,81 @@
+#!/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
+
+(awk --help) 2>&1 |grep 'Usage: awk' > /dev/null \
+  || skip_test_ "awk not found"
+
+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 || $1~/^total/)
+    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/ {
+  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