.
[coreutils.git] / src / df.c
blobe348f32536af66d3e2bc64739d9ae97b1b4e3407
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 nonzero, show inode information. */
51 static int inode_format;
53 /* If nonzero, show even filesystems with zero size or
54 uninteresting types. */
55 static int show_all_fs;
57 /* If nonzero, show only local filesystems. */
58 static int show_local_fs;
60 /* If nonzero, output data for each filesystem corresponding to a
61 command line argument -- even if it's a dummy (automounter) entry. */
62 static int 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 nonzero, use the POSIX output format. */
71 static int posix_format;
73 /* If nonzero, 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 SunOs4.1.3, for one. It is *not* necessary on Linux. */
77 static int require_sync = 0;
79 /* Nonzero if errors have occurred. */
80 static int exit_status;
82 /* A filesystem type to display. */
84 struct fs_type_list
86 char *fs_name;
87 struct fs_type_list *fs_next;
90 /* Linked list of filesystem 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 filesystem types; let the user specify any filesystem type
95 they want to, and if there are any filesystems of that type, they
96 will be shown.
98 Some filesystem 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 filesystem 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 filesystems. */
109 static struct mount_entry *mount_list;
111 /* If nonzero, print filesystem type as well. */
112 static int 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 /* If FSTYPE is a type of filesystem that should be listed,
202 return nonzero, else zero. */
204 static int
205 selected_fstype (const char *fstype)
207 const struct fs_type_list *fsp;
209 if (fs_select_list == NULL || fstype == NULL)
210 return 1;
211 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
212 if (STREQ (fstype, fsp->fs_name))
213 return 1;
214 return 0;
217 /* If FSTYPE is a type of filesystem that should be omitted,
218 return nonzero, else zero. */
220 static int
221 excluded_fstype (const char *fstype)
223 const struct fs_type_list *fsp;
225 if (fs_exclude_list == NULL || fstype == NULL)
226 return 0;
227 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
228 if (STREQ (fstype, fsp->fs_name))
229 return 1;
230 return 0;
233 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
234 except:
236 - Return "-" if N is -1,
237 - If NEGATIVE is 1 then N represents a negative number,
238 expressed in two's complement. */
240 static char const *
241 df_readable (int negative, uintmax_t n, char *buf,
242 uintmax_t input_units, uintmax_t output_units)
244 if (n == -1)
245 return "-";
246 else
248 char *p = human_readable (negative ? -n : n, buf + negative,
249 human_output_opts, input_units, output_units);
250 if (negative)
251 *--p = '-';
252 return p;
256 /* Display a space listing for the disk device with absolute path DISK.
257 If MOUNT_POINT is non-NULL, it is the path of the root of the
258 filesystem on DISK.
259 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
260 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
261 not be able to produce statistics in this case.
262 ME_DUMMY and ME_REMOTE are the mount entry flags. */
264 static void
265 show_dev (const char *disk, const char *mount_point, const char *fstype,
266 int me_dummy, int me_remote)
268 struct fs_usage fsu;
269 const char *stat_file;
270 char buf[3][LONGEST_HUMAN_READABLE + 2];
271 int width;
272 int use_width;
273 uintmax_t input_units;
274 uintmax_t output_units;
275 uintmax_t total;
276 uintmax_t available;
277 int negate_available;
278 uintmax_t available_to_root;
279 uintmax_t used;
280 int negate_used;
281 double pct = -1;
283 if (me_remote && show_local_fs)
284 return;
286 if (me_dummy && show_all_fs == 0 && !show_listed_fs)
287 return;
289 if (!selected_fstype (fstype) || excluded_fstype (fstype))
290 return;
292 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
293 program reports on the filesystem that the special file is on.
294 It would be better to report on the unmounted filesystem,
295 but statfs doesn't do that on most systems. */
296 stat_file = mount_point ? mount_point : disk;
298 if (get_fs_usage (stat_file, disk, &fsu))
300 error (0, errno, "%s", quote (stat_file));
301 exit_status = 1;
302 return;
305 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
306 return;
308 if (! disk)
309 disk = "-"; /* unknown */
310 if (! fstype)
311 fstype = "-"; /* unknown */
313 /* df.c reserved 5 positions for fstype,
314 but that does not suffice for type iso9660 */
315 if (print_type)
317 int disk_name_len = (int) strlen (disk);
318 int fstype_len = (int) strlen (fstype);
319 if (disk_name_len + fstype_len + 2 < 20)
320 printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
321 else if (!posix_format)
322 printf ("%s\n%18s ", disk, fstype);
323 else
324 printf ("%s %s", disk, fstype);
326 else
328 if ((int) strlen (disk) > 20 && !posix_format)
329 printf ("%s\n%20s", disk, "");
330 else
331 printf ("%-20s", disk);
334 if (inode_format)
336 width = 7;
337 use_width = 5;
338 input_units = output_units = 1;
339 total = fsu.fsu_files;
340 available = fsu.fsu_ffree;
341 negate_available = 0;
342 available_to_root = available;
344 else
346 width = (human_output_opts & human_autoscale
347 ? 5 + ! (human_output_opts & human_base_1024)
348 : 9);
349 use_width = ((posix_format
350 && ! (human_output_opts & human_autoscale))
351 ? 8 : 4);
352 input_units = fsu.fsu_blocksize;
353 output_units = output_block_size;
354 total = fsu.fsu_blocks;
355 available = fsu.fsu_bavail;
356 negate_available = fsu.fsu_bavail_top_bit_set;
357 available_to_root = fsu.fsu_bfree;
360 used = -1;
361 negate_used = 0;
362 if (total != -1 && available_to_root != -1)
364 used = total - available_to_root;
365 if (total < available_to_root)
367 negate_used = 1;
368 used = - used;
372 printf (" %*s %*s %*s ",
373 width, df_readable (0, total,
374 buf[0], input_units, output_units),
375 width, df_readable (negate_used, used,
376 buf[1], input_units, output_units),
377 width, df_readable (negate_available, available,
378 buf[2], input_units, output_units));
380 if (used == -1 || available == -1)
382 else if (!negate_used
383 && used <= TYPE_MAXIMUM (uintmax_t) / 100
384 && used + available != 0
385 && (used + available < used) == negate_available)
387 uintmax_t u100 = used * 100;
388 uintmax_t nonroot_total = used + available;
389 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
391 else
393 /* The calculation cannot be done easily with integer
394 arithmetic. Fall back on floating point. This can suffer
395 from minor rounding errors, but doing it exactly requires
396 multiple precision arithmetic, and it's not worth the
397 aggravation. */
398 double u = negate_used ? - (double) - used : used;
399 double a = negate_available ? - (double) - available : available;
400 double nonroot_total = u + a;
401 if (nonroot_total)
403 double ipct;
404 pct = u * 100 / nonroot_total;
405 ipct = (long) pct;
407 /* Like `pct = ceil (dpct);', but avoid ceil so that
408 the math library needn't be linked. */
409 if (ipct - 1 < pct && pct <= ipct + 1)
410 pct = ipct + (ipct < pct);
414 if (0 <= pct)
415 printf ("%*.0f%%", use_width - 1, pct);
416 else
417 printf ("%*s", use_width, "- ");
419 if (mount_point)
421 #ifdef HIDE_AUTOMOUNT_PREFIX
422 /* Don't print the first directory name in MOUNT_POINT if it's an
423 artifact of an automounter. This is a bit too aggressive to be
424 the default. */
425 if (strncmp ("/auto/", mount_point, 6) == 0)
426 mount_point += 5;
427 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
428 mount_point += 8;
429 #endif
430 printf (" %s", mount_point);
432 putchar ('\n');
435 /* Return the root mountpoint of the filesystem on which FILE exists, in
436 malloced storage. FILE_STAT should be the result of stating FILE.
437 Give a diagnostic and return NULL if unable to determine the mount point.
438 Exit if unable to restore current working directory. */
439 static char *
440 find_mount_point (const char *file, const struct stat *file_stat)
442 struct saved_cwd cwd;
443 struct stat last_stat;
444 char *mp = 0; /* The malloced mount point path. */
446 if (save_cwd (&cwd))
448 error (0, errno, _("cannot get current directory"));
449 return NULL;
452 if (S_ISDIR (file_stat->st_mode))
453 /* FILE is a directory, so just chdir there directly. */
455 last_stat = *file_stat;
456 if (chdir (file) < 0)
458 error (0, errno, _("cannot change to directory %s"), quote (file));
459 return NULL;
462 else
463 /* FILE is some other kind of file; use its directory. */
465 char *xdir = dir_name (file);
466 char *dir;
467 ASSIGN_STRDUPA (dir, xdir);
468 free (xdir);
470 if (chdir (dir) < 0)
472 error (0, errno, _("cannot change to directory %s"), quote (dir));
473 return NULL;
476 if (stat (".", &last_stat) < 0)
478 error (0, errno, _("cannot stat current directory (now %s)"),
479 quote (dir));
480 goto done;
484 /* Now walk up FILE's parents until we find another filesystem or /,
485 chdiring as we go. LAST_STAT holds stat information for the last place
486 we visited. */
487 for (;;)
489 struct stat st;
490 if (stat ("..", &st) < 0)
492 error (0, errno, _("cannot stat %s"), quote (".."));
493 goto done;
495 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
496 /* cwd is the mount point. */
497 break;
498 if (chdir ("..") < 0)
500 error (0, errno, _("cannot change to directory %s"), quote (".."));
501 goto done;
503 last_stat = st;
506 /* Finally reached a mount point, see what it's called. */
507 mp = xgetcwd ();
509 done:
510 /* Restore the original cwd. */
512 int save_errno = errno;
513 if (restore_cwd (&cwd))
514 error (EXIT_FAILURE, errno,
515 _("failed to return to initial working directory"));
516 free_cwd (&cwd);
517 errno = save_errno;
520 return mp;
523 /* If DISK corresponds to a mount point, show its usage
524 and return nonzero. Otherwise, return zero.
525 STATP must be the result of `stat (DISK, STATP)'. */
526 static int
527 show_disk (const char *disk, const struct stat *statp)
529 struct mount_entry *me;
531 for (me = mount_list; me; me = me->me_next)
532 if (STREQ (disk, me->me_devname))
534 show_dev (me->me_devname, me->me_mountdir, me->me_type,
535 me->me_dummy, me->me_remote);
536 return 1;
539 return 0;
542 /* Figure out which device file or directory POINT is mounted on
543 and show its disk usage.
544 STATP must be the result of `stat (POINT, STATP)'. */
545 static void
546 show_point (const char *point, const struct stat *statp)
548 struct stat disk_stats;
549 struct mount_entry *me;
550 struct mount_entry *matching_dummy = NULL;
552 /* If POINT is an absolute path name, see if we can find the
553 mount point without performing any extra stat calls at all. */
554 if (*point == '/')
556 for (me = mount_list; me; me = me->me_next)
558 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs"))
560 /* Prefer non-dummy entries. */
561 if (! me->me_dummy)
562 goto show_me;
563 matching_dummy = me;
567 if (matching_dummy)
568 goto show_matching_dummy;
571 /* Calculate the real absolute path for POINT, and use that to find
572 the mount point. This avoids statting unavailable mount points,
573 which can hang df. */
575 char *resolved = canonicalize_file_name (point);
576 ssize_t resolved_len = resolved ? strlen (resolved) : -1;
577 struct mount_entry *best_match = NULL;
579 if (1 <= resolved_len && resolved[0] == '/')
581 size_t best_match_len = 0;
583 for (me = mount_list; me; me = me->me_next)
584 if (! me->me_dummy)
586 size_t len = strlen (me->me_mountdir);
587 if (best_match_len < len && len <= resolved_len
588 && (len == 1 /* root file system */
589 || ((len == resolved_len || resolved[len] == '/')
590 && strncmp (me->me_mountdir, resolved, len) == 0)))
592 best_match = me;
593 best_match_len = len;
598 if (resolved)
599 free (resolved);
601 if (best_match && !STREQ (best_match->me_type, "lofs")
602 && stat (best_match->me_mountdir, &disk_stats) == 0
603 && disk_stats.st_dev == statp->st_dev)
605 me = best_match;
606 goto show_me;
610 for (me = mount_list; me; me = me->me_next)
612 if (me->me_dev == (dev_t) -1)
614 if (stat (me->me_mountdir, &disk_stats) == 0)
615 me->me_dev = disk_stats.st_dev;
616 else
618 error (0, errno, "%s", quote (me->me_mountdir));
619 exit_status = 1;
620 /* So we won't try and fail repeatedly. */
621 me->me_dev = (dev_t) -2;
625 if (statp->st_dev == me->me_dev)
627 /* Skip bogus mtab entries. */
628 if (stat (me->me_mountdir, &disk_stats) != 0
629 || disk_stats.st_dev != me->me_dev)
631 me->me_dev = (dev_t) -2;
632 continue;
635 /* Prefer non-dummy entries. */
636 if (! me->me_dummy)
637 goto show_me;
638 matching_dummy = me;
642 if (matching_dummy)
643 goto show_matching_dummy;
645 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
646 print as much info as we can; methods that require the device to be
647 present will fail at a later point. */
649 /* Find the actual mount point. */
650 char *mp = find_mount_point (point, statp);
651 if (mp)
653 show_dev (0, mp, 0, 0, 0);
654 free (mp);
658 return;
660 show_matching_dummy:
661 me = matching_dummy;
662 show_me:
663 show_dev (me->me_devname, me->me_mountdir, me->me_type, me->me_dummy,
664 me->me_remote);
667 /* Determine what kind of node PATH is and show the disk usage
668 for it. STATP is the results of `stat' on PATH. */
670 static void
671 show_entry (const char *path, const struct stat *statp)
673 if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
674 && show_disk (path, statp))
675 return;
677 show_point (path, statp);
680 /* Show all mounted filesystems, except perhaps those that are of
681 an unselected type or are empty. */
683 static void
684 show_all_entries (void)
686 struct mount_entry *me;
688 for (me = mount_list; me; me = me->me_next)
689 show_dev (me->me_devname, me->me_mountdir, me->me_type,
690 me->me_dummy, me->me_remote);
693 /* Add FSTYPE to the list of filesystem types to display. */
695 static void
696 add_fs_type (const char *fstype)
698 struct fs_type_list *fsp;
700 fsp = xmalloc (sizeof *fsp);
701 fsp->fs_name = (char *) fstype;
702 fsp->fs_next = fs_select_list;
703 fs_select_list = fsp;
706 /* Add FSTYPE to the list of filesystem types to be omitted. */
708 static void
709 add_excluded_fs_type (const char *fstype)
711 struct fs_type_list *fsp;
713 fsp = xmalloc (sizeof *fsp);
714 fsp->fs_name = (char *) fstype;
715 fsp->fs_next = fs_exclude_list;
716 fs_exclude_list = fsp;
719 void
720 usage (int status)
722 if (status != EXIT_SUCCESS)
723 fprintf (stderr, _("Try `%s --help' for more information.\n"),
724 program_name);
725 else
727 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
728 fputs (_("\
729 Show information about the filesystem on which each FILE resides,\n\
730 or all filesystems by default.\n\
732 "), stdout);
733 fputs (_("\
734 Mandatory arguments to long options are mandatory for short options too.\n\
735 "), stdout);
736 fputs (_("\
737 -a, --all include filesystems having 0 blocks\n\
738 -B, --block-size=SIZE use SIZE-byte blocks\n\
739 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
740 -H, --si likewise, but use powers of 1000 not 1024\n\
741 "), stdout);
742 fputs (_("\
743 -i, --inodes list inode information instead of block usage\n\
744 -k like --block-size=1K\n\
745 -l, --local limit listing to local filesystems\n\
746 --no-sync do not invoke sync before getting usage info (default)\n\
747 "), stdout);
748 fputs (_("\
749 -P, --portability use the POSIX output format\n\
750 --sync invoke sync before getting usage info\n\
751 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
752 -T, --print-type print filesystem type\n\
753 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
754 -v (ignored)\n\
755 "), stdout);
756 fputs (HELP_OPTION_DESCRIPTION, stdout);
757 fputs (VERSION_OPTION_DESCRIPTION, stdout);
758 fputs (_("\n\
759 SIZE may be (or may be an integer optionally followed by) one of following:\n\
760 kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
761 "), stdout);
762 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
764 exit (status);
768 main (int argc, char **argv)
770 int c;
771 struct stat *stats IF_LINT (= 0);
772 int n_valid_args = 0;
774 initialize_main (&argc, &argv);
775 program_name = argv[0];
776 setlocale (LC_ALL, "");
777 bindtextdomain (PACKAGE, LOCALEDIR);
778 textdomain (PACKAGE);
780 atexit (close_stdout);
782 fs_select_list = NULL;
783 fs_exclude_list = NULL;
784 inode_format = 0;
785 show_all_fs = 0;
786 show_listed_fs = 0;
788 human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false,
789 &output_block_size);
791 print_type = 0;
792 posix_format = 0;
793 exit_status = 0;
795 while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
796 != -1)
798 switch (c)
800 case 0: /* Long option. */
801 break;
802 case 'a':
803 show_all_fs = 1;
804 break;
805 case 'B':
806 human_output_opts = human_options (optarg, true, &output_block_size);
807 break;
808 case 'i':
809 inode_format = 1;
810 break;
811 case 'h':
812 human_output_opts = human_autoscale | human_SI | human_base_1024;
813 output_block_size = 1;
814 break;
815 case 'H':
816 human_output_opts = human_autoscale | human_SI;
817 output_block_size = 1;
818 break;
819 case 'k':
820 human_output_opts = 0;
821 output_block_size = 1024;
822 break;
823 case 'l':
824 show_local_fs = 1;
825 break;
826 case 'm': /* obsolescent */
827 human_output_opts = 0;
828 output_block_size = 1024 * 1024;
829 break;
830 case 'T':
831 print_type = 1;
832 break;
833 case 'P':
834 posix_format = 1;
835 break;
836 case SYNC_OPTION:
837 require_sync = 1;
838 break;
839 case NO_SYNC_OPTION:
840 require_sync = 0;
841 break;
843 case 'F':
844 /* Accept -F as a synonym for -t for compatibility with Solaris. */
845 case 't':
846 add_fs_type (optarg);
847 break;
849 case 'v': /* For SysV compatibility. */
850 /* ignore */
851 break;
852 case 'x':
853 add_excluded_fs_type (optarg);
854 break;
856 case_GETOPT_HELP_CHAR;
857 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
859 default:
860 usage (EXIT_FAILURE);
864 /* Fail if the same file system type was both selected and excluded. */
866 int match = 0;
867 struct fs_type_list *fs_incl;
868 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
870 struct fs_type_list *fs_excl;
871 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
873 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
875 error (0, 0,
876 _("file system type %s both selected and excluded"),
877 quote (fs_incl->fs_name));
878 match = 1;
879 break;
883 if (match)
884 exit (EXIT_FAILURE);
887 if (optind < argc)
889 int i;
891 /* stat all the given entries to make sure they get automounted,
892 if necessary, before reading the filesystem table. */
893 stats = xmalloc ((argc - optind) * sizeof *stats);
894 for (i = optind; i < argc; ++i)
896 if (stat (argv[i], &stats[i - optind]))
898 error (0, errno, "%s", quote (argv[i]));
899 exit_status = 1;
900 argv[i] = NULL;
902 else
904 ++n_valid_args;
909 mount_list =
910 read_filesystem_list ((fs_select_list != NULL
911 || fs_exclude_list != NULL
912 || print_type
913 || show_local_fs));
915 if (mount_list == NULL)
917 /* Couldn't read the table of mounted filesystems.
918 Fail if df was invoked with no file name arguments;
919 Otherwise, merely give a warning and proceed. */
920 const char *warning = (optind < argc ? _("Warning: ") : "");
921 int status = (optind < argc ? 0 : EXIT_FAILURE);
922 error (status, errno,
923 _("%scannot read table of mounted filesystems"), warning);
926 if (require_sync)
927 sync ();
929 if (optind < argc)
931 int i;
933 /* Display explicitly requested empty filesystems. */
934 show_listed_fs = 1;
936 if (n_valid_args > 0)
937 print_header ();
939 for (i = optind; i < argc; ++i)
940 if (argv[i])
941 show_entry (argv[i], &stats[i - optind]);
943 else
945 print_header ();
946 show_all_entries ();
949 exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);