Avoid spurious failure on x86 solaris2.9 when using c89.
[coreutils.git] / src / df.c
blobc26144cfe79b70c5a19a3eebb4d3bac961871152
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2004 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
19 --human-readable and --megabyte options added by lm@sgi.com.
20 --si and large file support added by eggert@twinsun.com. */
22 #include <config.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <getopt.h>
26 #include <assert.h>
28 #include "system.h"
29 #include "canonicalize.h"
30 #include "dirname.h"
31 #include "error.h"
32 #include "fsusage.h"
33 #include "human.h"
34 #include "inttostr.h"
35 #include "mountlist.h"
36 #include "path-concat.h"
37 #include "quote.h"
38 #include "save-cwd.h"
39 #include "xgetcwd.h"
41 /* The official name of this program (e.g., no `g' prefix). */
42 #define PROGRAM_NAME "df"
44 #define AUTHORS \
45 "Torbjorn Granlund", "David MacKenzie", "Paul Eggert"
47 /* Name this program was run with. */
48 char *program_name;
50 /* If true, show inode information. */
51 static bool inode_format;
53 /* If true, show even file systems with zero size or
54 uninteresting types. */
55 static bool show_all_fs;
57 /* If true, show only local file systems. */
58 static bool show_local_fs;
60 /* If true, output data for each file system corresponding to a
61 command line argument -- even if it's a dummy (automounter) entry. */
62 static bool show_listed_fs;
64 /* Human-readable options for output. */
65 static int human_output_opts;
67 /* The units to use when printing sizes. */
68 static uintmax_t output_block_size;
70 /* If true, use the POSIX output format. */
71 static bool posix_format;
73 /* If true, invoke the `sync' system call before getting any usage data.
74 Using this option can make df very slow, especially with many or very
75 busy disks. Note that this may make a difference on some systems --
76 SunOS 4.1.3, for one. It is *not* necessary on Linux. */
77 static bool require_sync;
79 /* Desired exit status. */
80 static int exit_status;
82 /* A file system type to display. */
84 struct fs_type_list
86 char *fs_name;
87 struct fs_type_list *fs_next;
90 /* Linked list of file system types to display.
91 If `fs_select_list' is NULL, list all types.
92 This table is generated dynamically from command-line options,
93 rather than hardcoding into the program what it thinks are the
94 valid file system types; let the user specify any file system type
95 they want to, and if there are any file systems of that type, they
96 will be shown.
98 Some file system types:
99 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
101 static struct fs_type_list *fs_select_list;
103 /* Linked list of file system types to omit.
104 If the list is empty, don't exclude any types. */
106 static struct fs_type_list *fs_exclude_list;
108 /* Linked list of mounted file systems. */
109 static struct mount_entry *mount_list;
111 /* If true, print file system type as well. */
112 static bool print_type;
114 /* For long options that have no equivalent short option, use a
115 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
116 enum
118 SYNC_OPTION = CHAR_MAX + 1,
119 NO_SYNC_OPTION
122 static struct option const long_options[] =
124 {"all", no_argument, NULL, 'a'},
125 {"block-size", required_argument, NULL, 'B'},
126 {"inodes", no_argument, NULL, 'i'},
127 {"human-readable", no_argument, NULL, 'h'},
128 {"si", no_argument, NULL, 'H'},
129 {"kilobytes", no_argument, NULL, 'k'}, /* long form is obsolescent */
130 {"local", no_argument, NULL, 'l'},
131 {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */
132 {"portability", no_argument, NULL, 'P'},
133 {"print-type", no_argument, NULL, 'T'},
134 {"sync", no_argument, NULL, SYNC_OPTION},
135 {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
136 {"type", required_argument, NULL, 't'},
137 {"exclude-type", required_argument, NULL, 'x'},
138 {GETOPT_HELP_OPTION_DECL},
139 {GETOPT_VERSION_OPTION_DECL},
140 {NULL, 0, NULL, 0}
143 static void
144 print_header (void)
146 char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
148 if (print_type)
149 fputs (_("Filesystem Type"), stdout);
150 else
151 fputs (_("Filesystem "), stdout);
153 if (inode_format)
154 printf (_(" Inodes IUsed IFree IUse%%"));
155 else if (human_output_opts & human_autoscale)
157 if (human_output_opts & human_base_1024)
158 printf (_(" Size Used Avail Use%%"));
159 else
160 printf (_(" Size Used Avail Use%%"));
162 else if (posix_format)
163 printf (_(" %4s-blocks Used Available Capacity"),
164 umaxtostr (output_block_size, buf));
165 else
167 int opts = (human_suppress_point_zero
168 | human_autoscale | human_SI
169 | (human_output_opts
170 & (human_group_digits | human_base_1024 | human_B)));
172 /* Prefer the base that makes the human-readable value more exact,
173 if there is a difference. */
175 uintmax_t q1000 = output_block_size;
176 uintmax_t q1024 = output_block_size;
177 bool divisible_by_1000;
178 bool divisible_by_1024;
182 divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
183 divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
185 while (divisible_by_1000 & divisible_by_1024);
187 if (divisible_by_1000 < divisible_by_1024)
188 opts |= human_base_1024;
189 if (divisible_by_1024 < divisible_by_1000)
190 opts &= ~human_base_1024;
191 if (! (opts & human_base_1024))
192 opts |= human_B;
194 printf (_(" %4s-blocks Used Available Use%%"),
195 human_readable (output_block_size, buf, opts, 1, 1));
198 printf (_(" Mounted on\n"));
201 /* Is FSTYPE a type of file system that should be listed? */
203 static bool
204 selected_fstype (const char *fstype)
206 const struct fs_type_list *fsp;
208 if (fs_select_list == NULL || fstype == NULL)
209 return true;
210 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
211 if (STREQ (fstype, fsp->fs_name))
212 return true;
213 return false;
216 /* Is FSTYPE a type of file system that should be omitted? */
218 static bool
219 excluded_fstype (const char *fstype)
221 const struct fs_type_list *fsp;
223 if (fs_exclude_list == NULL || fstype == NULL)
224 return false;
225 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
226 if (STREQ (fstype, fsp->fs_name))
227 return true;
228 return false;
231 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
232 except:
234 - Return "-" if N is -1,
235 - If NEGATIVE is 1 then N represents a negative number,
236 expressed in two's complement. */
238 static char const *
239 df_readable (bool negative, uintmax_t n, char *buf,
240 uintmax_t input_units, uintmax_t output_units)
242 if (n == UINTMAX_MAX)
243 return "-";
244 else
246 char *p = human_readable (negative ? -n : n, buf + negative,
247 human_output_opts, input_units, output_units);
248 if (negative)
249 *--p = '-';
250 return p;
254 /* Display a space listing for the disk device with absolute path DISK.
255 If MOUNT_POINT is non-NULL, it is the path of the root of the
256 file system on DISK.
257 If FSTYPE is non-NULL, it is the type of the file system on DISK.
258 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
259 not be able to produce statistics in this case.
260 ME_DUMMY and ME_REMOTE are the mount entry flags. */
262 static void
263 show_dev (const char *disk, const char *mount_point, const char *fstype,
264 bool me_dummy, bool me_remote)
266 struct fs_usage fsu;
267 const char *stat_file;
268 char buf[3][LONGEST_HUMAN_READABLE + 2];
269 int width;
270 int use_width;
271 uintmax_t input_units;
272 uintmax_t output_units;
273 uintmax_t total;
274 uintmax_t available;
275 bool negate_available;
276 uintmax_t available_to_root;
277 uintmax_t used;
278 bool negate_used;
279 double pct = -1;
281 if (me_remote & show_local_fs)
282 return;
284 if (me_dummy & !show_all_fs & !show_listed_fs)
285 return;
287 if (!selected_fstype (fstype) || excluded_fstype (fstype))
288 return;
290 /* If MOUNT_POINT is NULL, then the file system is not mounted, and this
291 program reports on the file system that the special file is on.
292 It would be better to report on the unmounted file system,
293 but statfs doesn't do that on most systems. */
294 stat_file = mount_point ? mount_point : disk;
296 if (get_fs_usage (stat_file, disk, &fsu))
298 error (0, errno, "%s", quote (stat_file));
299 exit_status = EXIT_FAILURE;
300 return;
303 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
304 return;
306 if (! disk)
307 disk = "-"; /* unknown */
308 if (! fstype)
309 fstype = "-"; /* unknown */
311 /* df.c reserved 5 positions for fstype,
312 but that does not suffice for type iso9660 */
313 if (print_type)
315 size_t disk_name_len = strlen (disk);
316 size_t fstype_len = strlen (fstype);
317 if (disk_name_len + fstype_len < 18)
318 printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype);
319 else if (!posix_format)
320 printf ("%s\n%18s ", disk, fstype);
321 else
322 printf ("%s %s", disk, fstype);
324 else
326 if (strlen (disk) > 20 && !posix_format)
327 printf ("%s\n%20s", disk, "");
328 else
329 printf ("%-20s", disk);
332 if (inode_format)
334 width = 7;
335 use_width = 5;
336 input_units = output_units = 1;
337 total = fsu.fsu_files;
338 available = fsu.fsu_ffree;
339 negate_available = false;
340 available_to_root = available;
342 else
344 width = (human_output_opts & human_autoscale
345 ? 5 + ! (human_output_opts & human_base_1024)
346 : 9);
347 use_width = ((posix_format
348 && ! (human_output_opts & human_autoscale))
349 ? 8 : 4);
350 input_units = fsu.fsu_blocksize;
351 output_units = output_block_size;
352 total = fsu.fsu_blocks;
353 available = fsu.fsu_bavail;
354 negate_available = fsu.fsu_bavail_top_bit_set;
355 available_to_root = fsu.fsu_bfree;
358 used = -1;
359 negate_used = false;
360 if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX)
362 used = total - available_to_root;
363 if (total < available_to_root)
365 negate_used = true;
366 used = - used;
370 printf (" %*s %*s %*s ",
371 width, df_readable (false, total,
372 buf[0], input_units, output_units),
373 width, df_readable (negate_used, used,
374 buf[1], input_units, output_units),
375 width, df_readable (negate_available, available,
376 buf[2], input_units, output_units));
378 if (used == UINTMAX_MAX || available == UINTMAX_MAX)
380 else if (!negate_used
381 && used <= TYPE_MAXIMUM (uintmax_t) / 100
382 && used + available != 0
383 && (used + available < used) == negate_available)
385 uintmax_t u100 = used * 100;
386 uintmax_t nonroot_total = used + available;
387 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
389 else
391 /* The calculation cannot be done easily with integer
392 arithmetic. Fall back on floating point. This can suffer
393 from minor rounding errors, but doing it exactly requires
394 multiple precision arithmetic, and it's not worth the
395 aggravation. */
396 double u = negate_used ? - (double) - used : used;
397 double a = negate_available ? - (double) - available : available;
398 double nonroot_total = u + a;
399 if (nonroot_total)
401 long int lipct = pct = u * 100 / nonroot_total;
402 double ipct = lipct;
404 /* Like `pct = ceil (dpct);', but avoid ceil so that
405 the math library needn't be linked. */
406 if (ipct - 1 < pct && pct <= ipct + 1)
407 pct = ipct + (ipct < pct);
411 if (0 <= pct)
412 printf ("%*.0f%%", use_width - 1, pct);
413 else
414 printf ("%*s", use_width, "- ");
416 if (mount_point)
418 #ifdef HIDE_AUTOMOUNT_PREFIX
419 /* Don't print the first directory name in MOUNT_POINT if it's an
420 artifact of an automounter. This is a bit too aggressive to be
421 the default. */
422 if (strncmp ("/auto/", mount_point, 6) == 0)
423 mount_point += 5;
424 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
425 mount_point += 8;
426 #endif
427 printf (" %s", mount_point);
429 putchar ('\n');
432 /* Return the root mountpoint of the file system on which FILE exists, in
433 malloced storage. FILE_STAT should be the result of stating FILE.
434 Give a diagnostic and return NULL if unable to determine the mount point.
435 Exit if unable to restore current working directory. */
436 static char *
437 find_mount_point (const char *file, const struct stat *file_stat)
439 struct saved_cwd cwd;
440 struct stat last_stat;
441 char *mp = 0; /* The malloced mount point path. */
443 if (save_cwd (&cwd) != 0)
445 error (0, errno, _("cannot get current directory"));
446 return NULL;
449 if (S_ISDIR (file_stat->st_mode))
450 /* FILE is a directory, so just chdir there directly. */
452 last_stat = *file_stat;
453 if (chdir (file) < 0)
455 error (0, errno, _("cannot change to directory %s"), quote (file));
456 return NULL;
459 else
460 /* FILE is some other kind of file; use its directory. */
462 char *xdir = dir_name (file);
463 char *dir;
464 ASSIGN_STRDUPA (dir, xdir);
465 free (xdir);
467 if (chdir (dir) < 0)
469 error (0, errno, _("cannot change to directory %s"), quote (dir));
470 return NULL;
473 if (stat (".", &last_stat) < 0)
475 error (0, errno, _("cannot stat current directory (now %s)"),
476 quote (dir));
477 goto done;
481 /* Now walk up FILE's parents until we find another file system or /,
482 chdiring as we go. LAST_STAT holds stat information for the last place
483 we visited. */
484 for (;;)
486 struct stat st;
487 if (stat ("..", &st) < 0)
489 error (0, errno, _("cannot stat %s"), quote (".."));
490 goto done;
492 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
493 /* cwd is the mount point. */
494 break;
495 if (chdir ("..") < 0)
497 error (0, errno, _("cannot change to directory %s"), quote (".."));
498 goto done;
500 last_stat = st;
503 /* Finally reached a mount point, see what it's called. */
504 mp = xgetcwd ();
506 done:
507 /* Restore the original cwd. */
509 int save_errno = errno;
510 if (restore_cwd (&cwd) != 0)
511 error (EXIT_FAILURE, errno,
512 _("failed to return to initial working directory"));
513 free_cwd (&cwd);
514 errno = save_errno;
517 return mp;
520 /* If DISK corresponds to a mount point, show its usage
521 and return true. Otherwise, return false. */
522 static bool
523 show_disk (char const *disk)
525 struct mount_entry const *me;
526 struct mount_entry const *best_match = NULL;
528 for (me = mount_list; me; me = me->me_next)
529 if (STREQ (disk, me->me_devname))
530 best_match = me;
532 if (best_match)
534 show_dev (best_match->me_devname, best_match->me_mountdir,
535 best_match->me_type, best_match->me_dummy,
536 best_match->me_remote);
537 return true;
540 return false;
543 /* Figure out which device file or directory POINT is mounted on
544 and show its disk usage.
545 STATP must be the result of `stat (POINT, STATP)'. */
546 static void
547 show_point (const char *point, const struct stat *statp)
549 struct stat disk_stats;
550 struct mount_entry *me;
551 struct mount_entry const *best_match = NULL;
553 /* If POINT is an absolute path name, see if we can find the
554 mount point without performing any extra stat calls at all. */
555 if (*point == '/')
557 /* Find the best match: prefer non-dummies, and then prefer the
558 last match if there are ties. */
560 for (me = mount_list; me; me = me->me_next)
561 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs")
562 && (!best_match || best_match->me_dummy || !me->me_dummy))
563 best_match = me;
566 /* Calculate the real absolute path for POINT, and use that to find
567 the mount point. This avoids statting unavailable mount points,
568 which can hang df. */
569 if (! best_match)
571 char *resolved = canonicalize_file_name (point);
573 if (resolved && resolved[0] == '/')
575 size_t resolved_len = strlen (resolved);
576 size_t best_match_len = 0;
578 for (me = mount_list; me; me = me->me_next)
579 if (!STREQ (me->me_type, "lofs")
580 && (!best_match || best_match->me_dummy || !me->me_dummy))
582 size_t len = strlen (me->me_mountdir);
583 if (best_match_len <= len && len <= resolved_len
584 && (len == 1 /* root file system */
585 || ((len == resolved_len || resolved[len] == '/')
586 && strncmp (me->me_mountdir, resolved, len) == 0)))
588 best_match = me;
589 best_match_len = len;
594 if (resolved)
595 free (resolved);
597 if (best_match
598 && (stat (best_match->me_mountdir, &disk_stats) != 0
599 || disk_stats.st_dev != statp->st_dev))
600 best_match = NULL;
603 if (! best_match)
604 for (me = mount_list; me; me = me->me_next)
606 if (me->me_dev == (dev_t) -1)
608 if (stat (me->me_mountdir, &disk_stats) == 0)
609 me->me_dev = disk_stats.st_dev;
610 else
612 error (0, errno, "%s", quote (me->me_mountdir));
613 exit_status = EXIT_FAILURE;
614 /* So we won't try and fail repeatedly. */
615 me->me_dev = (dev_t) -2;
619 if (statp->st_dev == me->me_dev
620 && !STREQ (me->me_type, "lofs")
621 && (!best_match || best_match->me_dummy || !me->me_dummy))
623 /* Skip bogus mtab entries. */
624 if (stat (me->me_mountdir, &disk_stats) != 0
625 || disk_stats.st_dev != me->me_dev)
626 me->me_dev = (dev_t) -2;
627 else
628 best_match = me;
632 if (best_match)
633 show_dev (best_match->me_devname, best_match->me_mountdir,
634 best_match->me_type, best_match->me_dummy, best_match->me_remote);
635 else
637 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
638 print as much info as we can; methods that require the device to be
639 present will fail at a later point. */
641 /* Find the actual mount point. */
642 char *mp = find_mount_point (point, statp);
643 if (mp)
645 show_dev (0, mp, 0, false, false);
646 free (mp);
651 /* Determine what kind of node PATH is and show the disk usage
652 for it. STATP is the results of `stat' on PATH. */
654 static void
655 show_entry (const char *path, const struct stat *statp)
657 if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
658 && show_disk (path))
659 return;
661 show_point (path, statp);
664 /* Show all mounted file systems, except perhaps those that are of
665 an unselected type or are empty. */
667 static void
668 show_all_entries (void)
670 struct mount_entry *me;
672 for (me = mount_list; me; me = me->me_next)
673 show_dev (me->me_devname, me->me_mountdir, me->me_type,
674 me->me_dummy, me->me_remote);
677 /* Add FSTYPE to the list of file system types to display. */
679 static void
680 add_fs_type (const char *fstype)
682 struct fs_type_list *fsp;
684 fsp = xmalloc (sizeof *fsp);
685 fsp->fs_name = (char *) fstype;
686 fsp->fs_next = fs_select_list;
687 fs_select_list = fsp;
690 /* Add FSTYPE to the list of file system types to be omitted. */
692 static void
693 add_excluded_fs_type (const char *fstype)
695 struct fs_type_list *fsp;
697 fsp = xmalloc (sizeof *fsp);
698 fsp->fs_name = (char *) fstype;
699 fsp->fs_next = fs_exclude_list;
700 fs_exclude_list = fsp;
703 void
704 usage (int status)
706 if (status != EXIT_SUCCESS)
707 fprintf (stderr, _("Try `%s --help' for more information.\n"),
708 program_name);
709 else
711 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
712 fputs (_("\
713 Show information about the file system on which each FILE resides,\n\
714 or all file systems by default.\n\
716 "), stdout);
717 fputs (_("\
718 Mandatory arguments to long options are mandatory for short options too.\n\
719 "), stdout);
720 fputs (_("\
721 -a, --all include file systems having 0 blocks\n\
722 -B, --block-size=SIZE use SIZE-byte blocks\n\
723 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
724 -H, --si likewise, but use powers of 1000 not 1024\n\
725 "), stdout);
726 fputs (_("\
727 -i, --inodes list inode information instead of block usage\n\
728 -k like --block-size=1K\n\
729 -l, --local limit listing to local file systems\n\
730 --no-sync do not invoke sync before getting usage info (default)\n\
731 "), stdout);
732 fputs (_("\
733 -P, --portability use the POSIX output format\n\
734 --sync invoke sync before getting usage info\n\
735 -t, --type=TYPE limit listing to file systems of type TYPE\n\
736 -T, --print-type print file system type\n\
737 -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\
738 -v (ignored)\n\
739 "), stdout);
740 fputs (HELP_OPTION_DESCRIPTION, stdout);
741 fputs (VERSION_OPTION_DESCRIPTION, stdout);
742 fputs (_("\n\
743 SIZE may be (or may be an integer optionally followed by) one of following:\n\
744 kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
745 "), stdout);
746 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
748 exit (status);
752 main (int argc, char **argv)
754 int c;
755 struct stat *stats IF_LINT (= 0);
756 int n_valid_args = 0;
758 initialize_main (&argc, &argv);
759 program_name = argv[0];
760 setlocale (LC_ALL, "");
761 bindtextdomain (PACKAGE, LOCALEDIR);
762 textdomain (PACKAGE);
764 atexit (close_stdout);
766 fs_select_list = NULL;
767 fs_exclude_list = NULL;
768 inode_format = false;
769 show_all_fs = false;
770 show_listed_fs = false;
772 human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false,
773 &output_block_size);
775 print_type = false;
776 posix_format = false;
777 exit_status = EXIT_SUCCESS;
779 while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
780 != -1)
782 switch (c)
784 case 'a':
785 show_all_fs = true;
786 break;
787 case 'B':
788 human_output_opts = human_options (optarg, true, &output_block_size);
789 break;
790 case 'i':
791 inode_format = true;
792 break;
793 case 'h':
794 human_output_opts = human_autoscale | human_SI | human_base_1024;
795 output_block_size = 1;
796 break;
797 case 'H':
798 human_output_opts = human_autoscale | human_SI;
799 output_block_size = 1;
800 break;
801 case 'k':
802 human_output_opts = 0;
803 output_block_size = 1024;
804 break;
805 case 'l':
806 show_local_fs = true;
807 break;
808 case 'm': /* obsolescent */
809 human_output_opts = 0;
810 output_block_size = 1024 * 1024;
811 break;
812 case 'T':
813 print_type = true;
814 break;
815 case 'P':
816 posix_format = true;
817 break;
818 case SYNC_OPTION:
819 require_sync = true;
820 break;
821 case NO_SYNC_OPTION:
822 require_sync = false;
823 break;
825 case 'F':
826 /* Accept -F as a synonym for -t for compatibility with Solaris. */
827 case 't':
828 add_fs_type (optarg);
829 break;
831 case 'v': /* For SysV compatibility. */
832 /* ignore */
833 break;
834 case 'x':
835 add_excluded_fs_type (optarg);
836 break;
838 case_GETOPT_HELP_CHAR;
839 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
841 default:
842 usage (EXIT_FAILURE);
846 /* Fail if the same file system type was both selected and excluded. */
848 bool match = false;
849 struct fs_type_list *fs_incl;
850 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
852 struct fs_type_list *fs_excl;
853 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
855 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
857 error (0, 0,
858 _("file system type %s both selected and excluded"),
859 quote (fs_incl->fs_name));
860 match = true;
861 break;
865 if (match)
866 exit (EXIT_FAILURE);
869 if (optind < argc)
871 int i;
873 /* stat all the given entries to make sure they get automounted,
874 if necessary, before reading the file system table. */
875 stats = xnmalloc (argc - optind, sizeof *stats);
876 for (i = optind; i < argc; ++i)
878 if (stat (argv[i], &stats[i - optind]))
880 error (0, errno, "%s", quote (argv[i]));
881 exit_status = EXIT_FAILURE;
882 argv[i] = NULL;
884 else
886 ++n_valid_args;
891 mount_list =
892 read_file_system_list ((fs_select_list != NULL
893 || fs_exclude_list != NULL
894 || print_type
895 || show_local_fs));
897 if (mount_list == NULL)
899 /* Couldn't read the table of mounted file systems.
900 Fail if df was invoked with no file name arguments;
901 Otherwise, merely give a warning and proceed. */
902 const char *warning = (optind < argc ? _("Warning: ") : "");
903 int status = (optind < argc ? 0 : EXIT_FAILURE);
904 error (status, errno,
905 _("%scannot read table of mounted file systems"), warning);
908 if (require_sync)
909 sync ();
911 if (optind < argc)
913 int i;
915 /* Display explicitly requested empty file systems. */
916 show_listed_fs = true;
918 if (n_valid_args > 0)
919 print_header ();
921 for (i = optind; i < argc; ++i)
922 if (argv[i])
923 show_entry (argv[i], &stats[i - optind]);
925 else
927 print_header ();
928 show_all_entries ();
931 exit (exit_status);