diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2007-03-22 21:23:21 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2007-03-22 21:23:21 +0000 |
commit | cbf5993c43f49281173f185863577d86bfac6eae (patch) | |
tree | 90737c96cf15b97273a2bdc5950b3cf09f1d94ca /src/df.c | |
download | coreutils-tarball-cbf5993c43f49281173f185863577d86bfac6eae.tar.gz |
coreutils-6.9coreutils-6.9
Diffstat (limited to 'src/df.c')
-rw-r--r-- | src/df.c | 967 |
1 files changed, 967 insertions, 0 deletions
diff --git a/src/df.c b/src/df.c new file mode 100644 index 0000000..609787e --- /dev/null +++ b/src/df.c @@ -0,0 +1,967 @@ +/* df - summarize free disk space + Copyright (C) 91, 1995-2007 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. + --human-readable and --megabyte options added by lm@sgi.com. + --si and large file support added by eggert@twinsun.com. */ + +#include <config.h> +#include <stdio.h> +#include <sys/types.h> +#include <getopt.h> + +#include "system.h" +#include "canonicalize.h" +#include "error.h" +#include "fsusage.h" +#include "human.h" +#include "inttostr.h" +#include "mountlist.h" +#include "quote.h" +#include "save-cwd.h" +#include "xgetcwd.h" + +/* The official name of this program (e.g., no `g' prefix). */ +#define PROGRAM_NAME "df" + +#define AUTHORS \ + "Torbjorn Granlund", "David MacKenzie", "Paul Eggert" + +/* Name this program was run with. */ +char *program_name; + +/* 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; + +/* If true, show only local file systems. */ +static bool show_local_fs; + +/* If true, output data for each file system corresponding to a + command line argument -- even if it's a dummy (automounter) entry. */ +static bool show_listed_fs; + +/* Human-readable options for output. */ +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; + +/* If true, invoke the `sync' system call before getting any usage data. + Using this option can make df very slow, especially with many or very + busy disks. Note that this may make a difference on some systems -- + SunOS 4.1.3, for one. It is *not* necessary on Linux. */ +static bool require_sync; + +/* Desired exit status. */ +static int exit_status; + +/* A file system type to display. */ + +struct fs_type_list +{ + char *fs_name; + struct fs_type_list *fs_next; +}; + +/* Linked list of file system types to display. + If `fs_select_list' is NULL, list all types. + This table is generated dynamically from command-line options, + rather than hardcoding into the program what it thinks are the + valid file system types; let the user specify any file system type + they want to, and if there are any file systems of that type, they + will be shown. + + Some file system types: + 4.2 4.3 ufs nfs swap ignore io vm efs dbg */ + +static struct fs_type_list *fs_select_list; + +/* Linked list of file system types to omit. + If the list is empty, don't exclude any types. */ + +static struct fs_type_list *fs_exclude_list; + +/* Linked list of mounted file systems. */ +static struct mount_entry *mount_list; + +/* If true, print file system type as well. */ +static bool print_type; + +/* For long options that have no equivalent short option, use a + non-character as a pseudo short option, starting with CHAR_MAX + 1. */ +enum +{ + NO_SYNC_OPTION = CHAR_MAX + 1, + /* FIXME: --kilobytes is deprecated (but not -k); remove in late 2006 */ + KILOBYTES_LONG_OPTION, + SYNC_OPTION +}; + +static struct option const long_options[] = +{ + {"all", no_argument, NULL, 'a'}, + {"block-size", required_argument, NULL, 'B'}, + {"inodes", no_argument, NULL, 'i'}, + {"human-readable", no_argument, NULL, 'h'}, + {"si", no_argument, NULL, 'H'}, + {"kilobytes", no_argument, NULL, KILOBYTES_LONG_OPTION}, + {"local", no_argument, NULL, 'l'}, + {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */ + {"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}, + {"type", required_argument, NULL, 't'}, + {"exclude-type", required_argument, NULL, 'x'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +}; + +static void +print_header (void) +{ + char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))]; + + if (print_type) + fputs (_("Filesystem Type"), stdout); + else + fputs (_("Filesystem "), stdout); + + if (inode_format) + printf (_(" Inodes IUsed IFree IUse%%")); + else if (human_output_opts & human_autoscale) + { + if (human_output_opts & human_base_1024) + printf (_(" Size Used Avail Use%%")); + else + printf (_(" Size Used Avail Use%%")); + } + else if (posix_format) + printf (_(" %s-blocks Used Available Capacity"), + umaxtostr (output_block_size, buf)); + else + { + int opts = (human_suppress_point_zero + | human_autoscale | human_SI + | (human_output_opts + & (human_group_digits | human_base_1024 | human_B))); + + /* Prefer the base that makes the human-readable value more exact, + if there is a difference. */ + + uintmax_t q1000 = output_block_size; + uintmax_t q1024 = output_block_size; + bool divisible_by_1000; + bool divisible_by_1024; + + do + { + divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000; + divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024; + } + while (divisible_by_1000 & divisible_by_1024); + + if (divisible_by_1000 < divisible_by_1024) + opts |= human_base_1024; + if (divisible_by_1024 < divisible_by_1000) + opts &= ~human_base_1024; + if (! (opts & human_base_1024)) + opts |= human_B; + + printf (_(" %4s-blocks Used Available Use%%"), + human_readable (output_block_size, buf, opts, 1, 1)); + } + + printf (_(" Mounted on\n")); +} + +/* Is FSTYPE a type of file system that should be listed? */ + +static bool +selected_fstype (const char *fstype) +{ + const struct fs_type_list *fsp; + + if (fs_select_list == NULL || fstype == NULL) + return true; + for (fsp = fs_select_list; fsp; fsp = fsp->fs_next) + if (STREQ (fstype, fsp->fs_name)) + return true; + return false; +} + +/* Is FSTYPE a type of file system that should be omitted? */ + +static bool +excluded_fstype (const char *fstype) +{ + const struct fs_type_list *fsp; + + if (fs_exclude_list == NULL || fstype == NULL) + return false; + for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next) + if (STREQ (fstype, fsp->fs_name)) + return true; + return false; +} + +/* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS), + except: + + - If NEGATIVE, then N represents a negative number, + expressed in two's complement. + - Otherwise, return "-" if N is UINTMAX_MAX. */ + +static char const * +df_readable (bool negative, uintmax_t n, char *buf, + uintmax_t input_units, uintmax_t output_units) +{ + if (n == UINTMAX_MAX && !negative) + return "-"; + else + { + char *p = human_readable (negative ? -n : n, buf + negative, + human_output_opts, input_units, output_units); + if (negative) + *--p = '-'; + return p; + } +} + +/* Display a space listing for the disk device with absolute file name DISK. + If MOUNT_POINT is non-NULL, it is the name of the root of the + file system on DISK. + If STAT_FILE is non-null, it is the name of a file within the file + system that the user originally asked for; this provides better + diagnostics, and sometimes it provides better results on networked + file systems that give different free-space results depending on + where in the file system you probe. + If FSTYPE is non-NULL, it is the type of the file system on DISK. + If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may + not be able to produce statistics in this case. + ME_DUMMY and ME_REMOTE are the mount entry flags. */ + +static void +show_dev (char const *disk, char const *mount_point, + char const *stat_file, char const *fstype, + bool me_dummy, bool me_remote) +{ + struct fs_usage fsu; + char buf[3][LONGEST_HUMAN_READABLE + 2]; + int width; + int col1_adjustment = 0; + int use_width; + 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; + + if (me_remote & show_local_fs) + return; + + if (me_dummy & !show_all_fs & !show_listed_fs) + return; + + if (!selected_fstype (fstype) || excluded_fstype (fstype)) + return; + + /* If MOUNT_POINT is NULL, then the file system is not mounted, and this + program reports on the file system that the special file is on. + It would be better to report on the unmounted file system, + but statfs doesn't do that on most systems. */ + if (!stat_file) + stat_file = mount_point ? mount_point : disk; + + if (get_fs_usage (stat_file, disk, &fsu)) + { + error (0, errno, "%s", quote (stat_file)); + exit_status = EXIT_FAILURE; + return; + } + + if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs) + return; + + if (! file_systems_processed) + { + file_systems_processed = true; + print_header (); + } + + if (! disk) + disk = "-"; /* unknown */ + if (! fstype) + fstype = "-"; /* unknown */ + + /* df.c reserved 5 positions for fstype, + but that does not suffice for type iso9660 */ + if (print_type) + { + size_t disk_name_len = strlen (disk); + size_t fstype_len = strlen (fstype); + if (disk_name_len + fstype_len < 18) + printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype); + else if (!posix_format) + printf ("%s\n%18s ", disk, fstype); + else + printf ("%s %s", disk, fstype); + } + else + { + if (strlen (disk) > 20 && !posix_format) + printf ("%s\n%20s", disk, ""); + else + printf ("%-20s", disk); + } + + if (inode_format) + { + width = 7; + use_width = 5; + input_units = output_units = 1; + total = fsu.fsu_files; + available = fsu.fsu_ffree; + negate_available = false; + available_to_root = available; + } + else + { + if (human_output_opts & human_autoscale) + width = 5 + ! (human_output_opts & human_base_1024); + else + { + width = 9; + if (posix_format) + { + uintmax_t b; + col1_adjustment = -3; + for (b = output_block_size; 9 < b; b /= 10) + col1_adjustment++; + } + } + use_width = ((posix_format + && ! (human_output_opts & human_autoscale)) + ? 8 : 4); + 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 + & (available != UINTMAX_MAX)); + available_to_root = fsu.fsu_bfree; + } + + used = UINTMAX_MAX; + negate_used = false; + if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX) + { + used = total - available_to_root; + negate_used = (total < available_to_root); + } + + printf (" %*s %*s %*s ", + width + col1_adjustment, + df_readable (false, total, + buf[0], input_units, output_units), + width, df_readable (negate_used, used, + buf[1], input_units, output_units), + width, df_readable (negate_available, available, + buf[2], input_units, output_units)); + + if (used == UINTMAX_MAX || available == UINTMAX_MAX) + ; + 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); + } + } + + if (0 <= pct) + printf ("%*.0f%%", use_width - 1, pct); + else + printf ("%*s", use_width, "- "); + + 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 ("/auto/", mount_point, 6) == 0) + mount_point += 5; + else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0) + mount_point += 8; +#endif + printf (" %s", mount_point); + } + putchar ('\n'); +} + +/* Return the root mountpoint of the file system on which FILE exists, in + malloced storage. FILE_STAT should be the result of stating FILE. + Give a diagnostic and return NULL if unable to determine the mount point. + Exit if unable to restore current working directory. */ +static char * +find_mount_point (const char *file, const struct stat *file_stat) +{ + struct saved_cwd cwd; + struct stat last_stat; + char *mp = NULL; /* The malloced mount point. */ + + if (save_cwd (&cwd) != 0) + { + error (0, errno, _("cannot get current directory")); + return NULL; + } + + if (S_ISDIR (file_stat->st_mode)) + /* FILE is a directory, so just chdir there directly. */ + { + last_stat = *file_stat; + if (chdir (file) < 0) + { + error (0, errno, _("cannot change to directory %s"), quote (file)); + return NULL; + } + } + else + /* FILE is some other kind of file; use its directory. */ + { + char *xdir = dir_name (file); + char *dir; + ASSIGN_STRDUPA (dir, xdir); + free (xdir); + + if (chdir (dir) < 0) + { + error (0, errno, _("cannot change to directory %s"), quote (dir)); + return NULL; + } + + if (stat (".", &last_stat) < 0) + { + error (0, errno, _("cannot stat current directory (now %s)"), + quote (dir)); + goto done; + } + } + + /* Now walk up FILE's parents until we find another file system or /, + chdiring as we go. LAST_STAT holds stat information for the last place + we visited. */ + for (;;) + { + struct stat st; + if (stat ("..", &st) < 0) + { + error (0, errno, _("cannot stat %s"), quote ("..")); + goto done; + } + if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino) + /* cwd is the mount point. */ + break; + if (chdir ("..") < 0) + { + error (0, errno, _("cannot change to directory %s"), quote ("..")); + goto done; + } + last_stat = st; + } + + /* Finally reached a mount point, see what it's called. */ + mp = xgetcwd (); + +done: + /* Restore the original cwd. */ + { + int save_errno = errno; + if (restore_cwd (&cwd) != 0) + error (EXIT_FAILURE, errno, + _("failed to return to initial working directory")); + free_cwd (&cwd); + errno = save_errno; + } + + return mp; +} + +/* If DISK corresponds to a mount point, show its usage + and return true. Otherwise, return false. */ +static bool +show_disk (char const *disk) +{ + struct mount_entry const *me; + struct mount_entry const *best_match = NULL; + + for (me = mount_list; me; me = me->me_next) + if (STREQ (disk, me->me_devname)) + best_match = me; + + if (best_match) + { + show_dev (best_match->me_devname, best_match->me_mountdir, NULL, + best_match->me_type, best_match->me_dummy, + best_match->me_remote); + return true; + } + + return false; +} + +/* Figure out which device file or directory POINT is mounted on + and show its disk usage. + STATP must be the result of `stat (POINT, STATP)'. */ +static void +show_point (const char *point, const struct stat *statp) +{ + struct stat disk_stats; + struct mount_entry *me; + struct mount_entry const *best_match = NULL; + + /* If POINT is an absolute file name, see if we can find the + mount point without performing any extra stat calls at all. */ + if (*point == '/') + { + /* Find the best match: prefer non-dummies, and then prefer the + last match if there are ties. */ + + for (me = mount_list; me; me = me->me_next) + if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs") + && (!best_match || best_match->me_dummy || !me->me_dummy)) + best_match = me; + } + + /* Calculate the real absolute file name for POINT, and use that to find + the mount point. This avoids statting unavailable mount points, + which can hang df. */ + if (! best_match) + { + char *resolved = canonicalize_file_name (point); + + if (resolved && resolved[0] == '/') + { + size_t resolved_len = strlen (resolved); + size_t best_match_len = 0; + + for (me = mount_list; me; me = me->me_next) + if (!STREQ (me->me_type, "lofs") + && (!best_match || best_match->me_dummy || !me->me_dummy)) + { + size_t len = strlen (me->me_mountdir); + if (best_match_len <= len && len <= resolved_len + && (len == 1 /* root file system */ + || ((len == resolved_len || resolved[len] == '/') + && strncmp (me->me_mountdir, resolved, len) == 0))) + { + best_match = me; + best_match_len = len; + } + } + } + + free (resolved); + + if (best_match + && (stat (best_match->me_mountdir, &disk_stats) != 0 + || disk_stats.st_dev != statp->st_dev)) + best_match = NULL; + } + + if (! best_match) + for (me = mount_list; me; me = me->me_next) + { + if (me->me_dev == (dev_t) -1) + { + if (stat (me->me_mountdir, &disk_stats) == 0) + me->me_dev = disk_stats.st_dev; + else + { + /* Report only I/O errors. Other errors might be + caused by shadowed mount points, which means POINT + can't possibly be on this file system. */ + if (errno == EIO) + { + error (0, errno, "%s", quote (me->me_mountdir)); + exit_status = EXIT_FAILURE; + } + + /* So we won't try and fail repeatedly. */ + me->me_dev = (dev_t) -2; + } + } + + if (statp->st_dev == me->me_dev + && !STREQ (me->me_type, "lofs") + && (!best_match || best_match->me_dummy || !me->me_dummy)) + { + /* Skip bogus mtab entries. */ + if (stat (me->me_mountdir, &disk_stats) != 0 + || disk_stats.st_dev != me->me_dev) + me->me_dev = (dev_t) -2; + else + best_match = me; + } + } + + if (best_match) + show_dev (best_match->me_devname, best_match->me_mountdir, point, + best_match->me_type, best_match->me_dummy, best_match->me_remote); + else + { + /* We couldn't find the mount entry corresponding to POINT. Go ahead and + print as much info as we can; methods that require the device to be + present will fail at a later point. */ + + /* Find the actual mount point. */ + char *mp = find_mount_point (point, statp); + if (mp) + { + show_dev (NULL, mp, NULL, NULL, false, false); + free (mp); + } + } +} + +/* Determine what kind of node NAME is and show the disk usage + for it. STATP is the results of `stat' on NAME. */ + +static void +show_entry (char const *name, struct stat const *statp) +{ + if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode)) + && show_disk (name)) + return; + + show_point (name, statp); +} + +/* Show all mounted file systems, except perhaps those that are of + an unselected type or are empty. */ + +static void +show_all_entries (void) +{ + struct mount_entry *me; + + for (me = mount_list; me; me = me->me_next) + show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type, + me->me_dummy, me->me_remote); +} + +/* Add FSTYPE to the list of file system types to display. */ + +static void +add_fs_type (const char *fstype) +{ + struct fs_type_list *fsp; + + fsp = xmalloc (sizeof *fsp); + fsp->fs_name = (char *) fstype; + fsp->fs_next = fs_select_list; + fs_select_list = fsp; +} + +/* Add FSTYPE to the list of file system types to be omitted. */ + +static void +add_excluded_fs_type (const char *fstype) +{ + struct fs_type_list *fsp; + + fsp = xmalloc (sizeof *fsp); + fsp->fs_name = (char *) fstype; + fsp->fs_next = fs_exclude_list; + fs_exclude_list = fsp; +} + +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name); + fputs (_("\ +Show information about the file system on which each FILE resides,\n\ +or all file systems by default.\n\ +\n\ +"), stdout); + fputs (_("\ +Mandatory arguments to long options are mandatory for short options too.\n\ +"), stdout); + fputs (_("\ + -a, --all include dummy file systems\n\ + -B, --block-size=SIZE use SIZE-byte blocks\n\ + -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\ + -H, --si likewise, but use powers of 1000 not 1024\n\ +"), stdout); + fputs (_("\ + -i, --inodes list inode information instead of block usage\n\ + -k like --block-size=1K\n\ + -l, --local limit listing to local file systems\n\ + --no-sync do not invoke sync before getting usage info (default)\n\ +"), stdout); + fputs (_("\ + -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\ + -T, --print-type print file system type\n\ + -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\ + -v (ignored)\n\ +"), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + fputs (_("\n\ +SIZE may be (or may be an integer optionally followed by) one of following:\n\ +kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\ +"), stdout); + printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + } + exit (status); +} + +int +main (int argc, char **argv) +{ + int c; + struct stat *stats IF_LINT (= 0); + + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + atexit (close_stdout); + + 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; + + while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL)) + != -1) + { + switch (c) + { + case 'a': + show_all_fs = true; + break; + case 'B': + human_output_opts = human_options (optarg, true, &output_block_size); + break; + case 'i': + inode_format = true; + break; + case 'h': + human_output_opts = human_autoscale | human_SI | human_base_1024; + output_block_size = 1; + break; + case 'H': + human_output_opts = human_autoscale | human_SI; + output_block_size = 1; + break; + case KILOBYTES_LONG_OPTION: + error (0, 0, + _("the --kilobytes option is deprecated; use -k instead")); + /* fall through */ + case 'k': + human_output_opts = 0; + output_block_size = 1024; + break; + case 'l': + show_local_fs = true; + break; + case 'm': /* obsolescent */ + human_output_opts = 0; + output_block_size = 1024 * 1024; + break; + case 'T': + print_type = true; + break; + case 'P': + posix_format = true; + break; + case SYNC_OPTION: + require_sync = true; + break; + case NO_SYNC_OPTION: + require_sync = false; + break; + + case 'F': + /* Accept -F as a synonym for -t for compatibility with Solaris. */ + case 't': + add_fs_type (optarg); + break; + + case 'v': /* For SysV compatibility. */ + /* ignore */ + break; + case 'x': + add_excluded_fs_type (optarg); + break; + + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + + default: + usage (EXIT_FAILURE); + } + } + + if (human_output_opts == -1) + { + if (posix_format) + { + human_output_opts = 0; + output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024); + } + else + human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false, + &output_block_size); + } + + /* Fail if the same file system type was both selected and excluded. */ + { + bool match = false; + struct fs_type_list *fs_incl; + for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next) + { + struct fs_type_list *fs_excl; + for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next) + { + if (STREQ (fs_incl->fs_name, fs_excl->fs_name)) + { + error (0, 0, + _("file system type %s both selected and excluded"), + quote (fs_incl->fs_name)); + match = true; + break; + } + } + } + if (match) + exit (EXIT_FAILURE); + } + + if (optind < argc) + { + int i; + + /* stat all the given entries to make sure they get automounted, + if necessary, before reading the file system table. */ + stats = xnmalloc (argc - optind, sizeof *stats); + for (i = optind; i < argc; ++i) + { + if (stat (argv[i], &stats[i - optind])) + { + error (0, errno, "%s", quote (argv[i])); + exit_status = EXIT_FAILURE; + argv[i] = NULL; + } + } + } + + mount_list = + read_file_system_list ((fs_select_list != NULL + || fs_exclude_list != NULL + || print_type + || show_local_fs)); + + if (mount_list == NULL) + { + /* Couldn't read the table of mounted file systems. + Fail if df was invoked with no file name arguments; + Otherwise, merely give a warning and proceed. */ + const char *warning = (optind < argc ? _("Warning: ") : ""); + int status = (optind < argc ? 0 : EXIT_FAILURE); + error (status, errno, + _("%scannot read table of mounted file systems"), warning); + } + + if (require_sync) + sync (); + + if (optind < argc) + { + int i; + + /* Display explicitly requested empty file systems. */ + show_listed_fs = true; + + for (i = optind; i < argc; ++i) + if (argv[i]) + show_entry (argv[i], &stats[i - optind]); + } + else + show_all_entries (); + + if (! file_systems_processed) + error (EXIT_FAILURE, 0, _("no file systems processed")); + + exit (exit_status); +} |