Port parts of the code to C89 to minimize the need for c99-to-c89.diff,
[coreutils.git] / src / df.c
blob61a0b40f4fed04dabeee1f2fe6f1a738659950e3
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2006 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>
27 #include "system.h"
28 #include "canonicalize.h"
29 #include "error.h"
30 #include "fsusage.h"
31 #include "human.h"
32 #include "inttostr.h"
33 #include "mountlist.h"
34 #include "quote.h"
35 #include "save-cwd.h"
36 #include "xgetcwd.h"
38 /* The official name of this program (e.g., no `g' prefix). */
39 #define PROGRAM_NAME "df"
41 #define AUTHORS \
42 "Torbjorn Granlund", "David MacKenzie", "Paul Eggert"
44 /* Name this program was run with. */
45 char *program_name;
47 /* If true, show inode information. */
48 static bool inode_format;
50 /* If true, show even file systems with zero size or
51 uninteresting types. */
52 static bool show_all_fs;
54 /* If true, show only local file systems. */
55 static bool show_local_fs;
57 /* If true, output data for each file system corresponding to a
58 command line argument -- even if it's a dummy (automounter) entry. */
59 static bool show_listed_fs;
61 /* Human-readable options for output. */
62 static int human_output_opts;
64 /* The units to use when printing sizes. */
65 static uintmax_t output_block_size;
67 /* If true, use the POSIX output format. */
68 static bool posix_format;
70 /* True if a file system has been processed for output. */
71 static bool file_systems_processed;
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 NO_SYNC_OPTION = CHAR_MAX + 1,
119 /* FIXME: --kilobytes is deprecated (but not -k); remove in late 2006 */
120 KILOBYTES_LONG_OPTION,
121 SYNC_OPTION
124 static struct option const long_options[] =
126 {"all", no_argument, NULL, 'a'},
127 {"block-size", required_argument, NULL, 'B'},
128 {"inodes", no_argument, NULL, 'i'},
129 {"human-readable", no_argument, NULL, 'h'},
130 {"si", no_argument, NULL, 'H'},
131 {"kilobytes", no_argument, NULL, KILOBYTES_LONG_OPTION},
132 {"local", no_argument, NULL, 'l'},
133 {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */
134 {"portability", no_argument, NULL, 'P'},
135 {"print-type", no_argument, NULL, 'T'},
136 {"sync", no_argument, NULL, SYNC_OPTION},
137 {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
138 {"type", required_argument, NULL, 't'},
139 {"exclude-type", required_argument, NULL, 'x'},
140 {GETOPT_HELP_OPTION_DECL},
141 {GETOPT_VERSION_OPTION_DECL},
142 {NULL, 0, NULL, 0}
145 static void
146 print_header (void)
148 char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
150 if (print_type)
151 fputs (_("Filesystem Type"), stdout);
152 else
153 fputs (_("Filesystem "), stdout);
155 if (inode_format)
156 printf (_(" Inodes IUsed IFree IUse%%"));
157 else if (human_output_opts & human_autoscale)
159 if (human_output_opts & human_base_1024)
160 printf (_(" Size Used Avail Use%%"));
161 else
162 printf (_(" Size Used Avail Use%%"));
164 else if (posix_format)
165 printf (_(" %s-blocks Used Available Capacity"),
166 umaxtostr (output_block_size, buf));
167 else
169 int opts = (human_suppress_point_zero
170 | human_autoscale | human_SI
171 | (human_output_opts
172 & (human_group_digits | human_base_1024 | human_B)));
174 /* Prefer the base that makes the human-readable value more exact,
175 if there is a difference. */
177 uintmax_t q1000 = output_block_size;
178 uintmax_t q1024 = output_block_size;
179 bool divisible_by_1000;
180 bool divisible_by_1024;
184 divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
185 divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
187 while (divisible_by_1000 & divisible_by_1024);
189 if (divisible_by_1000 < divisible_by_1024)
190 opts |= human_base_1024;
191 if (divisible_by_1024 < divisible_by_1000)
192 opts &= ~human_base_1024;
193 if (! (opts & human_base_1024))
194 opts |= human_B;
196 printf (_(" %4s-blocks Used Available Use%%"),
197 human_readable (output_block_size, buf, opts, 1, 1));
200 printf (_(" Mounted on\n"));
203 /* Is FSTYPE a type of file system that should be listed? */
205 static bool
206 selected_fstype (const char *fstype)
208 const struct fs_type_list *fsp;
210 if (fs_select_list == NULL || fstype == NULL)
211 return true;
212 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
213 if (STREQ (fstype, fsp->fs_name))
214 return true;
215 return false;
218 /* Is FSTYPE a type of file system that should be omitted? */
220 static bool
221 excluded_fstype (const char *fstype)
223 const struct fs_type_list *fsp;
225 if (fs_exclude_list == NULL || fstype == NULL)
226 return false;
227 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
228 if (STREQ (fstype, fsp->fs_name))
229 return true;
230 return false;
233 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
234 except:
236 - If NEGATIVE, then N represents a negative number,
237 expressed in two's complement.
238 - Otherwise, return "-" if N is UINTMAX_MAX. */
240 static char const *
241 df_readable (bool negative, uintmax_t n, char *buf,
242 uintmax_t input_units, uintmax_t output_units)
244 if (n == UINTMAX_MAX && !negative)
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 file name DISK.
257 If MOUNT_POINT is non-NULL, it is the name of the root of the
258 file system on DISK.
259 If STAT_FILE is non-null, it is the name of a file within the file
260 system that the user originally asked for; this provides better
261 diagnostics, and sometimes it provides better results on networked
262 file systems that give different free-space results depending on
263 where in the file system you probe.
264 If FSTYPE is non-NULL, it is the type of the file system on DISK.
265 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
266 not be able to produce statistics in this case.
267 ME_DUMMY and ME_REMOTE are the mount entry flags. */
269 static void
270 show_dev (char const *disk, char const *mount_point,
271 char const *stat_file, char const *fstype,
272 bool me_dummy, bool me_remote)
274 struct fs_usage fsu;
275 char buf[3][LONGEST_HUMAN_READABLE + 2];
276 int width;
277 int col1_adjustment = 0;
278 int use_width;
279 uintmax_t input_units;
280 uintmax_t output_units;
281 uintmax_t total;
282 uintmax_t available;
283 bool negate_available;
284 uintmax_t available_to_root;
285 uintmax_t used;
286 bool negate_used;
287 double pct = -1;
289 if (me_remote & show_local_fs)
290 return;
292 if (me_dummy & !show_all_fs & !show_listed_fs)
293 return;
295 if (!selected_fstype (fstype) || excluded_fstype (fstype))
296 return;
298 /* If MOUNT_POINT is NULL, then the file system is not mounted, and this
299 program reports on the file system that the special file is on.
300 It would be better to report on the unmounted file system,
301 but statfs doesn't do that on most systems. */
302 if (!stat_file)
303 stat_file = mount_point ? mount_point : disk;
305 if (get_fs_usage (stat_file, disk, &fsu))
307 error (0, errno, "%s", quote (stat_file));
308 exit_status = EXIT_FAILURE;
309 return;
312 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
313 return;
315 if (! file_systems_processed)
317 file_systems_processed = true;
318 print_header ();
321 if (! disk)
322 disk = "-"; /* unknown */
323 if (! fstype)
324 fstype = "-"; /* unknown */
326 /* df.c reserved 5 positions for fstype,
327 but that does not suffice for type iso9660 */
328 if (print_type)
330 size_t disk_name_len = strlen (disk);
331 size_t fstype_len = strlen (fstype);
332 if (disk_name_len + fstype_len < 18)
333 printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype);
334 else if (!posix_format)
335 printf ("%s\n%18s ", disk, fstype);
336 else
337 printf ("%s %s", disk, fstype);
339 else
341 if (strlen (disk) > 20 && !posix_format)
342 printf ("%s\n%20s", disk, "");
343 else
344 printf ("%-20s", disk);
347 if (inode_format)
349 width = 7;
350 use_width = 5;
351 input_units = output_units = 1;
352 total = fsu.fsu_files;
353 available = fsu.fsu_ffree;
354 negate_available = false;
355 available_to_root = available;
357 else
359 if (human_output_opts & human_autoscale)
360 width = 5 + ! (human_output_opts & human_base_1024);
361 else
363 width = 9;
364 if (posix_format)
366 uintmax_t b;
367 col1_adjustment = -3;
368 for (b = output_block_size; 9 < b; b /= 10)
369 col1_adjustment++;
372 use_width = ((posix_format
373 && ! (human_output_opts & human_autoscale))
374 ? 8 : 4);
375 input_units = fsu.fsu_blocksize;
376 output_units = output_block_size;
377 total = fsu.fsu_blocks;
378 available = fsu.fsu_bavail;
379 negate_available = (fsu.fsu_bavail_top_bit_set
380 & (available != UINTMAX_MAX));
381 available_to_root = fsu.fsu_bfree;
384 used = UINTMAX_MAX;
385 negate_used = false;
386 if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX)
388 used = total - available_to_root;
389 negate_used = (total < available_to_root);
392 printf (" %*s %*s %*s ",
393 width + col1_adjustment,
394 df_readable (false, total,
395 buf[0], input_units, output_units),
396 width, df_readable (negate_used, used,
397 buf[1], input_units, output_units),
398 width, df_readable (negate_available, available,
399 buf[2], input_units, output_units));
401 if (used == UINTMAX_MAX || available == UINTMAX_MAX)
403 else if (!negate_used
404 && used <= TYPE_MAXIMUM (uintmax_t) / 100
405 && used + available != 0
406 && (used + available < used) == negate_available)
408 uintmax_t u100 = used * 100;
409 uintmax_t nonroot_total = used + available;
410 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
412 else
414 /* The calculation cannot be done easily with integer
415 arithmetic. Fall back on floating point. This can suffer
416 from minor rounding errors, but doing it exactly requires
417 multiple precision arithmetic, and it's not worth the
418 aggravation. */
419 double u = negate_used ? - (double) - used : used;
420 double a = negate_available ? - (double) - available : available;
421 double nonroot_total = u + a;
422 if (nonroot_total)
424 long int lipct = pct = u * 100 / nonroot_total;
425 double ipct = lipct;
427 /* Like `pct = ceil (dpct);', but avoid ceil so that
428 the math library needn't be linked. */
429 if (ipct - 1 < pct && pct <= ipct + 1)
430 pct = ipct + (ipct < pct);
434 if (0 <= pct)
435 printf ("%*.0f%%", use_width - 1, pct);
436 else
437 printf ("%*s", use_width, "- ");
439 if (mount_point)
441 #ifdef HIDE_AUTOMOUNT_PREFIX
442 /* Don't print the first directory name in MOUNT_POINT if it's an
443 artifact of an automounter. This is a bit too aggressive to be
444 the default. */
445 if (strncmp ("/auto/", mount_point, 6) == 0)
446 mount_point += 5;
447 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
448 mount_point += 8;
449 #endif
450 printf (" %s", mount_point);
452 putchar ('\n');
455 /* Return the root mountpoint of the file system on which FILE exists, in
456 malloced storage. FILE_STAT should be the result of stating FILE.
457 Give a diagnostic and return NULL if unable to determine the mount point.
458 Exit if unable to restore current working directory. */
459 static char *
460 find_mount_point (const char *file, const struct stat *file_stat)
462 struct saved_cwd cwd;
463 struct stat last_stat;
464 char *mp = NULL; /* The malloced mount point. */
466 if (save_cwd (&cwd) != 0)
468 error (0, errno, _("cannot get current directory"));
469 return NULL;
472 if (S_ISDIR (file_stat->st_mode))
473 /* FILE is a directory, so just chdir there directly. */
475 last_stat = *file_stat;
476 if (chdir (file) < 0)
478 error (0, errno, _("cannot change to directory %s"), quote (file));
479 return NULL;
482 else
483 /* FILE is some other kind of file; use its directory. */
485 char *xdir = dir_name (file);
486 char *dir;
487 ASSIGN_STRDUPA (dir, xdir);
488 free (xdir);
490 if (chdir (dir) < 0)
492 error (0, errno, _("cannot change to directory %s"), quote (dir));
493 return NULL;
496 if (stat (".", &last_stat) < 0)
498 error (0, errno, _("cannot stat current directory (now %s)"),
499 quote (dir));
500 goto done;
504 /* Now walk up FILE's parents until we find another file system or /,
505 chdiring as we go. LAST_STAT holds stat information for the last place
506 we visited. */
507 for (;;)
509 struct stat st;
510 if (stat ("..", &st) < 0)
512 error (0, errno, _("cannot stat %s"), quote (".."));
513 goto done;
515 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
516 /* cwd is the mount point. */
517 break;
518 if (chdir ("..") < 0)
520 error (0, errno, _("cannot change to directory %s"), quote (".."));
521 goto done;
523 last_stat = st;
526 /* Finally reached a mount point, see what it's called. */
527 mp = xgetcwd ();
529 done:
530 /* Restore the original cwd. */
532 int save_errno = errno;
533 if (restore_cwd (&cwd) != 0)
534 error (EXIT_FAILURE, errno,
535 _("failed to return to initial working directory"));
536 free_cwd (&cwd);
537 errno = save_errno;
540 return mp;
543 /* If DISK corresponds to a mount point, show its usage
544 and return true. Otherwise, return false. */
545 static bool
546 show_disk (char const *disk)
548 struct mount_entry const *me;
549 struct mount_entry const *best_match = NULL;
551 for (me = mount_list; me; me = me->me_next)
552 if (STREQ (disk, me->me_devname))
553 best_match = me;
555 if (best_match)
557 show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
558 best_match->me_type, best_match->me_dummy,
559 best_match->me_remote);
560 return true;
563 return false;
566 /* Figure out which device file or directory POINT is mounted on
567 and show its disk usage.
568 STATP must be the result of `stat (POINT, STATP)'. */
569 static void
570 show_point (const char *point, const struct stat *statp)
572 struct stat disk_stats;
573 struct mount_entry *me;
574 struct mount_entry const *best_match = NULL;
576 /* If POINT is an absolute file name, see if we can find the
577 mount point without performing any extra stat calls at all. */
578 if (*point == '/')
580 /* Find the best match: prefer non-dummies, and then prefer the
581 last match if there are ties. */
583 for (me = mount_list; me; me = me->me_next)
584 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs")
585 && (!best_match || best_match->me_dummy || !me->me_dummy))
586 best_match = me;
589 /* Calculate the real absolute file name for POINT, and use that to find
590 the mount point. This avoids statting unavailable mount points,
591 which can hang df. */
592 if (! best_match)
594 char *resolved = canonicalize_file_name (point);
596 if (resolved && resolved[0] == '/')
598 size_t resolved_len = strlen (resolved);
599 size_t best_match_len = 0;
601 for (me = mount_list; me; me = me->me_next)
602 if (!STREQ (me->me_type, "lofs")
603 && (!best_match || best_match->me_dummy || !me->me_dummy))
605 size_t len = strlen (me->me_mountdir);
606 if (best_match_len <= len && len <= resolved_len
607 && (len == 1 /* root file system */
608 || ((len == resolved_len || resolved[len] == '/')
609 && strncmp (me->me_mountdir, resolved, len) == 0)))
611 best_match = me;
612 best_match_len = len;
617 free (resolved);
619 if (best_match
620 && (stat (best_match->me_mountdir, &disk_stats) != 0
621 || disk_stats.st_dev != statp->st_dev))
622 best_match = NULL;
625 if (! best_match)
626 for (me = mount_list; me; me = me->me_next)
628 if (me->me_dev == (dev_t) -1)
630 if (stat (me->me_mountdir, &disk_stats) == 0)
631 me->me_dev = disk_stats.st_dev;
632 else
634 /* Report only I/O errors. Other errors might be
635 caused by shadowed mount points, which means POINT
636 can't possibly be on this file system. */
637 if (errno == EIO)
639 error (0, errno, "%s", quote (me->me_mountdir));
640 exit_status = EXIT_FAILURE;
643 /* So we won't try and fail repeatedly. */
644 me->me_dev = (dev_t) -2;
648 if (statp->st_dev == me->me_dev
649 && !STREQ (me->me_type, "lofs")
650 && (!best_match || best_match->me_dummy || !me->me_dummy))
652 /* Skip bogus mtab entries. */
653 if (stat (me->me_mountdir, &disk_stats) != 0
654 || disk_stats.st_dev != me->me_dev)
655 me->me_dev = (dev_t) -2;
656 else
657 best_match = me;
661 if (best_match)
662 show_dev (best_match->me_devname, best_match->me_mountdir, point,
663 best_match->me_type, best_match->me_dummy, best_match->me_remote);
664 else
666 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
667 print as much info as we can; methods that require the device to be
668 present will fail at a later point. */
670 /* Find the actual mount point. */
671 char *mp = find_mount_point (point, statp);
672 if (mp)
674 show_dev (NULL, mp, NULL, NULL, false, false);
675 free (mp);
680 /* Determine what kind of node NAME is and show the disk usage
681 for it. STATP is the results of `stat' on NAME. */
683 static void
684 show_entry (char const *name, struct stat const *statp)
686 if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
687 && show_disk (name))
688 return;
690 show_point (name, statp);
693 /* Show all mounted file systems, except perhaps those that are of
694 an unselected type or are empty. */
696 static void
697 show_all_entries (void)
699 struct mount_entry *me;
701 for (me = mount_list; me; me = me->me_next)
702 show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
703 me->me_dummy, me->me_remote);
706 /* Add FSTYPE to the list of file system types to display. */
708 static void
709 add_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_select_list;
716 fs_select_list = fsp;
719 /* Add FSTYPE to the list of file system types to be omitted. */
721 static void
722 add_excluded_fs_type (const char *fstype)
724 struct fs_type_list *fsp;
726 fsp = xmalloc (sizeof *fsp);
727 fsp->fs_name = (char *) fstype;
728 fsp->fs_next = fs_exclude_list;
729 fs_exclude_list = fsp;
732 void
733 usage (int status)
735 if (status != EXIT_SUCCESS)
736 fprintf (stderr, _("Try `%s --help' for more information.\n"),
737 program_name);
738 else
740 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
741 fputs (_("\
742 Show information about the file system on which each FILE resides,\n\
743 or all file systems by default.\n\
745 "), stdout);
746 fputs (_("\
747 Mandatory arguments to long options are mandatory for short options too.\n\
748 "), stdout);
749 fputs (_("\
750 -a, --all include dummy file systems\n\
751 -B, --block-size=SIZE use SIZE-byte blocks\n\
752 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
753 -H, --si likewise, but use powers of 1000 not 1024\n\
754 "), stdout);
755 fputs (_("\
756 -i, --inodes list inode information instead of block usage\n\
757 -k like --block-size=1K\n\
758 -l, --local limit listing to local file systems\n\
759 --no-sync do not invoke sync before getting usage info (default)\n\
760 "), stdout);
761 fputs (_("\
762 -P, --portability use the POSIX output format\n\
763 --sync invoke sync before getting usage info\n\
764 -t, --type=TYPE limit listing to file systems of type TYPE\n\
765 -T, --print-type print file system type\n\
766 -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\
767 -v (ignored)\n\
768 "), stdout);
769 fputs (HELP_OPTION_DESCRIPTION, stdout);
770 fputs (VERSION_OPTION_DESCRIPTION, stdout);
771 fputs (_("\n\
772 SIZE may be (or may be an integer optionally followed by) one of following:\n\
773 kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
774 "), stdout);
775 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
777 exit (status);
781 main (int argc, char **argv)
783 int c;
784 struct stat *stats IF_LINT (= 0);
786 initialize_main (&argc, &argv);
787 program_name = argv[0];
788 setlocale (LC_ALL, "");
789 bindtextdomain (PACKAGE, LOCALEDIR);
790 textdomain (PACKAGE);
792 atexit (close_stdout);
794 fs_select_list = NULL;
795 fs_exclude_list = NULL;
796 inode_format = false;
797 show_all_fs = false;
798 show_listed_fs = false;
800 human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false,
801 &output_block_size);
803 print_type = false;
804 file_systems_processed = false;
805 posix_format = false;
806 exit_status = EXIT_SUCCESS;
808 while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
809 != -1)
811 switch (c)
813 case 'a':
814 show_all_fs = true;
815 break;
816 case 'B':
817 human_output_opts = human_options (optarg, true, &output_block_size);
818 break;
819 case 'i':
820 inode_format = true;
821 break;
822 case 'h':
823 human_output_opts = human_autoscale | human_SI | human_base_1024;
824 output_block_size = 1;
825 break;
826 case 'H':
827 human_output_opts = human_autoscale | human_SI;
828 output_block_size = 1;
829 break;
830 case KILOBYTES_LONG_OPTION:
831 error (0, 0,
832 _("the --kilobytes option is deprecated; use -k instead"));
833 /* fall through */
834 case 'k':
835 human_output_opts = 0;
836 output_block_size = 1024;
837 break;
838 case 'l':
839 show_local_fs = true;
840 break;
841 case 'm': /* obsolescent */
842 human_output_opts = 0;
843 output_block_size = 1024 * 1024;
844 break;
845 case 'T':
846 print_type = true;
847 break;
848 case 'P':
849 posix_format = true;
850 break;
851 case SYNC_OPTION:
852 require_sync = true;
853 break;
854 case NO_SYNC_OPTION:
855 require_sync = false;
856 break;
858 case 'F':
859 /* Accept -F as a synonym for -t for compatibility with Solaris. */
860 case 't':
861 add_fs_type (optarg);
862 break;
864 case 'v': /* For SysV compatibility. */
865 /* ignore */
866 break;
867 case 'x':
868 add_excluded_fs_type (optarg);
869 break;
871 case_GETOPT_HELP_CHAR;
872 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
874 default:
875 usage (EXIT_FAILURE);
879 /* Fail if the same file system type was both selected and excluded. */
881 bool match = false;
882 struct fs_type_list *fs_incl;
883 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
885 struct fs_type_list *fs_excl;
886 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
888 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
890 error (0, 0,
891 _("file system type %s both selected and excluded"),
892 quote (fs_incl->fs_name));
893 match = true;
894 break;
898 if (match)
899 exit (EXIT_FAILURE);
902 if (optind < argc)
904 int i;
906 /* stat all the given entries to make sure they get automounted,
907 if necessary, before reading the file system table. */
908 stats = xnmalloc (argc - optind, sizeof *stats);
909 for (i = optind; i < argc; ++i)
911 if (stat (argv[i], &stats[i - optind]))
913 error (0, errno, "%s", quote (argv[i]));
914 exit_status = EXIT_FAILURE;
915 argv[i] = NULL;
920 mount_list =
921 read_file_system_list ((fs_select_list != NULL
922 || fs_exclude_list != NULL
923 || print_type
924 || show_local_fs));
926 if (mount_list == NULL)
928 /* Couldn't read the table of mounted file systems.
929 Fail if df was invoked with no file name arguments;
930 Otherwise, merely give a warning and proceed. */
931 const char *warning = (optind < argc ? _("Warning: ") : "");
932 int status = (optind < argc ? 0 : EXIT_FAILURE);
933 error (status, errno,
934 _("%scannot read table of mounted file systems"), warning);
937 if (require_sync)
938 sync ();
940 if (optind < argc)
942 int i;
944 /* Display explicitly requested empty file systems. */
945 show_listed_fs = true;
947 for (i = optind; i < argc; ++i)
948 if (argv[i])
949 show_entry (argv[i], &stats[i - optind]);
951 else
952 show_all_entries ();
954 if (! file_systems_processed)
955 error (EXIT_FAILURE, 0, _("no file systems processed"));
957 exit (exit_status);