Use newer trap and tmpdir-creation framework.
[coreutils.git] / src / df.c
blobc48f2128793a54f33d189e962982441a79400ddb
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "quote.h"
37 #include "save-cwd.h"
38 #include "xgetcwd.h"
40 /* The official name of this program (e.g., no `g' prefix). */
41 #define PROGRAM_NAME "df"
43 #define AUTHORS \
44 "Torbjorn Granlund", "David MacKenzie", "Paul Eggert"
46 /* Name this program was run with. */
47 char *program_name;
49 /* If true, show inode information. */
50 static bool inode_format;
52 /* If true, show even file systems with zero size or
53 uninteresting types. */
54 static bool show_all_fs;
56 /* If true, show only local file systems. */
57 static bool show_local_fs;
59 /* If true, output data for each file system corresponding to a
60 command line argument -- even if it's a dummy (automounter) entry. */
61 static bool show_listed_fs;
63 /* Human-readable options for output. */
64 static int human_output_opts;
66 /* The units to use when printing sizes. */
67 static uintmax_t output_block_size;
69 /* If true, use the POSIX output format. */
70 static bool posix_format;
72 /* If true, invoke the `sync' system call before getting any usage data.
73 Using this option can make df very slow, especially with many or very
74 busy disks. Note that this may make a difference on some systems --
75 SunOS 4.1.3, for one. It is *not* necessary on Linux. */
76 static bool require_sync;
78 /* Desired exit status. */
79 static int exit_status;
81 /* A file system type to display. */
83 struct fs_type_list
85 char *fs_name;
86 struct fs_type_list *fs_next;
89 /* Linked list of file system types to display.
90 If `fs_select_list' is NULL, list all types.
91 This table is generated dynamically from command-line options,
92 rather than hardcoding into the program what it thinks are the
93 valid file system types; let the user specify any file system type
94 they want to, and if there are any file systems of that type, they
95 will be shown.
97 Some file system types:
98 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
100 static struct fs_type_list *fs_select_list;
102 /* Linked list of file system types to omit.
103 If the list is empty, don't exclude any types. */
105 static struct fs_type_list *fs_exclude_list;
107 /* Linked list of mounted file systems. */
108 static struct mount_entry *mount_list;
110 /* If true, print file system type as well. */
111 static bool print_type;
113 /* For long options that have no equivalent short option, use a
114 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
115 enum
117 SYNC_OPTION = CHAR_MAX + 1,
118 NO_SYNC_OPTION
121 static struct option const long_options[] =
123 {"all", no_argument, NULL, 'a'},
124 {"block-size", required_argument, NULL, 'B'},
125 {"inodes", no_argument, NULL, 'i'},
126 {"human-readable", no_argument, NULL, 'h'},
127 {"si", no_argument, NULL, 'H'},
128 {"kilobytes", no_argument, NULL, 'k'}, /* long form is obsolescent */
129 {"local", no_argument, NULL, 'l'},
130 {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */
131 {"portability", no_argument, NULL, 'P'},
132 {"print-type", no_argument, NULL, 'T'},
133 {"sync", no_argument, NULL, SYNC_OPTION},
134 {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
135 {"type", required_argument, NULL, 't'},
136 {"exclude-type", required_argument, NULL, 'x'},
137 {GETOPT_HELP_OPTION_DECL},
138 {GETOPT_VERSION_OPTION_DECL},
139 {NULL, 0, NULL, 0}
142 static void
143 print_header (void)
145 char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
147 if (print_type)
148 fputs (_("Filesystem Type"), stdout);
149 else
150 fputs (_("Filesystem "), stdout);
152 if (inode_format)
153 printf (_(" Inodes IUsed IFree IUse%%"));
154 else if (human_output_opts & human_autoscale)
156 if (human_output_opts & human_base_1024)
157 printf (_(" Size Used Avail Use%%"));
158 else
159 printf (_(" Size Used Avail Use%%"));
161 else if (posix_format)
162 printf (_(" %4s-blocks Used Available Capacity"),
163 umaxtostr (output_block_size, buf));
164 else
166 int opts = (human_suppress_point_zero
167 | human_autoscale | human_SI
168 | (human_output_opts
169 & (human_group_digits | human_base_1024 | human_B)));
171 /* Prefer the base that makes the human-readable value more exact,
172 if there is a difference. */
174 uintmax_t q1000 = output_block_size;
175 uintmax_t q1024 = output_block_size;
176 bool divisible_by_1000;
177 bool divisible_by_1024;
181 divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
182 divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
184 while (divisible_by_1000 & divisible_by_1024);
186 if (divisible_by_1000 < divisible_by_1024)
187 opts |= human_base_1024;
188 if (divisible_by_1024 < divisible_by_1000)
189 opts &= ~human_base_1024;
190 if (! (opts & human_base_1024))
191 opts |= human_B;
193 printf (_(" %4s-blocks Used Available Use%%"),
194 human_readable (output_block_size, buf, opts, 1, 1));
197 printf (_(" Mounted on\n"));
200 /* Is FSTYPE a type of file system that should be listed? */
202 static bool
203 selected_fstype (const char *fstype)
205 const struct fs_type_list *fsp;
207 if (fs_select_list == NULL || fstype == NULL)
208 return true;
209 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
210 if (STREQ (fstype, fsp->fs_name))
211 return true;
212 return false;
215 /* Is FSTYPE a type of file system that should be omitted? */
217 static bool
218 excluded_fstype (const char *fstype)
220 const struct fs_type_list *fsp;
222 if (fs_exclude_list == NULL || fstype == NULL)
223 return false;
224 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
225 if (STREQ (fstype, fsp->fs_name))
226 return true;
227 return false;
230 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
231 except:
233 - Return "-" if N is -1,
234 - If NEGATIVE is 1 then N represents a negative number,
235 expressed in two's complement. */
237 static char const *
238 df_readable (bool negative, uintmax_t n, char *buf,
239 uintmax_t input_units, uintmax_t output_units)
241 if (n == UINTMAX_MAX)
242 return "-";
243 else
245 char *p = human_readable (negative ? -n : n, buf + negative,
246 human_output_opts, input_units, output_units);
247 if (negative)
248 *--p = '-';
249 return p;
253 /* Display a space listing for the disk device with absolute file name DISK.
254 If MOUNT_POINT is non-NULL, it is the name of the root of the
255 file system on DISK.
256 If STAT_FILE is non-null, it is the name of a file within the file
257 system that the user originally asked for; this provides better
258 diagnostics, and sometimes it provides better results on networked
259 file systems that give different free-space results depending on
260 where in the file system you probe.
261 If FSTYPE is non-NULL, it is the type of the file system on DISK.
262 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
263 not be able to produce statistics in this case.
264 ME_DUMMY and ME_REMOTE are the mount entry flags. */
266 static void
267 show_dev (char const *disk, char const *mount_point,
268 char const *stat_file, char const *fstype,
269 bool me_dummy, bool me_remote)
271 struct fs_usage fsu;
272 char buf[3][LONGEST_HUMAN_READABLE + 2];
273 int width;
274 int use_width;
275 uintmax_t input_units;
276 uintmax_t output_units;
277 uintmax_t total;
278 uintmax_t available;
279 bool negate_available;
280 uintmax_t available_to_root;
281 uintmax_t used;
282 bool negate_used;
283 double pct = -1;
285 if (me_remote & show_local_fs)
286 return;
288 if (me_dummy & !show_all_fs & !show_listed_fs)
289 return;
291 if (!selected_fstype (fstype) || excluded_fstype (fstype))
292 return;
294 /* If MOUNT_POINT is NULL, then the file system is not mounted, and this
295 program reports on the file system that the special file is on.
296 It would be better to report on the unmounted file system,
297 but statfs doesn't do that on most systems. */
298 if (!stat_file)
299 stat_file = mount_point ? mount_point : disk;
301 if (get_fs_usage (stat_file, disk, &fsu))
303 error (0, errno, "%s", quote (stat_file));
304 exit_status = EXIT_FAILURE;
305 return;
308 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
309 return;
311 if (! disk)
312 disk = "-"; /* unknown */
313 if (! fstype)
314 fstype = "-"; /* unknown */
316 /* df.c reserved 5 positions for fstype,
317 but that does not suffice for type iso9660 */
318 if (print_type)
320 size_t disk_name_len = strlen (disk);
321 size_t fstype_len = strlen (fstype);
322 if (disk_name_len + fstype_len < 18)
323 printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype);
324 else if (!posix_format)
325 printf ("%s\n%18s ", disk, fstype);
326 else
327 printf ("%s %s", disk, fstype);
329 else
331 if (strlen (disk) > 20 && !posix_format)
332 printf ("%s\n%20s", disk, "");
333 else
334 printf ("%-20s", disk);
337 if (inode_format)
339 width = 7;
340 use_width = 5;
341 input_units = output_units = 1;
342 total = fsu.fsu_files;
343 available = fsu.fsu_ffree;
344 negate_available = false;
345 available_to_root = available;
347 else
349 width = (human_output_opts & human_autoscale
350 ? 5 + ! (human_output_opts & human_base_1024)
351 : 9);
352 use_width = ((posix_format
353 && ! (human_output_opts & human_autoscale))
354 ? 8 : 4);
355 input_units = fsu.fsu_blocksize;
356 output_units = output_block_size;
357 total = fsu.fsu_blocks;
358 available = fsu.fsu_bavail;
359 negate_available = fsu.fsu_bavail_top_bit_set;
360 available_to_root = fsu.fsu_bfree;
363 used = -1;
364 negate_used = false;
365 if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX)
367 used = total - available_to_root;
368 if (total < available_to_root)
370 negate_used = true;
371 used = - used;
375 printf (" %*s %*s %*s ",
376 width, df_readable (false, total,
377 buf[0], input_units, output_units),
378 width, df_readable (negate_used, used,
379 buf[1], input_units, output_units),
380 width, df_readable (negate_available, available,
381 buf[2], input_units, output_units));
383 if (used == UINTMAX_MAX || available == UINTMAX_MAX)
385 else if (!negate_used
386 && used <= TYPE_MAXIMUM (uintmax_t) / 100
387 && used + available != 0
388 && (used + available < used) == negate_available)
390 uintmax_t u100 = used * 100;
391 uintmax_t nonroot_total = used + available;
392 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
394 else
396 /* The calculation cannot be done easily with integer
397 arithmetic. Fall back on floating point. This can suffer
398 from minor rounding errors, but doing it exactly requires
399 multiple precision arithmetic, and it's not worth the
400 aggravation. */
401 double u = negate_used ? - (double) - used : used;
402 double a = negate_available ? - (double) - available : available;
403 double nonroot_total = u + a;
404 if (nonroot_total)
406 long int lipct = pct = u * 100 / nonroot_total;
407 double ipct = lipct;
409 /* Like `pct = ceil (dpct);', but avoid ceil so that
410 the math library needn't be linked. */
411 if (ipct - 1 < pct && pct <= ipct + 1)
412 pct = ipct + (ipct < pct);
416 if (0 <= pct)
417 printf ("%*.0f%%", use_width - 1, pct);
418 else
419 printf ("%*s", use_width, "- ");
421 if (mount_point)
423 #ifdef HIDE_AUTOMOUNT_PREFIX
424 /* Don't print the first directory name in MOUNT_POINT if it's an
425 artifact of an automounter. This is a bit too aggressive to be
426 the default. */
427 if (strncmp ("/auto/", mount_point, 6) == 0)
428 mount_point += 5;
429 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
430 mount_point += 8;
431 #endif
432 printf (" %s", mount_point);
434 putchar ('\n');
437 /* Return the root mountpoint of the file system on which FILE exists, in
438 malloced storage. FILE_STAT should be the result of stating FILE.
439 Give a diagnostic and return NULL if unable to determine the mount point.
440 Exit if unable to restore current working directory. */
441 static char *
442 find_mount_point (const char *file, const struct stat *file_stat)
444 struct saved_cwd cwd;
445 struct stat last_stat;
446 char *mp = NULL; /* The malloced mount point. */
448 if (save_cwd (&cwd) != 0)
450 error (0, errno, _("cannot get current directory"));
451 return NULL;
454 if (S_ISDIR (file_stat->st_mode))
455 /* FILE is a directory, so just chdir there directly. */
457 last_stat = *file_stat;
458 if (chdir (file) < 0)
460 error (0, errno, _("cannot change to directory %s"), quote (file));
461 return NULL;
464 else
465 /* FILE is some other kind of file; use its directory. */
467 char *xdir = dir_name (file);
468 char *dir;
469 ASSIGN_STRDUPA (dir, xdir);
470 free (xdir);
472 if (chdir (dir) < 0)
474 error (0, errno, _("cannot change to directory %s"), quote (dir));
475 return NULL;
478 if (stat (".", &last_stat) < 0)
480 error (0, errno, _("cannot stat current directory (now %s)"),
481 quote (dir));
482 goto done;
486 /* Now walk up FILE's parents until we find another file system or /,
487 chdiring as we go. LAST_STAT holds stat information for the last place
488 we visited. */
489 for (;;)
491 struct stat st;
492 if (stat ("..", &st) < 0)
494 error (0, errno, _("cannot stat %s"), quote (".."));
495 goto done;
497 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
498 /* cwd is the mount point. */
499 break;
500 if (chdir ("..") < 0)
502 error (0, errno, _("cannot change to directory %s"), quote (".."));
503 goto done;
505 last_stat = st;
508 /* Finally reached a mount point, see what it's called. */
509 mp = xgetcwd ();
511 done:
512 /* Restore the original cwd. */
514 int save_errno = errno;
515 if (restore_cwd (&cwd) != 0)
516 error (EXIT_FAILURE, errno,
517 _("failed to return to initial working directory"));
518 free_cwd (&cwd);
519 errno = save_errno;
522 return mp;
525 /* If DISK corresponds to a mount point, show its usage
526 and return true. Otherwise, return false. */
527 static bool
528 show_disk (char const *disk)
530 struct mount_entry const *me;
531 struct mount_entry const *best_match = NULL;
533 for (me = mount_list; me; me = me->me_next)
534 if (STREQ (disk, me->me_devname))
535 best_match = me;
537 if (best_match)
539 show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
540 best_match->me_type, best_match->me_dummy,
541 best_match->me_remote);
542 return true;
545 return false;
548 /* Figure out which device file or directory POINT is mounted on
549 and show its disk usage.
550 STATP must be the result of `stat (POINT, STATP)'. */
551 static void
552 show_point (const char *point, const struct stat *statp)
554 struct stat disk_stats;
555 struct mount_entry *me;
556 struct mount_entry const *best_match = NULL;
558 /* If POINT is an absolute file name, see if we can find the
559 mount point without performing any extra stat calls at all. */
560 if (*point == '/')
562 /* Find the best match: prefer non-dummies, and then prefer the
563 last match if there are ties. */
565 for (me = mount_list; me; me = me->me_next)
566 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs")
567 && (!best_match || best_match->me_dummy || !me->me_dummy))
568 best_match = me;
571 /* Calculate the real absolute file name for POINT, and use that to find
572 the mount point. This avoids statting unavailable mount points,
573 which can hang df. */
574 if (! best_match)
576 char *resolved = canonicalize_file_name (point);
578 if (resolved && resolved[0] == '/')
580 size_t resolved_len = strlen (resolved);
581 size_t best_match_len = 0;
583 for (me = mount_list; me; me = me->me_next)
584 if (!STREQ (me->me_type, "lofs")
585 && (!best_match || best_match->me_dummy || !me->me_dummy))
587 size_t len = strlen (me->me_mountdir);
588 if (best_match_len <= len && len <= resolved_len
589 && (len == 1 /* root file system */
590 || ((len == resolved_len || resolved[len] == '/')
591 && strncmp (me->me_mountdir, resolved, len) == 0)))
593 best_match = me;
594 best_match_len = len;
599 free (resolved);
601 if (best_match
602 && (stat (best_match->me_mountdir, &disk_stats) != 0
603 || disk_stats.st_dev != statp->st_dev))
604 best_match = NULL;
607 if (! best_match)
608 for (me = mount_list; me; me = me->me_next)
610 if (me->me_dev == (dev_t) -1)
612 if (stat (me->me_mountdir, &disk_stats) == 0)
613 me->me_dev = disk_stats.st_dev;
614 else
616 error (0, errno, "%s", quote (me->me_mountdir));
617 exit_status = EXIT_FAILURE;
618 /* So we won't try and fail repeatedly. */
619 me->me_dev = (dev_t) -2;
623 if (statp->st_dev == me->me_dev
624 && !STREQ (me->me_type, "lofs")
625 && (!best_match || best_match->me_dummy || !me->me_dummy))
627 /* Skip bogus mtab entries. */
628 if (stat (me->me_mountdir, &disk_stats) != 0
629 || disk_stats.st_dev != me->me_dev)
630 me->me_dev = (dev_t) -2;
631 else
632 best_match = me;
636 if (best_match)
637 show_dev (best_match->me_devname, best_match->me_mountdir, point,
638 best_match->me_type, best_match->me_dummy, best_match->me_remote);
639 else
641 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
642 print as much info as we can; methods that require the device to be
643 present will fail at a later point. */
645 /* Find the actual mount point. */
646 char *mp = find_mount_point (point, statp);
647 if (mp)
649 show_dev (NULL, mp, NULL, NULL, false, false);
650 free (mp);
655 /* Determine what kind of node NAME is and show the disk usage
656 for it. STATP is the results of `stat' on NAME. */
658 static void
659 show_entry (char const *name, struct stat const *statp)
661 if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
662 && show_disk (name))
663 return;
665 show_point (name, statp);
668 /* Show all mounted file systems, except perhaps those that are of
669 an unselected type or are empty. */
671 static void
672 show_all_entries (void)
674 struct mount_entry *me;
676 for (me = mount_list; me; me = me->me_next)
677 show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
678 me->me_dummy, me->me_remote);
681 /* Add FSTYPE to the list of file system types to display. */
683 static void
684 add_fs_type (const char *fstype)
686 struct fs_type_list *fsp;
688 fsp = xmalloc (sizeof *fsp);
689 fsp->fs_name = (char *) fstype;
690 fsp->fs_next = fs_select_list;
691 fs_select_list = fsp;
694 /* Add FSTYPE to the list of file system types to be omitted. */
696 static void
697 add_excluded_fs_type (const char *fstype)
699 struct fs_type_list *fsp;
701 fsp = xmalloc (sizeof *fsp);
702 fsp->fs_name = (char *) fstype;
703 fsp->fs_next = fs_exclude_list;
704 fs_exclude_list = fsp;
707 void
708 usage (int status)
710 if (status != EXIT_SUCCESS)
711 fprintf (stderr, _("Try `%s --help' for more information.\n"),
712 program_name);
713 else
715 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
716 fputs (_("\
717 Show information about the file system on which each FILE resides,\n\
718 or all file systems by default.\n\
720 "), stdout);
721 fputs (_("\
722 Mandatory arguments to long options are mandatory for short options too.\n\
723 "), stdout);
724 fputs (_("\
725 -a, --all include file systems having 0 blocks\n\
726 -B, --block-size=SIZE use SIZE-byte blocks\n\
727 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
728 -H, --si likewise, but use powers of 1000 not 1024\n\
729 "), stdout);
730 fputs (_("\
731 -i, --inodes list inode information instead of block usage\n\
732 -k like --block-size=1K\n\
733 -l, --local limit listing to local file systems\n\
734 --no-sync do not invoke sync before getting usage info (default)\n\
735 "), stdout);
736 fputs (_("\
737 -P, --portability use the POSIX output format\n\
738 --sync invoke sync before getting usage info\n\
739 -t, --type=TYPE limit listing to file systems of type TYPE\n\
740 -T, --print-type print file system type\n\
741 -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\
742 -v (ignored)\n\
743 "), stdout);
744 fputs (HELP_OPTION_DESCRIPTION, stdout);
745 fputs (VERSION_OPTION_DESCRIPTION, stdout);
746 fputs (_("\n\
747 SIZE may be (or may be an integer optionally followed by) one of following:\n\
748 kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
749 "), stdout);
750 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
752 exit (status);
756 main (int argc, char **argv)
758 int c;
759 struct stat *stats IF_LINT (= 0);
760 int n_valid_args = 0;
762 initialize_main (&argc, &argv);
763 program_name = argv[0];
764 setlocale (LC_ALL, "");
765 bindtextdomain (PACKAGE, LOCALEDIR);
766 textdomain (PACKAGE);
768 atexit (close_stdout);
770 fs_select_list = NULL;
771 fs_exclude_list = NULL;
772 inode_format = false;
773 show_all_fs = false;
774 show_listed_fs = false;
776 human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false,
777 &output_block_size);
779 print_type = false;
780 posix_format = false;
781 exit_status = EXIT_SUCCESS;
783 while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
784 != -1)
786 switch (c)
788 case 'a':
789 show_all_fs = true;
790 break;
791 case 'B':
792 human_output_opts = human_options (optarg, true, &output_block_size);
793 break;
794 case 'i':
795 inode_format = true;
796 break;
797 case 'h':
798 human_output_opts = human_autoscale | human_SI | human_base_1024;
799 output_block_size = 1;
800 break;
801 case 'H':
802 human_output_opts = human_autoscale | human_SI;
803 output_block_size = 1;
804 break;
805 case 'k':
806 human_output_opts = 0;
807 output_block_size = 1024;
808 break;
809 case 'l':
810 show_local_fs = true;
811 break;
812 case 'm': /* obsolescent */
813 human_output_opts = 0;
814 output_block_size = 1024 * 1024;
815 break;
816 case 'T':
817 print_type = true;
818 break;
819 case 'P':
820 posix_format = true;
821 break;
822 case SYNC_OPTION:
823 require_sync = true;
824 break;
825 case NO_SYNC_OPTION:
826 require_sync = false;
827 break;
829 case 'F':
830 /* Accept -F as a synonym for -t for compatibility with Solaris. */
831 case 't':
832 add_fs_type (optarg);
833 break;
835 case 'v': /* For SysV compatibility. */
836 /* ignore */
837 break;
838 case 'x':
839 add_excluded_fs_type (optarg);
840 break;
842 case_GETOPT_HELP_CHAR;
843 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
845 default:
846 usage (EXIT_FAILURE);
850 /* Fail if the same file system type was both selected and excluded. */
852 bool match = false;
853 struct fs_type_list *fs_incl;
854 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
856 struct fs_type_list *fs_excl;
857 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
859 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
861 error (0, 0,
862 _("file system type %s both selected and excluded"),
863 quote (fs_incl->fs_name));
864 match = true;
865 break;
869 if (match)
870 exit (EXIT_FAILURE);
873 if (optind < argc)
875 int i;
877 /* stat all the given entries to make sure they get automounted,
878 if necessary, before reading the file system table. */
879 stats = xnmalloc (argc - optind, sizeof *stats);
880 for (i = optind; i < argc; ++i)
882 if (stat (argv[i], &stats[i - optind]))
884 error (0, errno, "%s", quote (argv[i]));
885 exit_status = EXIT_FAILURE;
886 argv[i] = NULL;
888 else
890 ++n_valid_args;
895 mount_list =
896 read_file_system_list ((fs_select_list != NULL
897 || fs_exclude_list != NULL
898 || print_type
899 || show_local_fs));
901 if (mount_list == NULL)
903 /* Couldn't read the table of mounted file systems.
904 Fail if df was invoked with no file name arguments;
905 Otherwise, merely give a warning and proceed. */
906 const char *warning = (optind < argc ? _("Warning: ") : "");
907 int status = (optind < argc ? 0 : EXIT_FAILURE);
908 error (status, errno,
909 _("%scannot read table of mounted file systems"), warning);
912 if (require_sync)
913 sync ();
915 if (optind < argc)
917 int i;
919 /* Display explicitly requested empty file systems. */
920 show_listed_fs = true;
922 if (n_valid_args > 0)
923 print_header ();
925 for (i = optind; i < argc; ++i)
926 if (argv[i])
927 show_entry (argv[i], &stats[i - optind]);
929 else
931 print_header ();
932 show_all_entries ();
935 exit (exit_status);