>From 057a6fcf486d1d7424b81a5547b6b8f8fee423c3 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Thu, 30 Aug 2012 14:58:22 +0200 Subject: [PATCH 01/20] df: move the call of get_header from get_dev to main src/df.c (get_dev): Factor out calling get_header to ... (main): ... here. Call print_table only if file_systems_processed. --- src/df.c | 13 ++++++------- 1 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/df.c b/src/df.c index 83fef77..a8b835c 100644 --- a/src/df.c +++ b/src/df.c @@ -515,12 +515,8 @@ get_dev (char const *disk, char const *mount_point, if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs) return; - if (! file_systems_processed) - { - if (! force_fsu) - file_systems_processed = true; - get_header (); - } + if (! force_fsu) + file_systems_processed = true; alloc_table_row (); @@ -1122,6 +1118,8 @@ main (int argc, char **argv) if (require_sync) sync (); + get_header (); + if (optind < argc) { int i; @@ -1143,7 +1141,8 @@ main (int argc, char **argv) get_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu, false); } - print_table (); + if (file_systems_processed) + print_table (); /* Print the "no FS processed" diagnostic only if there was no preceding diagnostic, e.g., if all have been excluded. */ -- 1.7.7 >From 621c4fbb416c0a5a62d7b39df060cbceea60cb66 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Thu, 30 Aug 2012 22:09:39 +0200 Subject: [PATCH 02/20] df: rename some displayable fields src/df.c (Displayable fields): Rename DEV_FIELD to SOURCE_FIELD. Rename TYPE_FIELD to FSTYPE_FIELD. Rename FREE_FIELD to AVAIL_FIELD. Rename MNT_FIELD to TARGET_FIELD. --- src/df.c | 37 ++++++++++++++++++++++--------------- 1 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/df.c b/src/df.c index a8b835c..3170f9c 100644 --- a/src/df.c +++ b/src/df.c @@ -117,19 +117,26 @@ static bool print_grand_total; static struct fs_usage grand_fsu; /* Display modes. */ -enum { DEFAULT_MODE, INODES_MODE, HUMAN_MODE, POSIX_MODE, NMODES }; +enum +{ + DEFAULT_MODE, + INODES_MODE, + HUMAN_MODE, + POSIX_MODE, + NMODES +}; static int header_mode = DEFAULT_MODE; /* Displayable fields. */ enum { - DEV_FIELD, /* file system */ - TYPE_FIELD, /* FS type */ - TOTAL_FIELD, /* blocks or inodes */ - USED_FIELD, /* ditto */ - FREE_FIELD, /* ditto */ - PCENT_FIELD, /* percent used */ - MNT_FIELD, /* mount point */ + SOURCE_FIELD, /* file system */ + FSTYPE_FIELD, /* FS type */ + TOTAL_FIELD, /* blocks or inodes */ + USED_FIELD, /* ditto */ + AVAIL_FIELD, /* ditto */ + PCENT_FIELD, /* percent used */ + TARGET_FIELD, /* mount point */ NFIELDS }; @@ -237,12 +244,12 @@ print_table (void) if (!cell) /* Missing type column, or mount point etc. */ continue; - /* Note the DEV_FIELD used to be displayed on it's own line + /* Note the SOURCE_FIELD used to be displayed on it's own line if (!posix_format && mbswidth (cell) > 20), but that functionality is probably more problematic than helpful. */ if (field != 0) putchar (' '); - if (field == MNT_FIELD) /* The last one. */ + if (field == TARGET_FIELD) /* The last one. */ fputs (cell, stdout); else { @@ -271,7 +278,7 @@ get_header (void) for (field = 0; field < NFIELDS; field++) { - if (field == TYPE_FIELD && !print_type) + if (field == FSTYPE_FIELD && !print_type) { table[nrows-1][field] = NULL; continue; @@ -588,11 +595,11 @@ get_dev (char const *disk, char const *mount_point, { switch (field) { - case DEV_FIELD: + case SOURCE_FIELD: cell = dev_name; break; - case TYPE_FIELD: + case FSTYPE_FIELD: cell = print_type ? xstrdup (fstype) : NULL; break; @@ -604,7 +611,7 @@ get_dev (char const *disk, char const *mount_point, cell = xstrdup (df_readable (negate_used, used, buf, input_units, output_units)); break; - case FREE_FIELD: + case AVAIL_FIELD: cell = xstrdup (df_readable (negate_available, available, buf, input_units, output_units)); break; @@ -656,7 +663,7 @@ get_dev (char const *disk, char const *mount_point, break; - case MNT_FIELD: + case TARGET_FIELD: if (mount_point) { #ifdef HIDE_AUTOMOUNT_PREFIX -- 1.7.7 >From b141a42af3e6b9eed040fcc8e7d62a47ffb9c96c Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Thu, 30 Aug 2012 23:20:54 +0200 Subject: [PATCH 03/20] df: rework internal processing of output columns Prepare the handling of output columns for the following implementation of the --output switch. The code now represents the status quo, i.e. it supports the output formats DEFAULT_FORMAT, INODE_FORMAT, HUMAN_FORMAT and POSIX_FORMAT (without yet supporting both "size" and inode formats). * src/df.c (display_field_t): Turn loose enum definition of the displayable fields into a typedef. Add the inode fields ITOTAL_FIELD, IUSED_FIELD, IAVAIL_FIELD, IPCENT_FIELD. (field_t): Define structure to hold the caption, the width and the alignment for each field of the above type. (headers, alignments, widths): Remove arrays. (field_defaults): Add structure to hold the defaults for the caption, the width and the alignment of each field. (columns): Add a pointer to the storage for the array of the actual output columns, i.e. fields. (ncolumns): Add counter for the current output columns. (alloc_table_row): Allocate the dynamic ncolumns value of strings. (print_table): Loop over ncolumns instead of constant NFIELDS. Rename loop variable 'field' to 'col' to avoid ambiguity with the 'field' element in the columns structure. Adjust the condition for printing the last column by comparing with the column number instead of the field name (MNT_FIELD). Use the width and the alignment stored in the columns array. (alloc_field): Add new function to allocate a field in the columns array. (get_field_list): Add new function to fill the array of output columns for each mode. (get_header): Loop over ncolumns instead of constant NFIELDS. Rename the loop variable 'field' to 'col' to avoid ambiguity with the 'field' element in the columns structure. Remove the code for continuing the loop if the current column is the file system type and print_type is not active (which is now impossible). Store the cell in the columns store along with the new width. (get_dev): Loop over ncolumns instead of the constant NFIELDS. Rename the loop variable 'field' to 'col' to avoid ambiguity with the 'field' element in the columns structure; move the definition down to where it is used first. Add cases for the inode fields ITOTAL_FIELD, IUSED_FIELD, IAVAIL_FIELD and IPCENT_FIELD. Store the cell in the columns store along with the new width. (main): Use new get_field_list function to fill the list of output columns. --- src/df.c | 189 ++++++++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 136 insertions(+), 53 deletions(-) diff --git a/src/df.c b/src/df.c index 3170f9c..2c44a14 100644 --- a/src/df.c +++ b/src/df.c @@ -128,40 +128,51 @@ enum static int header_mode = DEFAULT_MODE; /* Displayable fields. */ -enum +typedef enum { SOURCE_FIELD, /* file system */ FSTYPE_FIELD, /* FS type */ - TOTAL_FIELD, /* blocks or inodes */ - USED_FIELD, /* ditto */ - AVAIL_FIELD, /* ditto */ + TOTAL_FIELD, /* FS size */ + USED_FIELD, /* FS size used */ + AVAIL_FIELD, /* FS size available */ PCENT_FIELD, /* percent used */ + ITOTAL_FIELD, /* inode total */ + IUSED_FIELD, /* inodes used */ + IAVAIL_FIELD, /* inodes available */ + IPCENT_FIELD, /* inodes used in percent */ TARGET_FIELD, /* mount point */ NFIELDS -}; +} display_field_t; -/* Header strings for the above fields in each mode. - NULL means to use the header for the default mode. */ -static const char *headers[NFIELDS][NMODES] = { -/* DEFAULT_MODE INODES_MODE HUMAN_MODE POSIX_MODE */ - { N_("Filesystem"), NULL, NULL, NULL }, - { N_("Type"), NULL, NULL, NULL }, - { N_("blocks"), N_("Inodes"), N_("Size"), NULL }, - { N_("Used"), N_("IUsed"), NULL, NULL }, - { N_("Available"), N_("IFree"), N_("Avail"), NULL }, - { N_("Use%"), N_("IUse%"), NULL, N_("Capacity") }, - { N_("Mounted on"), NULL, NULL, NULL } + +/* An output columns, i.e. a field. */ +struct field_t { + display_field_t field; + const char *caption;/* NULL means to use the default header of this field. */ + size_t width; /* Auto adjusted (up) widths used to align columns. */ + mbs_align_t align; /* Alignment for this field. */ }; -/* Alignments for the 3 textual and 4 numeric fields. */ -static mbs_align_t alignments[NFIELDS] = { - MBS_ALIGN_LEFT, MBS_ALIGN_LEFT, - MBS_ALIGN_RIGHT, MBS_ALIGN_RIGHT, MBS_ALIGN_RIGHT, MBS_ALIGN_RIGHT, - MBS_ALIGN_LEFT +/* Header strings, minimum width and alignment for the above fields. */ +static struct field_t field_defaults[NFIELDS] = { + { SOURCE_FIELD, N_("Filesystem"), 14, MBS_ALIGN_LEFT }, + { FSTYPE_FIELD, N_("Type"), 4, MBS_ALIGN_LEFT }, + { TOTAL_FIELD, N_("blocks"), 5, MBS_ALIGN_RIGHT }, + { USED_FIELD, N_("Used"), 5, MBS_ALIGN_RIGHT }, + { AVAIL_FIELD, N_("Available"), 5, MBS_ALIGN_RIGHT }, + { PCENT_FIELD, N_("Use%"), 4, MBS_ALIGN_RIGHT }, + { ITOTAL_FIELD, N_("Inodes"), 5, MBS_ALIGN_RIGHT }, + { IUSED_FIELD, N_("IUsed"), 5, MBS_ALIGN_RIGHT }, + { IAVAIL_FIELD, N_("IFree"), 5, MBS_ALIGN_RIGHT }, + { IPCENT_FIELD, N_("IUse%"), 4, MBS_ALIGN_RIGHT }, + { TARGET_FIELD, N_("Mounted on"), 0, MBS_ALIGN_LEFT } }; -/* Auto adjusted (up) widths used to align columns. */ -static size_t widths[NFIELDS] = { 14, 4, 5, 5, 5, 4, 0 }; +/* Storage for the definition of output columns. */ +static struct field_t *columns; + +/* The current number of output columns. */ +static size_t ncolumns; /* Storage for pointers for each string (cell of table). */ static char ***table; @@ -224,7 +235,7 @@ alloc_table_row (void) { nrows++; table = xnrealloc (table, nrows, sizeof (char *)); - table[nrows-1] = xnmalloc (NFIELDS, sizeof (char *)); + table[nrows-1] = xnmalloc (ncolumns, sizeof (char *)); } /* Output each cell in the table, accounting for the @@ -233,32 +244,33 @@ alloc_table_row (void) static void print_table (void) { - size_t field, row; + size_t row; for (row = 0; row < nrows; row ++) { - for (field = 0; field < NFIELDS; field++) + size_t col; + for (col = 0; col < ncolumns; col++) { - size_t width = widths[field]; - char *cell = table[row][field]; + char *cell = table[row][col]; if (!cell) /* Missing type column, or mount point etc. */ continue; /* Note the SOURCE_FIELD used to be displayed on it's own line if (!posix_format && mbswidth (cell) > 20), but that functionality is probably more problematic than helpful. */ - if (field != 0) + if (col != 0) putchar (' '); - if (field == TARGET_FIELD) /* The last one. */ + if (col == ncolumns - 1) /* The last one. */ fputs (cell, stdout); else { - cell = ambsalign (cell, &width, alignments[field], 0); + size_t width = columns[col].width; + cell = ambsalign (cell, &width, columns[col].align, 0); /* When ambsalign fails, output unaligned data. */ - fputs (cell ? cell : table[row][field], stdout); + fputs (cell ? cell : table[row][col], stdout); free (cell); } - IF_LINT (free (table[row][field])); + IF_LINT (free (table[row][col])); } putchar ('\n'); IF_LINT (free (table[row])); @@ -267,29 +279,95 @@ print_table (void) IF_LINT (free (table)); } +/* Dynamically allocate a struct field_t in COLUMNS, which + can then be accessed with standard array notation. */ + +static void +alloc_field (int f, const char *c) +{ + ncolumns++; + columns = xnrealloc (columns, ncolumns, sizeof (struct field_t)); + columns[ncolumns-1].field = f; + if (c != NULL) + columns[ncolumns-1].caption = c; + else + columns[ncolumns-1].caption = field_defaults[f].caption; + columns[ncolumns-1].width = field_defaults[f].width; + columns[ncolumns-1].align = field_defaults[f].align; +} + + +/* Get the appropriate columns for the mode. */ +static void +get_field_list (void) +{ + switch (header_mode) + { + case DEFAULT_MODE: + alloc_field (SOURCE_FIELD, NULL); + if (print_type) + alloc_field (FSTYPE_FIELD, NULL); + alloc_field (TOTAL_FIELD, NULL); + alloc_field (USED_FIELD, NULL); + alloc_field (AVAIL_FIELD, NULL); + alloc_field (PCENT_FIELD, NULL); + alloc_field (TARGET_FIELD, NULL); + break; + + case HUMAN_MODE: + alloc_field (SOURCE_FIELD, NULL); + if (print_type) + alloc_field (FSTYPE_FIELD, NULL); + + alloc_field (TOTAL_FIELD, N_("Size")); + alloc_field (USED_FIELD, NULL); + alloc_field (AVAIL_FIELD, N_("Avail")); + alloc_field (PCENT_FIELD, NULL); + alloc_field (TARGET_FIELD, NULL); + break; + + case INODES_MODE: + alloc_field (SOURCE_FIELD, NULL); + if (print_type) + alloc_field (FSTYPE_FIELD, NULL); + alloc_field (ITOTAL_FIELD, NULL); + alloc_field (IUSED_FIELD, NULL); + alloc_field (IAVAIL_FIELD, NULL); + alloc_field (IPCENT_FIELD, NULL); + alloc_field (TARGET_FIELD, NULL); + break; + + case POSIX_MODE: + alloc_field (SOURCE_FIELD, NULL); + if (print_type) + alloc_field (FSTYPE_FIELD, NULL); + alloc_field (TOTAL_FIELD, NULL); + alloc_field (USED_FIELD, NULL); + alloc_field (AVAIL_FIELD, NULL); + alloc_field (PCENT_FIELD, N_("Capacity")); + alloc_field (TARGET_FIELD, NULL); + break; + + default: + assert (!"invalid header_mode"); + } +} + /* Obtain the appropriate header entries. */ static void get_header (void) { - size_t field; + size_t col; alloc_table_row (); - for (field = 0; field < NFIELDS; field++) + for (col = 0; col < ncolumns; col++) { - if (field == FSTYPE_FIELD && !print_type) - { - table[nrows-1][field] = NULL; - continue; - } - char *cell = NULL; - char const *header = _(headers[field][header_mode]); - if (!header) - header = _(headers[field][DEFAULT_MODE]); + char const *header = _(columns[col].caption); - if (header_mode == DEFAULT_MODE && field == TOTAL_FIELD) + if (header_mode == DEFAULT_MODE && columns[col].field == TOTAL_FIELD) { char buf[LONGEST_HUMAN_READABLE + 1]; @@ -326,7 +404,7 @@ get_header (void) if (asprintf (&cell, _("%s-%s"), num, header) == -1) cell = NULL; } - else if (header_mode == POSIX_MODE && field == TOTAL_FIELD) + else if (header_mode == POSIX_MODE && columns[col].field == TOTAL_FIELD) { char buf[INT_BUFSIZE_BOUND (uintmax_t)]; char *num = umaxtostr (output_block_size, buf); @@ -343,9 +421,9 @@ get_header (void) hide_problematic_chars (cell); - table[nrows-1][field] = cell; + table[nrows-1][col] = cell; - widths[field] = MAX (widths[field], mbswidth (cell, 0)); + columns[col].width = MAX (columns[col].width, mbswidth (cell, 0)); } } @@ -492,7 +570,6 @@ get_dev (char const *disk, char const *mount_point, bool negate_used; double pct = -1; char* cell; - size_t field; if (me_remote && show_local_fs) return; @@ -591,9 +668,10 @@ get_dev (char const *disk, char const *mount_point, negate_used = (total < available_to_root); } - for (field = 0; field < NFIELDS; field++) + size_t col; + for (col = 0; col < ncolumns; col++) { - switch (field) + switch (columns[col].field) { case SOURCE_FIELD: cell = dev_name; @@ -604,19 +682,23 @@ get_dev (char const *disk, char const *mount_point, break; case TOTAL_FIELD: + case ITOTAL_FIELD: cell = xstrdup (df_readable (false, total, buf, input_units, output_units)); break; case USED_FIELD: + case IUSED_FIELD: cell = xstrdup (df_readable (negate_used, used, buf, input_units, output_units)); break; case AVAIL_FIELD: + case IAVAIL_FIELD: cell = xstrdup (df_readable (negate_available, available, buf, input_units, output_units)); break; case PCENT_FIELD: + case IPCENT_FIELD: if (! known_value (used) || ! known_value (available)) ; else if (!negate_used @@ -688,9 +770,9 @@ get_dev (char const *disk, char const *mount_point, if (cell) { hide_problematic_chars (cell); - widths[field] = MAX (widths[field], mbswidth (cell, 0)); + columns[col].width = MAX (columns[col].width, mbswidth (cell, 0)); } - table[nrows-1][field] = cell; + table[nrows-1][col] = cell; } } @@ -1125,6 +1207,7 @@ main (int argc, char **argv) if (require_sync) sync (); + get_field_list (); get_header (); if (optind < argc) -- 1.7.7 >From 333f2942bcda20a74ae71820378d26b447c3bbf3 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 12:04:48 +0200 Subject: [PATCH 04/20] df: apply ambsalign to the last field with MBA_NO_RIGHT_PAD This change is an advance for the upcoming --output option where any field can be the last one, not just the left-aligned target field. * src/df.c (print_table): Instead of fputs()ing directly, apply ambsalign on the last field, too. Use the new MBA_NO_RIGHT_PAD flag for this. --- src/df.c | 19 ++++++++++--------- 1 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/df.c b/src/df.c index 2c44a14..bcd0fc0 100644 --- a/src/df.c +++ b/src/df.c @@ -260,16 +260,17 @@ print_table (void) functionality is probably more problematic than helpful. */ if (col != 0) putchar (' '); + + int flags = 0; if (col == ncolumns - 1) /* The last one. */ - fputs (cell, stdout); - else - { - size_t width = columns[col].width; - cell = ambsalign (cell, &width, columns[col].align, 0); - /* When ambsalign fails, output unaligned data. */ - fputs (cell ? cell : table[row][col], stdout); - free (cell); - } + flags = MBA_NO_RIGHT_PAD; + + size_t width = columns[col].width; + cell = ambsalign (cell, &width, columns[col].align, flags); + /* When ambsalign fails, output unaligned data. */ + fputs (cell ? cell : table[row][col], stdout); + free (cell); + IF_LINT (free (table[row][col])); } putchar ('\n'); -- 1.7.7 >From b476f7ba5220bc751608c3795c013d4c7ac13d2e Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 17:35:11 +0200 Subject: [PATCH 05/20] df: fix bracket opening style for structs * src/df.c (field_t): Open bracket on same line as the struct name, as suggested by 'indent --no-tabs'. (long_options): Likewise. --- src/df.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/df.c b/src/df.c index bcd0fc0..d5f258b 100644 --- a/src/df.c +++ b/src/df.c @@ -146,7 +146,8 @@ typedef enum /* An output columns, i.e. a field. */ -struct field_t { +struct field_t +{ display_field_t field; const char *caption;/* NULL means to use the default header of this field. */ size_t width; /* Auto adjusted (up) widths used to align columns. */ @@ -189,8 +190,7 @@ enum MEGABYTES_OPTION /* FIXME: remove long opt in Aug 2013 */ }; -static struct option const long_options[] = -{ +static struct option const long_options[] = { {"all", no_argument, NULL, 'a'}, {"block-size", required_argument, NULL, 'B'}, {"inodes", no_argument, NULL, 'i'}, -- 1.7.7 >From f8d570618ba9859f59b9b2b27d5346eaf56aa456 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 17:40:49 +0200 Subject: [PATCH 06/20] df: use enum value for long option --total * src/df.c (TOTAL_OPTION): Add new enum value. (long_options): Use it for the "total" option instead of 'c'. (main): Likewise. --- src/df.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/df.c b/src/df.c index d5f258b..d3edfe5 100644 --- a/src/df.c +++ b/src/df.c @@ -187,6 +187,7 @@ enum { NO_SYNC_OPTION = CHAR_MAX + 1, SYNC_OPTION, + TOTAL_OPTION, MEGABYTES_OPTION /* FIXME: remove long opt in Aug 2013 */ }; @@ -202,7 +203,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'}, + {"total", no_argument, NULL, TOTAL_OPTION}, {"type", required_argument, NULL, 't'}, {"exclude-type", required_argument, NULL, 'x'}, {GETOPT_HELP_OPTION_DECL}, @@ -1100,7 +1101,7 @@ main (int argc, char **argv) add_excluded_fs_type (optarg); break; - case 'c': + case TOTAL_OPTION: print_grand_total = true; break; -- 1.7.7 >From 7e707b869037f045212a3db5f0759662171d98e9 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 17:45:15 +0200 Subject: [PATCH 07/20] df: fix typo in comment * src/df.c (field_t): Fix type in struct description. --- src/df.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/df.c b/src/df.c index d3edfe5..8224238 100644 --- a/src/df.c +++ b/src/df.c @@ -145,7 +145,7 @@ typedef enum } display_field_t; -/* An output columns, i.e. a field. */ +/* An output column, i.e. a field. */ struct field_t { display_field_t field; -- 1.7.7 >From 337491b701434376d7dd780f5132854288c3d8c2 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 17:53:57 +0200 Subject: [PATCH 08/20] df: remove now-obsolescent condition * src/df.c (get_dev): Remove condition to copy the fstype into the FSTYPE_FIELD - based on whether print_type is non-Null. Since the introduction of get_field_list(), there are only fields added to the columns array which have to be added. --- src/df.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/df.c b/src/df.c index 8224238..3befa05 100644 --- a/src/df.c +++ b/src/df.c @@ -680,7 +680,7 @@ get_dev (char const *disk, char const *mount_point, break; case FSTYPE_FIELD: - cell = print_type ? xstrdup (fstype) : NULL; + cell = xstrdup (fstype); break; case TOTAL_FIELD: -- 1.7.7 >From b96bcf9912553899d28157b8341e03c2f6793402 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 22:35:37 +0200 Subject: [PATCH 09/20] df: only sum up grand total if required This is a change in advance for the following commit which would otherwise have the side effect of doubling the total values. * src/df.c (get_dev): Guard the summing up of the values for the grand total: only do it if we have to print the total and if the current invocation is not for processing it. --- src/df.c | 33 +++++++++++++++++++++------------ 1 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/df.c b/src/df.c index 3befa05..9a95585 100644 --- a/src/df.c +++ b/src/df.c @@ -637,10 +637,14 @@ get_dev (char const *disk, char const *mount_point, negate_available = false; available_to_root = available; - if (known_value (total)) - grand_fsu.fsu_files += total; - if (known_value (available)) - grand_fsu.fsu_ffree += available; + /* Add to grand total unless processing grand total line. */ + if (print_grand_total && ! force_fsu) + { + if (known_value (total)) + grand_fsu.fsu_files += total; + if (known_value (available)) + grand_fsu.fsu_ffree += available; + } } else { @@ -652,14 +656,19 @@ get_dev (char const *disk, char const *mount_point, && known_value (available)); available_to_root = fsu.fsu_bfree; - if (known_value (total)) - grand_fsu.fsu_blocks += input_units * total; - if (known_value (available_to_root)) - grand_fsu.fsu_bfree += input_units * available_to_root; - if (known_value (available)) - add_uint_with_neg_flag (&grand_fsu.fsu_bavail, - &grand_fsu.fsu_bavail_top_bit_set, - input_units * available, negate_available); + /* Add to grand total unless processing grand total line. */ + if (print_grand_total && ! force_fsu) + { + if (known_value (total)) + grand_fsu.fsu_blocks += input_units * total; + if (known_value (available_to_root)) + grand_fsu.fsu_bfree += input_units * available_to_root; + if (known_value (available)) + add_uint_with_neg_flag (&grand_fsu.fsu_bavail, + &grand_fsu.fsu_bavail_top_bit_set, + input_units * available, + negate_available); + } } used = UINTMAX_MAX; -- 1.7.7 >From 8a2a3da7f86bf1626c6e6d147f64223543881af3 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 23:13:05 +0200 Subject: [PATCH 10/20] df: print '-' into the target column of the total line df --total now has to print something into the target field, because that could be on another than the last column in future. I choose the print '-' for it. * src/df.c (main): Pass a hyphen "-" for the mount point name to get_dev. (get_dev): As the mount_point is now always there, remove the condition and the else case for the TARGET_FIELD. Instead, simply copy the mount_point. All cells are now always present. Therefore, add an assertion statement if one was not. Furthermore, hide the problematic characters unconditionally. (print_table): Remove the skipping of empty cells. * tests/df/total-verify.sh: Accommodate to the new "-" in the target field of the summary line. * NEWS: Mention the change in behavior. --- NEWS | 4 ++++ src/df.c | 35 ++++++++++++++--------------------- tests/df/total-verify.sh | 6 +++--- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/NEWS b/NEWS index 59a8510..d84b36c 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,10 @@ GNU coreutils NEWS -*- outline -*- nproc now diagnoses with an error, non option command line parameters. + df --total now prints '-' into the target column (mount point) of the + summary line, accommodating to the upcoming --output option where the + target field can be on another than the last column. + ** Improvements seq is now up to 70 times faster than it was in coreutils-8.19 and prior, diff --git a/src/df.c b/src/df.c index 9a95585..b68f766 100644 --- a/src/df.c +++ b/src/df.c @@ -253,8 +253,6 @@ print_table (void) for (col = 0; col < ncolumns; col++) { char *cell = table[row][col]; - if (!cell) /* Missing type column, or mount point etc. */ - continue; /* Note the SOURCE_FIELD used to be displayed on it's own line if (!posix_format && mbswidth (cell) > 20), but that @@ -757,32 +755,27 @@ get_dev (char const *disk, char const *mount_point, break; case TARGET_FIELD: - if (mount_point) - { #ifdef HIDE_AUTOMOUNT_PREFIX - /* Don't print the first directory name in MOUNT_POINT if it's an - artifact of an automounter. This is a bit too aggressive to be - the default. */ - if (STRNCMP_LIT (mount_point, "/auto/") == 0) - mount_point += 5; - else if (STRNCMP_LIT (mount_point, "/tmp_mnt/") == 0) - mount_point += 8; + /* Don't print the first directory name in MOUNT_POINT if it's an + artifact of an automounter. This is a bit too aggressive to be + the default. */ + if (STRNCMP_LIT (mount_point, "/auto/") == 0) + mount_point += 5; + else if (STRNCMP_LIT (mount_point, "/tmp_mnt/") == 0) + mount_point += 8; #endif - cell = xstrdup (mount_point); - } - else - cell = NULL; + cell = xstrdup (mount_point); break; default: assert (!"unhandled field"); } - if (cell) - { - hide_problematic_chars (cell); - columns[col].width = MAX (columns[col].width, mbswidth (cell, 0)); - } + if (!cell) + assert (!"empty cell"); + + hide_problematic_chars (cell); + columns[col].width = MAX (columns[col].width, mbswidth (cell, 0)); table[nrows-1][col] = cell; } } @@ -1239,7 +1232,7 @@ main (int argc, char **argv) { if (inode_format) grand_fsu.fsu_blocks = 1; - get_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu, false); + get_dev ("total", "-", NULL, NULL, false, false, &grand_fsu, false); } if (file_systems_processed) diff --git a/tests/df/total-verify.sh b/tests/df/total-verify.sh index 45c838e..18d215f 100755 --- a/tests/df/total-verify.sh +++ b/tests/df/total-verify.sh @@ -31,10 +31,10 @@ while (<>) # Recognize df output lines like these: # /dev/sdc1 0 0 0 - /c # tmpfs 1536000 12965 1523035 1% /tmp - # total 5285932 787409 4498523 15% - /^(.*?) +(-?\d+|-) +(-?\d+|-) +(-?\d+|-) +(?:- |[0-9]+%)(.*)$/ + # total 5285932 787409 4498523 15% - + /^(.*?) +(-?\d+|-) +(-?\d+|-) +(-?\d+|-) +(?:- |[0-9]+%) (.*)$/ or die "$0: invalid input line\n: $_"; - if ($1 eq 'total' && $5 eq '') + if ($1 eq 'total' && $5 eq '-') { $total == $2 or die "$total != $2"; $used == $3 or die "$used != $3"; -- 1.7.7 >From e4cb594714f97f5e4d0ad696d96be8a1551a9201 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Sun, 16 Sep 2012 23:57:56 +0200 Subject: [PATCH 11/20] df: Add basic support for mixed block and inode fields * src/df.c (field_type_t): Add new typedef of 3 enums to distinguish between block, inode and other fields. (field_t): Add field_type member of the above new type. (field_defaults): Add default values for the above field_type, indicating whether a field contains block values, inode values or other, generic values. (alloc_field): Initialize the new field_type member. (field_values_t): Add this struct to store the field values, used by and factored out from get_dev to be able to define such a struct for both the inode and the block values. (get_field_values): Add this function to obtain the block values and the inode values from the file system usage, used by and factored out from get_dev. (add_to_grand_total): Add this function to sum the values of the current mount point up for the grand total, used by and factored out from get_dev. (get_dev): Move the definition of the variables fsu, buf, pct and cell down to where they are used first to give them a better scope. Factor out input_units, output_units, total, available, negate_available, available_to_root, used and negate_used into the above struct field_values_t. Factor out the mapping of the fsu values to the above variables into above function get_field_values. Factor out the summing up of the grand total values into the above function add_to_grand_total. Define block_values and inode_values of the new type and call the new get_field_values to fill them from the fsu values. Call the above function add_to_grand_total for summing up the values for the grand total. Inside the loop over all fields, define a variable 'v' to point to either the block_values or the inode_values, depending on the current field's field_type. Change the code in the cases TOTAL_FIELD/ITOTAL_FIELD, USED_FIELD/IUSED_FIELD, AVAIL_FIELD/ IAVAIL_FIELD and PCENT_FIELD/IPCENT_FIELD to use the field values where 'v' is pointing to, i.e. either the block_values or the inode_values. --- src/df.c | 289 +++++++++++++++++++++++++++++++++++++------------------------- 1 files changed, 171 insertions(+), 118 deletions(-) diff --git a/src/df.c b/src/df.c index b68f766..b0d4dc2 100644 --- a/src/df.c +++ b/src/df.c @@ -144,11 +144,19 @@ typedef enum NFIELDS } display_field_t; +/* Flag if a field contains a block, an inode or another value. */ +typedef enum +{ + BLOCK_FLD, /* Block values field */ + INODE_FLD, /* Inode values field */ + OTHER_FLD /* Neutral field, e.g. target */ +} field_type_t; /* An output column, i.e. a field. */ struct field_t { display_field_t field; + field_type_t field_type; const char *caption;/* NULL means to use the default header of this field. */ size_t width; /* Auto adjusted (up) widths used to align columns. */ mbs_align_t align; /* Alignment for this field. */ @@ -156,17 +164,17 @@ struct field_t /* Header strings, minimum width and alignment for the above fields. */ static struct field_t field_defaults[NFIELDS] = { - { SOURCE_FIELD, N_("Filesystem"), 14, MBS_ALIGN_LEFT }, - { FSTYPE_FIELD, N_("Type"), 4, MBS_ALIGN_LEFT }, - { TOTAL_FIELD, N_("blocks"), 5, MBS_ALIGN_RIGHT }, - { USED_FIELD, N_("Used"), 5, MBS_ALIGN_RIGHT }, - { AVAIL_FIELD, N_("Available"), 5, MBS_ALIGN_RIGHT }, - { PCENT_FIELD, N_("Use%"), 4, MBS_ALIGN_RIGHT }, - { ITOTAL_FIELD, N_("Inodes"), 5, MBS_ALIGN_RIGHT }, - { IUSED_FIELD, N_("IUsed"), 5, MBS_ALIGN_RIGHT }, - { IAVAIL_FIELD, N_("IFree"), 5, MBS_ALIGN_RIGHT }, - { IPCENT_FIELD, N_("IUse%"), 4, MBS_ALIGN_RIGHT }, - { TARGET_FIELD, N_("Mounted on"), 0, MBS_ALIGN_LEFT } + { SOURCE_FIELD, OTHER_FLD, N_("Filesystem"), 14, MBS_ALIGN_LEFT }, + { FSTYPE_FIELD, OTHER_FLD, N_("Type"), 4, MBS_ALIGN_LEFT }, + { TOTAL_FIELD, BLOCK_FLD, N_("blocks"), 5, MBS_ALIGN_RIGHT }, + { USED_FIELD, BLOCK_FLD, N_("Used"), 5, MBS_ALIGN_RIGHT }, + { AVAIL_FIELD, BLOCK_FLD, N_("Available"), 5, MBS_ALIGN_RIGHT }, + { PCENT_FIELD, BLOCK_FLD, N_("Use%"), 4, MBS_ALIGN_RIGHT }, + { ITOTAL_FIELD, INODE_FLD, N_("Inodes"), 5, MBS_ALIGN_RIGHT }, + { IUSED_FIELD, INODE_FLD, N_("IUsed"), 5, MBS_ALIGN_RIGHT }, + { IAVAIL_FIELD, INODE_FLD, N_("IFree"), 5, MBS_ALIGN_RIGHT }, + { IPCENT_FIELD, INODE_FLD, N_("IUse%"), 4, MBS_ALIGN_RIGHT }, + { TARGET_FIELD, OTHER_FLD, N_("Mounted on"), 0, MBS_ALIGN_LEFT } }; /* Storage for the definition of output columns. */ @@ -175,6 +183,19 @@ static struct field_t *columns; /* The current number of output columns. */ static size_t ncolumns; +/* Field values. */ +struct field_values_t +{ + uintmax_t input_units; + uintmax_t output_units; + uintmax_t total; + uintmax_t available; + bool negate_available; + uintmax_t available_to_root; + uintmax_t used; + bool negate_used; +}; + /* Storage for pointers for each string (cell of table). */ static char ***table; @@ -288,6 +309,7 @@ alloc_field (int f, const char *c) ncolumns++; columns = xnrealloc (columns, ncolumns, sizeof (struct field_t)); columns[ncolumns-1].field = f; + columns[ncolumns-1].field_type = field_defaults[f].field_type; if (c != NULL) columns[ncolumns-1].caption = c; else @@ -536,6 +558,65 @@ has_uuid_suffix (char const *s) && strspn (s + len - 36, "-0123456789abcdefABCDEF") == 36); } +/* Obtain the block values BV and inode values IV + from the file system usage FSU. */ +static void +get_field_values (struct field_values_t *bv, + struct field_values_t *iv, + const struct fs_usage *fsu) +{ + /* Inode values. */ + iv->input_units = iv->output_units = 1; + iv->total = fsu->fsu_files; + iv->available = iv->available_to_root = fsu->fsu_ffree; + iv->negate_available = false; + + iv->used = UINTMAX_MAX; + iv->negate_used = false; + if (known_value (iv->total) && known_value (iv->available_to_root)) + { + iv->used = iv->total - iv->available_to_root; + iv->negate_used = (iv->total < iv->available_to_root); + } + + /* Block values. */ + bv->input_units = fsu->fsu_blocksize; + bv->output_units = output_block_size; + bv->total = fsu->fsu_blocks; + bv->available = fsu->fsu_bavail; + bv->available_to_root = fsu->fsu_bfree; + bv->negate_available = (fsu->fsu_bavail_top_bit_set + && known_value (fsu->fsu_bavail)); + + bv->used = UINTMAX_MAX; + bv->negate_used = false; + if (known_value (bv->total) && known_value (bv->available_to_root)) + { + bv->used = bv->total - bv->available_to_root; + bv->negate_used = (bv->total < bv->available_to_root); + } +} + +/* Add block and inode values to grand total. */ +static void +add_to_grand_total (struct field_values_t *bv, struct field_values_t *iv) +{ + if (known_value (iv->total)) + grand_fsu.fsu_files += iv->total; + if (known_value (iv->available)) + grand_fsu.fsu_ffree += iv->available; + + if (known_value (bv->total)) + grand_fsu.fsu_blocks += bv->input_units * bv->total; + if (known_value (bv->available_to_root)) + grand_fsu.fsu_bfree += bv->input_units * bv->available_to_root; + if (known_value (bv->available)) + add_uint_with_neg_flag (&grand_fsu.fsu_bavail, + &grand_fsu.fsu_bavail_top_bit_set, + bv->input_units * bv->available, + bv->negate_available); +} + /* Obtain 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. @@ -558,19 +639,6 @@ get_dev (char const *disk, char const *mount_point, const struct fs_usage *force_fsu, bool process_all) { - struct fs_usage fsu; - char buf[LONGEST_HUMAN_READABLE + 2]; - uintmax_t input_units; - uintmax_t output_units; - uintmax_t total; - uintmax_t available; - bool negate_available; - uintmax_t available_to_root; - uintmax_t used; - bool negate_used; - double pct = -1; - char* cell; - if (me_remote && show_local_fs) return; @@ -587,6 +655,7 @@ get_dev (char const *disk, char const *mount_point, if (!stat_file) stat_file = mount_point ? mount_point : disk; + struct fs_usage fsu; if (force_fsu) fsu = *force_fsu; else if (get_fs_usage (stat_file, disk, &fsu)) @@ -627,59 +696,36 @@ get_dev (char const *disk, char const *mount_point, if (! fstype) fstype = "-"; /* unknown */ - if (inode_format) - { - input_units = output_units = 1; - total = fsu.fsu_files; - available = fsu.fsu_ffree; - negate_available = false; - available_to_root = available; + struct field_values_t block_values; + struct field_values_t inode_values; + get_field_values (&block_values, &inode_values, &fsu); - /* Add to grand total unless processing grand total line. */ - if (print_grand_total && ! force_fsu) - { - if (known_value (total)) - grand_fsu.fsu_files += total; - if (known_value (available)) - grand_fsu.fsu_ffree += available; - } - } - else + /* Add to grand total unless processing grand total line. */ + if (print_grand_total && ! force_fsu) + add_to_grand_total (&block_values, &inode_values); + + size_t col; + for (col = 0; col < ncolumns; col++) { - input_units = fsu.fsu_blocksize; - output_units = output_block_size; - total = fsu.fsu_blocks; - available = fsu.fsu_bavail; - negate_available = (fsu.fsu_bavail_top_bit_set - && known_value (available)); - available_to_root = fsu.fsu_bfree; + char buf[LONGEST_HUMAN_READABLE + 2]; + char* cell; - /* Add to grand total unless processing grand total line. */ - if (print_grand_total && ! force_fsu) + struct field_values_t *v; + switch (columns[col].field_type) { - if (known_value (total)) - grand_fsu.fsu_blocks += input_units * total; - if (known_value (available_to_root)) - grand_fsu.fsu_bfree += input_units * available_to_root; - if (known_value (available)) - add_uint_with_neg_flag (&grand_fsu.fsu_bavail, - &grand_fsu.fsu_bavail_top_bit_set, - input_units * available, - negate_available); + case BLOCK_FLD: + v = &block_values; + break; + case INODE_FLD: + v = &inode_values; + break; + case OTHER_FLD: + v = NULL; + break; + default: + assert (!"bad field_type"); } - } - - used = UINTMAX_MAX; - negate_used = false; - if (known_value (total) && known_value (available_to_root)) - { - used = total - available_to_root; - negate_used = (total < available_to_root); - } - size_t col; - for (col = 0; col < ncolumns; col++) - { switch (columns[col].field) { case SOURCE_FIELD: @@ -692,67 +738,74 @@ get_dev (char const *disk, char const *mount_point, case TOTAL_FIELD: case ITOTAL_FIELD: - cell = xstrdup (df_readable (false, total, buf, - input_units, output_units)); + cell = xstrdup (df_readable (false, v->total, buf, + v->input_units, v->output_units)); break; + case USED_FIELD: case IUSED_FIELD: - cell = xstrdup (df_readable (negate_used, used, buf, - input_units, output_units)); + cell = xstrdup (df_readable (v->negate_used, v->used, buf, + v->input_units, v->output_units)); break; + case AVAIL_FIELD: case IAVAIL_FIELD: - cell = xstrdup (df_readable (negate_available, available, buf, - input_units, output_units)); + cell = xstrdup (df_readable (v->negate_available, v->available, buf, + v->input_units, v->output_units)); break; case PCENT_FIELD: case IPCENT_FIELD: - if (! known_value (used) || ! known_value (available)) - ; - else if (!negate_used - && used <= TYPE_MAXIMUM (uintmax_t) / 100 - && used + available != 0 - && (used + available < used) == negate_available) - { - uintmax_t u100 = used * 100; - uintmax_t nonroot_total = used + available; - pct = u100 / nonroot_total + (u100 % nonroot_total != 0); - } - else - { - /* The calculation cannot be done easily with integer - arithmetic. Fall back on floating point. This can suffer - from minor rounding errors, but doing it exactly requires - multiple precision arithmetic, and it's not worth the - aggravation. */ - double u = negate_used ? - (double) - used : used; - double a = negate_available ? - (double) - available : available; - double nonroot_total = u + a; - if (nonroot_total) - { - long int lipct = pct = u * 100 / nonroot_total; - double ipct = lipct; + { + double pct = -1; + if (! known_value (v->used) || ! known_value (v->available)) + ; + else if (!v->negate_used + && v->used <= TYPE_MAXIMUM (uintmax_t) / 100 + && v->used + v->available != 0 + && (v->used + v->available < v->used) + == v->negate_available) + { + uintmax_t u100 = v->used * 100; + uintmax_t nonroot_total = v->used + v->available; + pct = u100 / nonroot_total + (u100 % nonroot_total != 0); + } + else + { + /* The calculation cannot be done easily with integer + arithmetic. Fall back on floating point. This can suffer + from minor rounding errors, but doing it exactly requires + multiple precision arithmetic, and it's not worth the + aggravation. */ + double u = v->negate_used ? - (double) - v->used : v->used; + double a = v->negate_available + ? - (double) - v->available : v->available; + double nonroot_total = u + a; + if (nonroot_total) + { + long int lipct = pct = u * 100 / nonroot_total; + double ipct = lipct; - /* Like 'pct = ceil (dpct);', but avoid ceil so that - the math library needn't be linked. */ - if (ipct - 1 < pct && pct <= ipct + 1) - pct = ipct + (ipct < pct); - } - } + /* Like 'pct = ceil (dpct);', but avoid ceil so that + the math library needn't be linked. */ + if (ipct - 1 < pct && pct <= ipct + 1) + pct = ipct + (ipct < pct); + } + } - if (0 <= pct) - { - if (asprintf (&cell, "%.0f%%", pct) == -1) - cell = NULL; - } - else - cell = strdup ("-"); + if (0 <= pct) + { + if (asprintf (&cell, "%.0f%%", pct) == -1) + cell = NULL; + } + else + cell = strdup ("-"); - if (!cell) - xalloc_die (); + if (!cell) + xalloc_die (); - break; + break; + } case TARGET_FIELD: #ifdef HIDE_AUTOMOUNT_PREFIX -- 1.7.7 >From 63ebee3be3b9c050e7dc1c1fc403eb5f29e3e46f Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Mon, 17 Sep 2012 00:26:07 +0200 Subject: [PATCH 12/20] df: remove remainder of pre-mixed fields support * src/df.c (main): Remove setting of grand_fsu.fsu_blocks in the inode_format case as this is no longer needed and would lead to wrong results once when mixed block/inode fields will be used. --- src/df.c | 6 +----- 1 files changed, 1 insertions(+), 5 deletions(-) diff --git a/src/df.c b/src/df.c index b0d4dc2..99669d9 100644 --- a/src/df.c +++ b/src/df.c @@ -1282,11 +1282,7 @@ main (int argc, char **argv) get_all_entries (); if (print_grand_total && file_systems_processed) - { - if (inode_format) - grand_fsu.fsu_blocks = 1; - get_dev ("total", "-", NULL, NULL, false, false, &grand_fsu, false); - } + get_dev ("total", "-", NULL, NULL, false, false, &grand_fsu, false); if (file_systems_processed) print_table (); -- 1.7.7 >From 1d824293f3ca57bf8932b63420e7faafc8528352 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Mon, 17 Sep 2012 00:35:32 +0200 Subject: [PATCH 13/20] df: minor cleanup to improve readability * src/df.c (main): Cleanup the code at the end regarding file_systems_processed to make the code clearer. --- src/df.c | 20 ++++++++++++-------- 1 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/df.c b/src/df.c index 99669d9..4ea77f7 100644 --- a/src/df.c +++ b/src/df.c @@ -1281,16 +1281,20 @@ main (int argc, char **argv) else get_all_entries (); - if (print_grand_total && file_systems_processed) - get_dev ("total", "-", NULL, NULL, false, false, &grand_fsu, false); - if (file_systems_processed) - print_table (); + { + if (print_grand_total) + get_dev ("total", "-", NULL, NULL, false, false, &grand_fsu, false); - /* Print the "no FS processed" diagnostic only if there was no preceding - diagnostic, e.g., if all have been excluded. */ - if (exit_status == EXIT_SUCCESS && ! file_systems_processed) - error (EXIT_FAILURE, 0, _("no file systems processed")); + print_table (); + } + else + { + /* Print the "no FS processed" diagnostic only if there was no preceding + diagnostic, e.g., if all have been excluded. */ + if (exit_status == EXIT_SUCCESS) + error (EXIT_FAILURE, 0, _("no file systems processed")); + } exit (exit_status); } -- 1.7.7 >From 5f7074dc9b587767766a2f9512276dc4591a83be Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Tue, 18 Sep 2012 18:18:03 +0200 Subject: [PATCH 14/20] df: cleanup header_mode handling regarding --inodes option * src/df.c (inode_format): Remove variable. (main): Remove initialization of the above variable. In getopts loop, directly set the header_mode to INODES_MODE instead of using the above variable. Afterwards, remove the mapping to INODES_MODE as it is already set. --- src/df.c | 10 +++------- 1 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/df.c b/src/df.c index 4ea77f7..29cb9ed 100644 --- a/src/df.c +++ b/src/df.c @@ -43,9 +43,6 @@ proper_name ("David MacKenzie"), \ proper_name ("Paul Eggert") -/* If true, show inode information. */ -static bool inode_format; - /* If true, show even file systems with zero size or uninteresting types. */ static bool show_all_fs; @@ -1069,7 +1066,6 @@ main (int argc, char **argv) fs_select_list = NULL; fs_exclude_list = NULL; - inode_format = false; show_all_fs = false; show_listed_fs = false; human_output_opts = -1; @@ -1102,7 +1098,7 @@ main (int argc, char **argv) } break; case 'i': - inode_format = true; + header_mode = INODES_MODE; break; case 'h': human_output_opts = human_autoscale | human_SI | human_base_1024; @@ -1180,8 +1176,8 @@ main (int argc, char **argv) &human_output_opts, &output_block_size); } - if (inode_format) - header_mode = INODES_MODE; + if (header_mode == INODES_MODE) + ; else if (human_output_opts & human_autoscale) header_mode = HUMAN_MODE; else if (posix_format) -- 1.7.7 >From d47b4db438e034e56ff3d1e67da96d8784aad57b Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Tue, 18 Sep 2012 18:20:05 +0200 Subject: [PATCH 15/20] df: give posix_format variable a better scope * src/df.c (posix_format): Move variable ... (main): ... to here. --- src/df.c | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/df.c b/src/df.c index 29cb9ed..d80d328 100644 --- a/src/df.c +++ b/src/df.c @@ -60,9 +60,6 @@ static int human_output_opts; /* The units to use when printing sizes. */ static uintmax_t output_block_size; -/* If true, use the POSIX output format. */ -static bool posix_format; - /* True if a file system has been processed for output. */ static bool file_systems_processed; @@ -1071,11 +1068,12 @@ main (int argc, char **argv) human_output_opts = -1; print_type = false; file_systems_processed = false; - posix_format = false; exit_status = EXIT_SUCCESS; print_grand_total = false; grand_fsu.fsu_blocksize = 1; + bool posix_format = false; + while (true) { int oi = -1; -- 1.7.7 >From a81c833068d8f8747d8b4fec9e2804efaa994eeb Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Tue, 18 Sep 2012 18:26:27 +0200 Subject: [PATCH 16/20] df: remove old comment * src/df.c (print_table): Remove comment about 2-line format in cases where the SOURCE_FIELD exceeds 20 chars, as such behavior has been removed long ago by commit v8.10-40-g99679ff. --- src/df.c | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/src/df.c b/src/df.c index d80d328..531db4c 100644 --- a/src/df.c +++ b/src/df.c @@ -269,9 +269,6 @@ print_table (void) { char *cell = table[row][col]; - /* Note the SOURCE_FIELD used to be displayed on it's own line - if (!posix_format && mbswidth (cell) > 20), but that - functionality is probably more problematic than helpful. */ if (col != 0) putchar (' '); -- 1.7.7 >From 234f58472e22f8ff20e63f8bc5aafae8763bbb07 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Fri, 21 Sep 2012 00:59:35 +0200 Subject: [PATCH 17/20] df: add new --output option * src/df.c (Display modes): Add OUTPUT_MODE, remove unused NMODES. (display_field_t): Remove unnecessary NFIELDS. (field_defaults): Define as unsized array, let the compiler choose from the actual number of array elements. (OUTPUT_OPTION): Add enum to identify the long --output option. (long_options): Add optional-argument --output option. (decode_output_arg): Add function to parse the comma-separated field list passed for the --output option in order to add the appropriate fields to the columns array. (get_field_list): Add case for the new OUTPUT_MODE to add all available fields to columns in the case the --output option has been passed without any values. Use the comma-separated field list form to pass to decode_output_arg to keep the field header mapping for the OUTPUT_MODE only on one place. (main): Define format string msg_mut_excl to be used in the following checks whether the use of --output and the other option is mutually exclusive. In the getopt_long loop, add a check to the case for the -i option to issue an error message when it is used together with --output; Likewise for -T and -P. Add a new case for OUTPUT_OPTION, together with similar checks as above and eventually passing the optarg to decode_output_arg. After the getopt_long loop, consider the OUTPUT_MODE case in order not to run into -h or -P mode. --- src/df.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 136 insertions(+), 5 deletions(-) diff --git a/src/df.c b/src/df.c index 531db4c..e4aba1e 100644 --- a/src/df.c +++ b/src/df.c @@ -25,6 +25,7 @@ #include #include "system.h" +#include "argmatch.h" #include "canonicalize.h" #include "error.h" #include "fsusage.h" @@ -117,7 +118,7 @@ enum INODES_MODE, HUMAN_MODE, POSIX_MODE, - NMODES + OUTPUT_MODE }; static int header_mode = DEFAULT_MODE; @@ -134,8 +135,7 @@ typedef enum IUSED_FIELD, /* inodes used */ IAVAIL_FIELD, /* inodes available */ IPCENT_FIELD, /* inodes used in percent */ - TARGET_FIELD, /* mount point */ - NFIELDS + TARGET_FIELD /* mount point */ } display_field_t; /* Flag if a field contains a block, an inode or another value. */ @@ -157,7 +157,7 @@ struct field_t }; /* Header strings, minimum width and alignment for the above fields. */ -static struct field_t field_defaults[NFIELDS] = { +static struct field_t field_defaults[] = { { SOURCE_FIELD, OTHER_FLD, N_("Filesystem"), 14, MBS_ALIGN_LEFT }, { FSTYPE_FIELD, OTHER_FLD, N_("Type"), 4, MBS_ALIGN_LEFT }, { TOTAL_FIELD, BLOCK_FLD, N_("blocks"), 5, MBS_ALIGN_RIGHT }, @@ -203,6 +203,7 @@ enum NO_SYNC_OPTION = CHAR_MAX + 1, SYNC_OPTION, TOTAL_OPTION, + OUTPUT_OPTION, MEGABYTES_OPTION /* FIXME: remove long opt in Aug 2013 */ }; @@ -214,6 +215,7 @@ static struct option const long_options[] = { {"si", no_argument, NULL, 'H'}, {"local", no_argument, NULL, 'l'}, {"megabytes", no_argument, NULL, MEGABYTES_OPTION}, /* obsolescent, */ + {"output", optional_argument, NULL, OUTPUT_OPTION}, {"portability", no_argument, NULL, 'P'}, {"print-type", no_argument, NULL, 'T'}, {"sync", no_argument, NULL, SYNC_OPTION}, @@ -310,6 +312,88 @@ alloc_field (int f, const char *c) } +/* Given a string, ARG, containing a comma-separated list of arguments + to the --output option, add the appropriate fields to columns. */ +static void +decode_output_arg (char const *arg) +{ + static display_field_t const output_vals[] = + { + SOURCE_FIELD, FSTYPE_FIELD, + TOTAL_FIELD, USED_FIELD, AVAIL_FIELD, PCENT_FIELD, + ITOTAL_FIELD, IUSED_FIELD, IAVAIL_FIELD, IPCENT_FIELD, + TARGET_FIELD + }; + + /* Array to mark each field as used. */ + static bool field_used[sizeof (output_vals)]; + + /* Valid arguments to the '--output' option. */ + static char const* const output_args[] = + { + "source", "fstype", + "total", "used", "avail", "pcent", + "itotal", "iused", "iavail", "ipcent", + "target", NULL + }; + ARGMATCH_VERIFY (output_args, output_vals); + + char *arg_writable = xstrdup (arg); + char *s = arg_writable; + do + { + /* find next comma */ + char *comma = strchr (s, ','); + + /* If we found a comma, put a NUL in its place and advance. */ + if (comma) + *comma++ = 0; + + /* process S. */ + display_field_t field; + field = XARGMATCH ("--output", s, output_args, output_vals); + + if (field_used[field]) + { + /* Prevent the fields from being used more than once. */ + error (0, 0, _("option --output: field '%s' used more than once"), + output_args[field]); + usage (EXIT_FAILURE); + } + field_used[field] = true; + + switch (field) + { + case SOURCE_FIELD: + case FSTYPE_FIELD: + case USED_FIELD: + case PCENT_FIELD: + case ITOTAL_FIELD: + case IUSED_FIELD: + case IAVAIL_FIELD: + case IPCENT_FIELD: + case TARGET_FIELD: + alloc_field (field, NULL); + break; + + case TOTAL_FIELD: + alloc_field (field, N_("Size")); + break; + + case AVAIL_FIELD: + alloc_field (field, N_("Avail")); + break; + + default: + assert (!"invalid XARGSMATCH return value"); + } + s = comma; + } + while (s); + + free (arg_writable); +} + /* Get the appropriate columns for the mode. */ static void get_field_list (void) @@ -361,6 +445,15 @@ get_field_list (void) alloc_field (TARGET_FIELD, NULL); break; + case OUTPUT_MODE: + if (!ncolumns) + { + /* Add all fields if --output was given without a field list. */ + decode_output_arg ("source,fstype,total,used,avail,pcent," + "itotal,iused,iavail,ipcent,target"); + } + break; + default: assert (!"invalid header_mode"); } @@ -1071,6 +1164,8 @@ main (int argc, char **argv) bool posix_format = false; + const char *msg_mut_excl = _("options %s and %s are mutually exclusive"); + while (true) { int oi = -1; @@ -1093,6 +1188,11 @@ main (int argc, char **argv) } break; case 'i': + if (header_mode == OUTPUT_MODE) + { + error (0, 0, msg_mut_excl, "-i", "--output"); + usage (EXIT_FAILURE); + } header_mode = INODES_MODE; break; case 'h': @@ -1122,9 +1222,19 @@ main (int argc, char **argv) output_block_size = 1024 * 1024; break; case 'T': + if (header_mode == OUTPUT_MODE) + { + error (0, 0, msg_mut_excl, "-T", "--output"); + usage (EXIT_FAILURE); + } print_type = true; break; case 'P': + if (header_mode == OUTPUT_MODE) + { + error (0, 0, msg_mut_excl, "-P", "--output"); + usage (EXIT_FAILURE); + } posix_format = true; break; case SYNC_OPTION: @@ -1147,6 +1257,27 @@ main (int argc, char **argv) add_excluded_fs_type (optarg); break; + case OUTPUT_OPTION: + if (header_mode == INODES_MODE) + { + error (0, 0, msg_mut_excl, "-i", "--output"); + usage (EXIT_FAILURE); + } + if (posix_format && header_mode == DEFAULT_MODE) + { + error (0, 0, msg_mut_excl, "-P", "--output"); + usage (EXIT_FAILURE); + } + if (print_type) + { + error (0, 0, msg_mut_excl, "-T", "--output"); + usage (EXIT_FAILURE); + } + header_mode = OUTPUT_MODE; + if (optarg) + decode_output_arg (optarg); + break; + case TOTAL_OPTION: print_grand_total = true; break; @@ -1171,7 +1302,7 @@ main (int argc, char **argv) &human_output_opts, &output_block_size); } - if (header_mode == INODES_MODE) + if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE) ; else if (human_output_opts & human_autoscale) header_mode = HUMAN_MODE; -- 1.7.7 >From 841f840c469765207c90d51a109a83ba6e199d54 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Fri, 21 Sep 2012 01:38:27 +0200 Subject: [PATCH 18/20] df: plug two minor memory holes detected by valgrind When the new --output option is used without the SOURCE_FIELD being in the field list, then df leaked the memory for dev_name: $ valgrind --leak-check=full src/df --o=target,total ... ==10999== LEAK SUMMARY: ==10999== definitely lost: 122 bytes in 15 blocks Second, valgrind warns about still reachable columns memory (which happens in any case): 64 bytes in 1 blocks are still reachable in loss record 1 of 5 ... ==11869== by 0x40AAB0: xrealloc (xmalloc.c:63) ==11869== by 0x40353B: alloc_field (xalloc.h:126) ==11869== by 0x404692: decode_output_arg (df.c:380) ==11869== by 0x4027DC: main (df.c:1279) * src/df.c (get_dev): Also xstrdup the dev_name, and free it afterwards to silence a valgrind warning about definitely lost memory. (main): Free the columns store to silence valgrind, guarded by the IF_LINT macro. --- src/df.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/src/df.c b/src/df.c index e4aba1e..ed3774a 100644 --- a/src/df.c +++ b/src/df.c @@ -813,7 +813,7 @@ get_dev (char const *disk, char const *mount_point, switch (columns[col].field) { case SOURCE_FIELD: - cell = dev_name; + cell = xstrdup (dev_name); break; case FSTYPE_FIELD: @@ -915,6 +915,7 @@ get_dev (char const *disk, char const *mount_point, columns[col].width = MAX (columns[col].width, mbswidth (cell, 0)); table[nrows-1][col] = cell; } + free (dev_name); } /* If DISK corresponds to a mount point, show its usage @@ -1418,5 +1419,7 @@ main (int argc, char **argv) error (EXIT_FAILURE, 0, _("no file systems processed")); } + IF_LINT ( free (columns)); + exit (exit_status); } -- 1.7.7 >From c27fbe7f715224a29769a451d09c841b27f52132 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Fri, 21 Sep 2012 04:03:10 +0200 Subject: [PATCH 19/20] df: document the new --output option * src/df.c (usage): Add a short description of --output and its available field names for use in the optional argument. * doc/coreutils.texi (df invocation): Add the new option with more details and a few examples. * NEWS (New features): Mention the new option. --- NEWS | 5 ++++ doc/coreutils.texi | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/df.c | 7 ++++++ 3 files changed, 71 insertions(+), 0 deletions(-) diff --git a/NEWS b/NEWS index d84b36c..41ae279 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,11 @@ GNU coreutils NEWS -*- outline -*- ** New features + df now accepts the --output[=FIELD_LIST] option to define the list of columns + to include in the output, or all available columns if the FIELD_LIST is + omitted. This is also the first time for df to be able to print block fields + together with inode fields. + md5sum now accepts the --tag option to print BSD-style output with GNU file name escaping. This also affects sha1sum, sha224sum, sha256sum, sha384sum and sha512sum. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index c0abd7f..a01b6ea 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -10660,6 +10660,65 @@ This may make @command{df} run significantly faster on systems with many disks, but on some systems (notably SunOS) the results may be slightly out of date. This is the default. +@item --output +@itemx @w{@kbd{--output}[=@var{field_list}]} +@opindex --output +Use the output format defined by @var{field_list}, or print all fields if +@var{field_list} is omitted. + +The use of the @option{--output} together with each of the options @option{-i}, +@option{-P}, and @option{-T} is mutually exclusive. + +FIELD_LIST is a comma-separated list of columns to be included in @command{df}'s +output and therefore effectively controls the order of output columns. +Each field can thus be used at the place of choice, but yet must only be +used once. + +Valid field names in the @var{field_list} are: +@table @samp +@item source +The source of the mount point, usually a device. +@item fstype +File system type. + +@item total +Total number of blocks. +@item used +Number of used blocks. +@item avail +Number of available blocks. +@item pcent +Percentage of @var{used} divided by @var{total}. + +@item itotal +Total number of inodes. +@item iused +Number of used inodes. +@item iavail +Number of available inodes. +@item ipcent +Percentage of @var{iused} divided by @var{itotal}. + +@item target +The mount point. +@end table + +The fields for block and inodes statistics are affected by the scaling +options like @option{-h} as usual. +The @var{field_list} items can be abbreviated - as long as they are +unambiguous among the others. The definition of the @var{field_list} can +even be splitted among several @option{--output} uses. + +@example +#!/bin/sh +# Print the TARGET (i.e. the mount point) along with their percentage +# statistic regarding the blocks and the inodes. +df --out=targ --output=pc,ip + +# Print all available fields. +df --o +@end example + @item -P @itemx --portability @opindex -P diff --git a/src/df.c b/src/df.c index ed3774a..44d5487 100644 --- a/src/df.c +++ b/src/df.c @@ -1123,6 +1123,8 @@ Mandatory arguments to long options are mandatory for short options too.\n\ \n\ "), stdout); fputs (_("\ + --output[=FIELD_LIST] use the output format defined by FIELD_LIST,\n\ + or print all fields if FIELD_LIST is omitted.\n\ -P, --portability use the POSIX output format\n\ --sync invoke sync before getting usage info\n\ -t, --type=TYPE limit listing to file systems of type TYPE\n\ @@ -1134,6 +1136,11 @@ Mandatory arguments to long options are mandatory for short options too.\n\ fputs (VERSION_OPTION_DESCRIPTION, stdout); emit_blocksize_note ("DF"); emit_size_note (); + fputs (_("\n\ +FIELD_LIST is a comma-separated list of columns to be included. Valid\n\ +field names are: 'source', 'fstype', 'total', 'used', 'avail', 'pcent',\n\ +'itotal', 'iused', 'iavail', 'ipcent' and 'target' (see info page).\n\ +"), stdout); emit_ancillary_info (); } exit (status); -- 1.7.7 >From 8493faa5e2a2f5cd9086c9f411be9045b5c3cd82 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Fri, 21 Sep 2012 04:20:48 +0200 Subject: [PATCH 20/20] df: add a test for the --output option * tests/df/df-output.sh: Add a test case. * tests/local.mk (all_tests): Mention the test. * cfg.mk (sc_file_system): Exempt the test from this syntax-check. --- cfg.mk | 2 +- tests/df/df-output.sh | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/local.mk | 1 + 3 files changed, 102 insertions(+), 1 deletions(-) create mode 100644 tests/df/df-output.sh diff --git a/cfg.mk b/cfg.mk index 079ab5b..465255c 100644 --- a/cfg.mk +++ b/cfg.mk @@ -532,7 +532,7 @@ exclude_file_name_regexp--sc_prohibit_always-defined_macros = \ exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = ^tests/pr/ exclude_file_name_regexp--sc_program_name = ^(gl/.*|lib/euidaccess-stat)\.c$$ exclude_file_name_regexp--sc_file_system = \ - NEWS|^(init\.cfg|src/df\.c|tests/df/df-P\.sh)$$ + NEWS|^(init\.cfg|src/df\.c|tests/df/df-P\.sh|tests/df/df-output\.sh)$$ exclude_file_name_regexp--sc_prohibit_always_true_header_tests = \ ^m4/stat-prog\.m4$$ exclude_file_name_regexp--sc_prohibit_fail_0 = \ diff --git a/tests/df/df-output.sh b/tests/df/df-output.sh new file mode 100644 index 0000000..79560ea --- /dev/null +++ b/tests/df/df-output.sh @@ -0,0 +1,100 @@ +#!/bin/sh +# Exercise df's --output option. + +# Copyright (C) 2012 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_ df + +# Ensure that --output is mutually exclusive with -i, -P, and -T. +# Ensure that this limitation is not depending on the order of options. +cat <<\EOF > exp || framework_failure_ +df: options OPT and --output are mutually exclusive +Try 'df --help' for more information. +EOF + +df -i --output '.' 2>out && fail=1 +sed -i 's/ -i / OPT /' out +compare exp out || fail=1 + +df --output -i '.' 2>out && fail=1 +sed -i 's/ -i / OPT /' out +compare exp out || fail=1 + +df -P --output '.' 2>out && fail=1 +sed -i 's/ -P / OPT /' out +compare exp out || fail=1 + +df --output -P '.' 2>out && fail=1 +sed -i 's/ -P / OPT /' out +compare exp out || fail=1 + +df -T --output '.' 2>out && fail=1 +sed -i 's/ -T / OPT /' out +compare exp out || fail=1 + +df --output -T '.' 2>out && fail=1 +sed -i 's/ -T / OPT /' out +compare exp out || fail=1 + +# Ensure that each field is only used once for the --output argument. +cat <<\EOF > exp || framework_failure_ +df: option --output: field 'target' used more than once +Try 'df --help' for more information. +EOF + +df --output=target,source,target '.' 2>out && fail=1 +compare exp out || fail=1 + +# Ensure that this limitation also works for splitted --output options +# and abbreviated field names. +df --out=ta,s --out=ta '.' 2>out && fail=1 +compare exp out || fail=1 + +# Ensure that the full output includes all fields, and +# that --o (without argument) is identical to the full list. + +cat <<\EOF > exp || framework_failure_ +Filesystem Type Size Used Avail Use% Inodes IUsed IFree IUse% Mounted on +EOF + +df --o=s,f,to,u,a,p,ito,iu,ia,ip,ta '.' >out || fail=1 +sed -e '1 {s/ [ ]*/ /g;q}' out > out2 +compare exp out2 || fail=1 + +df --output '.' >out || fail=1 +sed -e '1 {s/ [ ]*/ /g;q}' out > out2 +compare exp out2 || fail=1 + +# Ensure that the grand total line now contains a "-" in the TARGET field. +cat <<\EOF > exp || framework_failure_ +- +EOF + +df --output=target --total "." >out || fail=1 +sed -n -e '3 {p;q}' out > out2 +compare exp out2 || fail=1 + +# Ensure that --output is mentioned in the usage. +cat <<\EOF > exp || framework_failure_ +--output +EOF + +df --help > out || fail=1 +grep ' --output' out | sed 's/^.*\(--output\).*$/\1/;q' > out2 +compare exp out2 || fail=1 + +Exit $fail diff --git a/tests/local.mk b/tests/local.mk index 55700b5..c9495b8 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -450,6 +450,7 @@ all_tests = \ tests/cp/thru-dangling.sh \ tests/df/header.sh \ tests/df/df-P.sh \ + tests/df/df-output.sh \ tests/df/unreadable.sh \ tests/df/total-unprocessed.sh \ tests/df/no-mtab-status.sh \ -- 1.7.7