diff options
-rw-r--r-- | src/df.c | 771 | ||||
-rwxr-xr-x | tests/df/total-verify.sh | 6 |
2 files changed, 539 insertions, 238 deletions
@@ -43,11 +43,8 @@ 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. */ + uninteresting types. */ static bool show_all_fs; /* If true, show only local file systems. */ @@ -63,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; @@ -78,7 +72,7 @@ static bool require_sync; /* Desired exit status. */ static int exit_status; -/* A file system type to display. */ +/* A file system type to display. */ struct fs_type_list { @@ -104,7 +98,7 @@ static struct fs_type_list *fs_select_list; static struct fs_type_list *fs_exclude_list; -/* Linked list of mounted file systems. */ +/* Linked list of mounted file systems. */ static struct mount_entry *mount_list; /* If true, print file system type as well. */ @@ -113,48 +107,113 @@ static bool print_type; /* If true, print a grand total at the end. */ static bool print_grand_total; -/* Grand total data. */ +/* Grand total data. */ 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, + OUTPUT_MODE +}; static int header_mode = DEFAULT_MODE; /* Displayable fields. */ -enum +typedef 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 */ - NFIELDS -}; + SOURCE_FIELD, /* file system */ + FSTYPE_FIELD, /* FS type */ + SIZE_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 */ +} 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; -/* 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 } +/* Attributes of a display field. */ +struct field_data_t +{ + display_field_t field; + char const *arg; + 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. */ + bool used; }; -/* 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_data_t field_data[] = { + [SOURCE_FIELD] = { SOURCE_FIELD, + "source", OTHER_FLD, N_("Filesystem"), 14, MBS_ALIGN_LEFT, false }, + + [FSTYPE_FIELD] = { FSTYPE_FIELD, + "fstype", OTHER_FLD, N_("Type"), 4, MBS_ALIGN_LEFT, false }, + + [SIZE_FIELD] = { SIZE_FIELD, + "size", BLOCK_FLD, N_("blocks"), 5, MBS_ALIGN_RIGHT, false }, + + [USED_FIELD] = { USED_FIELD, + "used", BLOCK_FLD, N_("Used"), 5, MBS_ALIGN_RIGHT, false }, + + [AVAIL_FIELD] = { AVAIL_FIELD, + "avail", BLOCK_FLD, N_("Available"), 5, MBS_ALIGN_RIGHT, false }, + + [PCENT_FIELD] = { PCENT_FIELD, + "pcent", BLOCK_FLD, N_("Use%"), 4, MBS_ALIGN_RIGHT, false }, + + [ITOTAL_FIELD] = { ITOTAL_FIELD, + "itotal", INODE_FLD, N_("Inodes"), 5, MBS_ALIGN_RIGHT, false }, + + [IUSED_FIELD] = { IUSED_FIELD, + "iused", INODE_FLD, N_("IUsed"), 5, MBS_ALIGN_RIGHT, false }, + + [IAVAIL_FIELD] = { IAVAIL_FIELD, + "iavail", INODE_FLD, N_("IFree"), 5, MBS_ALIGN_RIGHT, false }, + + [IPCENT_FIELD] = { IPCENT_FIELD, + "ipcent", INODE_FLD, N_("IUse%"), 4, MBS_ALIGN_RIGHT, false }, + + [TARGET_FIELD] = { TARGET_FIELD, + "target", OTHER_FLD, N_("Mounted on"), 0, MBS_ALIGN_LEFT, false } }; -/* Auto adjusted (up) widths used to align columns. */ -static size_t widths[NFIELDS] = { 14, 4, 5, 5, 5, 4, 0 }; +static char const *all_args_string = "source,fstype,size,used,avail,pcent," + "itotal,iused,iavail,ipcent,target"; + +/* Storage for the definition of output columns. */ +static struct field_data_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; @@ -168,6 +227,8 @@ enum { NO_SYNC_OPTION = CHAR_MAX + 1, SYNC_OPTION, + TOTAL_OPTION, + OUTPUT_OPTION, MEGABYTES_OPTION /* FIXME: remove long opt in Aug 2013 */ }; @@ -180,11 +241,12 @@ 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}, {"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}, @@ -217,7 +279,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 @@ -226,32 +288,33 @@ alloc_table_row (void) static void print_table (void) { - size_t field, row; + size_t row; - for (row = 0; row < nrows; 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]; - if (!cell) /* Missing type column, or mount point etc. */ - continue; + char *cell = table[row][col]; - /* 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) + functionality was probably more problematic than helpful, + hence changed in commit v8.10-40-g99679ff. */ + if (col != 0) putchar (' '); - if (field == MNT_FIELD) /* The last one. */ - fputs (cell, stdout); - else - { - cell = ambsalign (cell, &width, alignments[field], 0); - /* When ambsalign fails, output unaligned data. */ - fputs (cell ? cell : table[row][field], stdout); - free (cell); - } - IF_LINT (free (table[row][field])); + + int flags = 0; + if (col == ncolumns - 1) /* The last one. */ + 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'); IF_LINT (free (table[row])); @@ -260,29 +323,180 @@ print_table (void) IF_LINT (free (table)); } -/* Obtain the appropriate header entries. */ +/* Dynamically allocate a struct field_t in COLUMNS, which + can then be accessed with standard array notation. */ static void -get_header (void) +alloc_field (int f, const char *c) { - size_t field; + ncolumns++; + columns = xnrealloc (columns, ncolumns, sizeof (struct field_data_t *)); + columns[ncolumns - 1] = &field_data[f]; + if (c != NULL) + columns[ncolumns - 1]->caption = c; - alloc_table_row (); + if (field_data[f].used) + assert (!"field used"); + + /* Mark field as used. */ + field_data[f].used = true; +} - for (field = 0; field < NFIELDS; field++) + +/* 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) +{ + char *arg_writable = xstrdup (arg); + char *s = arg_writable; + do { - if (field == TYPE_FIELD && !print_type) + /* 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 = -1; + for (unsigned int i = 0; i < ARRAY_CARDINALITY (field_data); i++) { - table[nrows-1][field] = NULL; - continue; + if (STREQ (field_data[i].arg, s)) + { + field = i; + break; + } + } + if (field == -1) + { + error (0, 0, _("option --output: field '%s' unknown"), s); + usage (EXIT_FAILURE); + } + + if (field_data[field].used) + { + /* Prevent the fields from being used more than once. */ + error (0, 0, _("option --output: field '%s' used more than once"), + field_data[field].arg); + usage (EXIT_FAILURE); + } + + 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 SIZE_FIELD: + alloc_field (field, N_("Size")); + break; + + case AVAIL_FIELD: + alloc_field (field, N_("Avail")); + break; + + default: + assert (!"invalid field"); + } + s = comma; + } + while (s); + + free (arg_writable); +} + +/* 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 (SIZE_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 (SIZE_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 (SIZE_FIELD, NULL); + alloc_field (USED_FIELD, NULL); + alloc_field (AVAIL_FIELD, NULL); + alloc_field (PCENT_FIELD, N_("Capacity")); + 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 (all_args_string); } + break; + + default: + assert (!"invalid header_mode"); + } +} +/* Obtain the appropriate header entries. */ + +static void +get_header (void) +{ + size_t col; + + alloc_table_row (); + + for (col = 0; col < ncolumns; col++) + { 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 (columns[col]->field == SIZE_FIELD + && (header_mode == DEFAULT_MODE + || (header_mode == OUTPUT_MODE + && !(human_output_opts & human_autoscale)))) { char buf[LONGEST_HUMAN_READABLE + 1]; @@ -315,11 +529,14 @@ get_header (void) char *num = human_readable (output_block_size, buf, opts, 1, 1); + /* Reset the header back to the default in OUTPUT_MODE. */ + header = N_("blocks"); + /* TRANSLATORS: this is the "1K-blocks" header in "df" output. */ 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 == SIZE_FIELD) { char buf[INT_BUFSIZE_BOUND (uintmax_t)]; char *num = umaxtostr (output_block_size, buf); @@ -336,9 +553,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)); } } @@ -409,7 +626,7 @@ df_readable (bool negative, uintmax_t n, char *buf, #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. + 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 @@ -451,6 +668,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. @@ -473,20 +749,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; - size_t field; - if (me_remote && show_local_fs) return; @@ -503,6 +765,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)) @@ -515,12 +778,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 (); @@ -547,148 +806,142 @@ 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; - - if (known_value (total)) - grand_fsu.fsu_files += total; - if (known_value (available)) - grand_fsu.fsu_ffree += available; - } - else - { - 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; - - 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); - } + struct field_values_t block_values; + struct field_values_t inode_values; + get_field_values (&block_values, &inode_values, &fsu); - 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); - } + /* Add to grand total unless processing grand total line. */ + if (print_grand_total && ! force_fsu) + add_to_grand_total (&block_values, &inode_values); - for (field = 0; field < NFIELDS; field++) + size_t col; + for (col = 0; col < ncolumns; col++) { - switch (field) + char buf[LONGEST_HUMAN_READABLE + 2]; + char *cell; + + struct field_values_t *v; + switch (columns[col]->field_type) { - case DEV_FIELD: - cell = dev_name; + 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"); + } + + switch (columns[col]->field) + { + case SOURCE_FIELD: + cell = xstrdup (dev_name); break; - case TYPE_FIELD: - cell = print_type ? xstrdup (fstype) : NULL; + case FSTYPE_FIELD: + cell = xstrdup (fstype); break; - case TOTAL_FIELD: - cell = xstrdup (df_readable (false, total, buf, - input_units, output_units)); + case SIZE_FIELD: + case ITOTAL_FIELD: + cell = xstrdup (df_readable (false, v->total, buf, + v->input_units, v->output_units)); break; + case USED_FIELD: - cell = xstrdup (df_readable (negate_used, used, buf, - input_units, output_units)); + case IUSED_FIELD: + cell = xstrdup (df_readable (v->negate_used, v->used, buf, + v->input_units, v->output_units)); break; - case FREE_FIELD: - cell = xstrdup (df_readable (negate_available, available, buf, - input_units, output_units)); + + case AVAIL_FIELD: + case IAVAIL_FIELD: + cell = xstrdup (df_readable (v->negate_available, v->available, buf, + v->input_units, v->output_units)); break; case PCENT_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; - - /* 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); - } - } + case IPCENT_FIELD: + { + 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; - if (0 <= pct) - { - if (asprintf (&cell, "%.0f%%", pct) == -1) - cell = NULL; - } - else - cell = strdup ("-"); + /* 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 (!cell) - xalloc_die (); + if (0 <= pct) + { + if (asprintf (&cell, "%.0f%%", pct) == -1) + cell = NULL; + } + else + cell = strdup ("-"); - break; + if (!cell) + xalloc_die (); - case MNT_FIELD: - if (mount_point) - { + break; + } + + case TARGET_FIELD: #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); - widths[field] = MAX (widths[field], mbswidth (cell, 0)); - } - table[nrows-1][field] = cell; + if (!cell) + assert (!"empty cell"); + + hide_problematic_chars (cell); + 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 @@ -772,7 +1025,7 @@ get_point (const char *point, const struct stat *statp) exit_status = EXIT_FAILURE; } - /* So we won't try and fail repeatedly. */ + /* So we won't try and fail repeatedly. */ me->me_dev = (dev_t) -2; } } @@ -824,7 +1077,7 @@ get_entry (char const *name, struct stat const *statp) } /* Show all mounted file systems, except perhaps those that are of - an unselected type or are empty. */ + an unselected type or are empty. */ static void get_all_entries (void) @@ -836,7 +1089,7 @@ get_all_entries (void) me->me_dummy, me->me_remote, NULL, true); } -/* Add FSTYPE to the list of file system types to display. */ +/* Add FSTYPE to the list of file system types to display. */ static void add_fs_type (const char *fstype) @@ -849,7 +1102,7 @@ add_fs_type (const char *fstype) fs_select_list = fsp; } -/* Add FSTYPE to the list of file system types to be omitted. */ +/* Add FSTYPE to the list of file system types to be omitted. */ static void add_excluded_fs_type (const char *fstype) @@ -927,17 +1180,20 @@ 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; print_type = false; file_systems_processed = false; - posix_format = false; exit_status = EXIT_SUCCESS; print_grand_total = false; grand_fsu.fsu_blocksize = 1; + /* If true, use the POSIX output format. */ + bool posix_format = false; + + const char *msg_mut_excl = _("options %s and %s are mutually exclusive"); + while (true) { int oi = -1; @@ -960,7 +1216,12 @@ main (int argc, char **argv) } break; case 'i': - inode_format = true; + if (header_mode == OUTPUT_MODE) + { + error (0, 0, msg_mut_excl, "-i", "--output"); + usage (EXIT_FAILURE); + } + header_mode = INODES_MODE; break; case 'h': human_output_opts = human_autoscale | human_SI | human_base_1024; @@ -989,9 +1250,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: @@ -1007,14 +1278,35 @@ main (int argc, char **argv) add_fs_type (optarg); break; - case 'v': /* For SysV compatibility. */ + case 'v': /* For SysV compatibility. */ /* ignore */ break; case 'x': add_excluded_fs_type (optarg); break; - case 'c': + 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; @@ -1038,8 +1330,8 @@ main (int argc, char **argv) &human_output_opts, &output_block_size); } - if (inode_format) - header_mode = INODES_MODE; + if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE) + ; else if (human_output_opts & human_autoscale) header_mode = HUMAN_MODE; else if (posix_format) @@ -1097,6 +1389,7 @@ main (int argc, char **argv) read_file_system_list ((fs_select_list != NULL || fs_exclude_list != NULL || print_type + || field_data[FSTYPE_FIELD].used || show_local_fs)); if (mount_list == NULL) @@ -1104,7 +1397,7 @@ main (int argc, char **argv) /* Couldn't read the table of mounted file systems. Fail if df was invoked with no file name arguments, or when either of -a, -l, -t or -x is used with file name - arguments. Otherwise, merely give a warning and proceed. */ + arguments. Otherwise, merely give a warning and proceed. */ int status = 0; if ( ! (optind < argc) || (show_all_fs @@ -1122,11 +1415,14 @@ main (int argc, char **argv) if (require_sync) sync (); + get_field_list (); + get_header (); + if (optind < argc) { int i; - /* Display explicitly requested empty file systems. */ + /* Display explicitly requested empty file systems. */ show_listed_fs = true; for (i = optind; i < argc; ++i) @@ -1136,19 +1432,24 @@ main (int argc, char **argv) else get_all_entries (); - if (print_grand_total && file_systems_processed) + if (file_systems_processed) { - if (inode_format) - grand_fsu.fsu_blocks = 1; - get_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu, false); - } + if (print_grand_total) + get_dev ("total", + (field_data[SOURCE_FIELD].used ? "-" : "total"), + NULL, NULL, false, false, &grand_fsu, false); - print_table (); + 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")); + } - /* 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")); + IF_LINT (free (columns)); exit (exit_status); } diff --git a/tests/df/total-verify.sh b/tests/df/total-verify.sh index 45c838ecf..18d215f11 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"; |