1
0
mirror of git://git.sv.gnu.org/coreutils.git synced 2026-05-18 16:31:26 +02:00

df: new option: --total to print grand totals

* 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.
This commit is contained in:
Kamil Dudka
2008-09-03 10:33:06 +02:00
committed by Jim Meyering
parent ef6a361a07
commit ea2887bbe4
8 changed files with 208 additions and 8 deletions

3
NEWS
View File

@@ -21,6 +21,9 @@ GNU coreutils NEWS -*- outline -*-
With this new option, after a short read, dd repeatedly calls read,
until it fills the incomplete block, reaches EOF, or encounters an error.
df accepts a new option --total, which produces a grand total of all
arguments after all arguments have been processed.
If the GNU MP library is available at configure time, factor and
expr support arbitrarily large numbers. Pollard's rho algorithm is
used to factor large numbers.

2
TODO
View File

@@ -72,8 +72,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.

View File

@@ -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.
@itemx --total
@opindex --total
@cindex 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

View File

@@ -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"));

View File

@@ -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 \

View File

@@ -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) \

42
tests/df/total Executable file
View File

@@ -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

78
tests/df/total-awk Executable file
View File

@@ -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