(__restrict): Define to `restrict' or to nothing.
[coreutils.git] / src / df.c
blob42b8f88c1242291c8ef87f56079d886abb2b7944
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2003 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 #ifdef _AIX
23 #pragma alloca
24 #endif
26 #include <config.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <getopt.h>
30 #include <assert.h>
32 #include "system.h"
33 #include "canonicalize.h"
34 #include "dirname.h"
35 #include "error.h"
36 #include "fsusage.h"
37 #include "human.h"
38 #include "inttostr.h"
39 #include "mountlist.h"
40 #include "path-concat.h"
41 #include "quote.h"
42 #include "save-cwd.h"
43 #include "xgetcwd.h"
45 /* The official name of this program (e.g., no `g' prefix). */
46 #define PROGRAM_NAME "df"
48 #define AUTHORS \
49 N_ ("Torbjorn Granlund, David MacKenzie, Larry McVoy, and Paul Eggert")
51 /* Name this program was run with. */
52 char *program_name;
54 /* If nonzero, show inode information. */
55 static int inode_format;
57 /* If nonzero, show even filesystems with zero size or
58 uninteresting types. */
59 static int show_all_fs;
61 /* If nonzero, show only local filesystems. */
62 static int show_local_fs;
64 /* If nonzero, output data for each filesystem corresponding to a
65 command line argument -- even if it's a dummy (automounter) entry. */
66 static int show_listed_fs;
68 /* Human-readable options for output. */
69 static int human_output_opts;
71 /* The units to use when printing sizes. */
72 static uintmax_t output_block_size;
74 /* If nonzero, use the POSIX output format. */
75 static int posix_format;
77 /* If nonzero, invoke the `sync' system call before getting any usage data.
78 Using this option can make df very slow, especially with many or very
79 busy disks. Note that this may make a difference on some systems --
80 SunOs4.1.3, for one. It is *not* necessary on Linux. */
81 static int require_sync = 0;
83 /* Nonzero if errors have occurred. */
84 static int exit_status;
86 /* A filesystem type to display. */
88 struct fs_type_list
90 char *fs_name;
91 struct fs_type_list *fs_next;
94 /* Linked list of filesystem types to display.
95 If `fs_select_list' is NULL, list all types.
96 This table is generated dynamically from command-line options,
97 rather than hardcoding into the program what it thinks are the
98 valid filesystem types; let the user specify any filesystem type
99 they want to, and if there are any filesystems of that type, they
100 will be shown.
102 Some filesystem types:
103 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
105 static struct fs_type_list *fs_select_list;
107 /* Linked list of filesystem types to omit.
108 If the list is empty, don't exclude any types. */
110 static struct fs_type_list *fs_exclude_list;
112 /* Linked list of mounted filesystems. */
113 static struct mount_entry *mount_list;
115 /* If nonzero, print filesystem type as well. */
116 static int print_type;
118 /* For long options that have no equivalent short option, use a
119 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
120 enum
122 SYNC_OPTION = CHAR_MAX + 1,
123 NO_SYNC_OPTION
126 static struct option const long_options[] =
128 {"all", no_argument, NULL, 'a'},
129 {"block-size", required_argument, NULL, 'B'},
130 {"inodes", no_argument, NULL, 'i'},
131 {"human-readable", no_argument, NULL, 'h'},
132 {"si", no_argument, NULL, 'H'},
133 {"kilobytes", no_argument, NULL, 'k'}, /* long form is obsolescent */
134 {"local", no_argument, NULL, 'l'},
135 {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */
136 {"portability", no_argument, NULL, 'P'},
137 {"print-type", no_argument, NULL, 'T'},
138 {"sync", no_argument, NULL, SYNC_OPTION},
139 {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
140 {"type", required_argument, NULL, 't'},
141 {"exclude-type", required_argument, NULL, 'x'},
142 {GETOPT_HELP_OPTION_DECL},
143 {GETOPT_VERSION_OPTION_DECL},
144 {NULL, 0, NULL, 0}
147 static void
148 print_header (void)
150 char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
152 printf (_("Filesystem "));
154 if (print_type)
155 printf (_(" Type"));
156 else
157 printf (" ");
159 if (inode_format)
160 printf (_(" Inodes IUsed IFree IUse%%"));
161 else if (human_output_opts & human_autoscale)
163 if (human_output_opts & human_base_1024)
164 printf (_(" Size Used Avail Use%%"));
165 else
166 printf (_(" Size Used Avail Use%%"));
168 else if (posix_format)
169 printf (_(" %4s-blocks Used Available Capacity"),
170 umaxtostr (output_block_size, buf));
171 else
173 int opts = (human_suppress_point_zero
174 | human_autoscale | human_SI
175 | (human_output_opts
176 & (human_group_digits | human_base_1024 | human_B)));
178 /* Prefer the base that makes the human-readable value more exact,
179 if there is a difference. */
181 uintmax_t q1000 = output_block_size;
182 uintmax_t q1024 = output_block_size;
183 bool divisible_by_1000;
184 bool divisible_by_1024;
188 divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
189 divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
191 while (divisible_by_1000 & divisible_by_1024);
193 if (divisible_by_1000 < divisible_by_1024)
194 opts |= human_base_1024;
195 if (divisible_by_1024 < divisible_by_1000)
196 opts &= ~human_base_1024;
197 if (! (opts & human_base_1024))
198 opts |= human_B;
200 printf (_(" %4s-blocks Used Available Use%%"),
201 human_readable (output_block_size, buf, opts, 1, 1));
204 printf (_(" Mounted on\n"));
207 /* If FSTYPE is a type of filesystem that should be listed,
208 return nonzero, else zero. */
210 static int
211 selected_fstype (const char *fstype)
213 const struct fs_type_list *fsp;
215 if (fs_select_list == NULL || fstype == NULL)
216 return 1;
217 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
218 if (STREQ (fstype, fsp->fs_name))
219 return 1;
220 return 0;
223 /* If FSTYPE is a type of filesystem that should be omitted,
224 return nonzero, else zero. */
226 static int
227 excluded_fstype (const char *fstype)
229 const struct fs_type_list *fsp;
231 if (fs_exclude_list == NULL || fstype == NULL)
232 return 0;
233 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
234 if (STREQ (fstype, fsp->fs_name))
235 return 1;
236 return 0;
239 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
240 except:
242 - Return "-" if N is -1,
243 - If NEGATIVE is 1 then N represents a negative number,
244 expressed in two's complement. */
246 static char const *
247 df_readable (int negative, uintmax_t n, char *buf,
248 uintmax_t input_units, uintmax_t output_units)
250 if (n == -1)
251 return "-";
252 else
254 char *p = human_readable (negative ? -n : n, buf + negative,
255 human_output_opts, input_units, output_units);
256 if (negative)
257 *--p = '-';
258 return p;
262 /* Display a space listing for the disk device with absolute path DISK.
263 If MOUNT_POINT is non-NULL, it is the path of the root of the
264 filesystem on DISK.
265 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
266 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
267 not be able to produce statistics in this case.
268 ME_DUMMY and ME_REMOTE are the mount entry flags. */
270 static void
271 show_dev (const char *disk, const char *mount_point, const char *fstype,
272 int me_dummy, int me_remote)
274 struct fs_usage fsu;
275 const char *stat_file;
276 char buf[3][LONGEST_HUMAN_READABLE + 2];
277 int width;
278 int use_width;
279 uintmax_t input_units;
280 uintmax_t output_units;
281 uintmax_t total;
282 uintmax_t available;
283 int negate_available;
284 uintmax_t available_to_root;
285 uintmax_t used;
286 int negate_used;
287 double pct = -1;
289 if (me_remote && show_local_fs)
290 return;
292 if (me_dummy && show_all_fs == 0 && !show_listed_fs)
293 return;
295 if (!selected_fstype (fstype) || excluded_fstype (fstype))
296 return;
298 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
299 program reports on the filesystem that the special file is on.
300 It would be better to report on the unmounted filesystem,
301 but statfs doesn't do that on most systems. */
302 stat_file = mount_point ? mount_point : disk;
304 if (get_fs_usage (stat_file, disk, &fsu))
306 error (0, errno, "%s", quote (stat_file));
307 exit_status = 1;
308 return;
311 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
312 return;
314 if (! disk)
315 disk = "-"; /* unknown */
316 if (! fstype)
317 fstype = "-"; /* unknown */
319 /* df.c reserved 5 positions for fstype,
320 but that does not suffice for type iso9660 */
321 if (print_type)
323 int disk_name_len = (int) strlen (disk);
324 int fstype_len = (int) strlen (fstype);
325 if (disk_name_len + fstype_len + 2 < 20)
326 printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
327 else if (!posix_format)
328 printf ("%s\n%18s ", disk, fstype);
329 else
330 printf ("%s %s", disk, fstype);
332 else
334 if ((int) strlen (disk) > 20 && !posix_format)
335 printf ("%s\n%20s", disk, "");
336 else
337 printf ("%-20s", disk);
340 if (inode_format)
342 width = 7;
343 use_width = 5;
344 input_units = output_units = 1;
345 total = fsu.fsu_files;
346 available = fsu.fsu_ffree;
347 negate_available = 0;
348 available_to_root = available;
350 else
352 width = (human_output_opts & human_autoscale
353 ? 5 + ! (human_output_opts & human_base_1024)
354 : 9);
355 use_width = ((posix_format
356 && ! (human_output_opts & human_autoscale))
357 ? 8 : 4);
358 input_units = fsu.fsu_blocksize;
359 output_units = output_block_size;
360 total = fsu.fsu_blocks;
361 available = fsu.fsu_bavail;
362 negate_available = fsu.fsu_bavail_top_bit_set;
363 available_to_root = fsu.fsu_bfree;
366 used = -1;
367 negate_used = 0;
368 if (total != -1 && available_to_root != -1)
370 used = total - available_to_root;
371 if (total < available_to_root)
373 negate_used = 1;
374 used = - used;
378 printf (" %*s %*s %*s ",
379 width, df_readable (0, total,
380 buf[0], input_units, output_units),
381 width, df_readable (negate_used, used,
382 buf[1], input_units, output_units),
383 width, df_readable (negate_available, available,
384 buf[2], input_units, output_units));
386 if (used == -1 || available == -1)
388 else if (!negate_used
389 && used <= TYPE_MAXIMUM (uintmax_t) / 100
390 && used + available != 0
391 && (used + available < used) == negate_available)
393 uintmax_t u100 = used * 100;
394 uintmax_t nonroot_total = used + available;
395 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
397 else
399 /* The calculation cannot be done easily with integer
400 arithmetic. Fall back on floating point. This can suffer
401 from minor rounding errors, but doing it exactly requires
402 multiple precision arithmetic, and it's not worth the
403 aggravation. */
404 double u = negate_used ? - (double) - used : used;
405 double a = negate_available ? - (double) - available : available;
406 double nonroot_total = u + a;
407 if (nonroot_total)
409 double ipct;
410 pct = u * 100 / nonroot_total;
411 ipct = (long) pct;
413 /* Like `pct = ceil (dpct);', but avoid ceil so that
414 the math library needn't be linked. */
415 if (ipct - 1 < pct && pct <= ipct + 1)
416 pct = ipct + (ipct < pct);
420 if (0 <= pct)
421 printf ("%*.0f%%", use_width - 1, pct);
422 else
423 printf ("%*s", use_width, "- ");
425 if (mount_point)
427 #ifdef HIDE_AUTOMOUNT_PREFIX
428 /* Don't print the first directory name in MOUNT_POINT if it's an
429 artifact of an automounter. This is a bit too aggressive to be
430 the default. */
431 if (strncmp ("/auto/", mount_point, 6) == 0)
432 mount_point += 5;
433 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
434 mount_point += 8;
435 #endif
436 printf (" %s", mount_point);
438 putchar ('\n');
441 /* Return the root mountpoint of the filesystem on which FILE exists, in
442 malloced storage. FILE_STAT should be the result of stating FILE. */
443 static char *
444 find_mount_point (const char *file, const struct stat *file_stat)
446 struct saved_cwd cwd;
447 struct stat last_stat;
448 char *mp = 0; /* The malloced mount point path. */
450 if (save_cwd (&cwd))
451 return NULL;
453 if (S_ISDIR (file_stat->st_mode))
454 /* FILE is a directory, so just chdir there directly. */
456 last_stat = *file_stat;
457 if (chdir (file) < 0)
458 return NULL;
460 else
461 /* FILE is some other kind of file, we need to use its directory. */
463 char *dir = dir_name (file);
464 int rv = chdir (dir);
465 free (dir);
467 if (rv < 0)
468 return NULL;
470 if (stat (".", &last_stat) < 0)
471 goto done;
474 /* Now walk up FILE's parents until we find another filesystem or /,
475 chdiring as we go. LAST_STAT holds stat information for the last place
476 we visited. */
477 for (;;)
479 struct stat st;
480 if (stat ("..", &st) < 0)
481 goto done;
482 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
483 /* cwd is the mount point. */
484 break;
485 if (chdir ("..") < 0)
486 goto done;
487 last_stat = st;
490 /* Finally reached a mount point, see what it's called. */
491 mp = xgetcwd ();
493 done:
494 /* Restore the original cwd. */
496 int save_errno = errno;
497 if (restore_cwd (&cwd, 0, mp))
498 exit (EXIT_FAILURE); /* We're scrod. */
499 free_cwd (&cwd);
500 errno = save_errno;
503 return mp;
506 /* Identify the directory, if any, that device
507 DISK is mounted on, and show its disk usage.
508 STATP must be the result of `stat (DISK, STATP)'. */
510 static void
511 show_disk (const char *disk, const struct stat *statp)
513 struct mount_entry *me;
514 char *mount_point;
516 for (me = mount_list; me; me = me->me_next)
517 if (STREQ (disk, me->me_devname))
519 show_dev (me->me_devname, me->me_mountdir, me->me_type,
520 me->me_dummy, me->me_remote);
521 return;
524 mount_point = find_mount_point (disk, statp);
525 if (!mount_point)
527 error (0, errno, "%s", quote (disk));
528 exit_status = EXIT_FAILURE;
531 /* No filesystem is mounted on DISK. */
532 show_dev (disk, mount_point, NULL, 0, 0);
533 if (mount_point)
534 free (mount_point);
537 /* Figure out which device file or directory POINT is mounted on
538 and show its disk usage.
539 STATP must be the result of `stat (POINT, STATP)'. */
540 static void
541 show_point (const char *point, const struct stat *statp)
543 struct stat disk_stats;
544 struct mount_entry *me;
545 struct mount_entry *matching_dummy = NULL;
547 /* If POINT is an absolute path name, see if we can find the
548 mount point without performing any extra stat calls at all. */
549 if (*point == '/')
551 for (me = mount_list; me; me = me->me_next)
553 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs"))
555 /* Prefer non-dummy entries. */
556 if (! me->me_dummy)
557 goto show_me;
558 matching_dummy = me;
562 if (matching_dummy)
563 goto show_matching_dummy;
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. */
570 char *resolved = canonicalize_file_name (point);
571 ssize_t resolved_len = resolved ? strlen (resolved) : -1;
572 struct mount_entry *best_match = NULL;
574 if (1 <= resolved_len && resolved[0] == '/')
576 size_t best_match_len = 0;
578 for (me = mount_list; me; me = me->me_next)
579 if (! me->me_dummy)
581 size_t len = strlen (me->me_mountdir);
582 if (best_match_len < len && len <= resolved_len
583 && (len == 1 /* root file system */
584 || ((len == resolved_len || resolved[len] == '/')
585 && strncmp (me->me_mountdir, resolved, len) == 0)))
587 best_match = me;
588 best_match_len = len;
593 if (resolved)
594 free (resolved);
596 if (best_match && !STREQ (best_match->me_type, "lofs")
597 && stat (best_match->me_mountdir, &disk_stats) == 0
598 && disk_stats.st_dev == statp->st_dev)
600 me = best_match;
601 goto show_me;
605 for (me = mount_list; me; me = me->me_next)
607 if (me->me_dev == (dev_t) -1)
609 if (stat (me->me_mountdir, &disk_stats) == 0)
610 me->me_dev = disk_stats.st_dev;
611 else
613 error (0, errno, "%s", quote (me->me_mountdir));
614 exit_status = 1;
615 /* So we won't try and fail repeatedly. */
616 me->me_dev = (dev_t) -2;
620 if (statp->st_dev == me->me_dev)
622 /* Skip bogus mtab entries. */
623 if (stat (me->me_mountdir, &disk_stats) != 0
624 || disk_stats.st_dev != me->me_dev)
626 me->me_dev = (dev_t) -2;
627 continue;
630 /* Prefer non-dummy entries. */
631 if (! me->me_dummy)
632 goto show_me;
633 matching_dummy = me;
637 if (matching_dummy)
638 goto show_matching_dummy;
640 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
641 print as much info as we can; methods that require the device to be
642 present will fail at a later point. */
644 /* Find the actual mount point. */
645 char *mp = find_mount_point (point, statp);
646 if (mp)
648 show_dev (0, mp, 0, 0, 0);
649 free (mp);
651 else
652 error (0, errno, "%s", quote (point));
655 return;
657 show_matching_dummy:
658 me = matching_dummy;
659 show_me:
660 show_dev (me->me_devname, me->me_mountdir, me->me_type, me->me_dummy,
661 me->me_remote);
664 /* Determine what kind of node PATH is and show the disk usage
665 for it. STATP is the results of `stat' on PATH. */
667 static void
668 show_entry (const char *path, const struct stat *statp)
670 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
671 show_disk (path, statp);
672 else
673 show_point (path, statp);
676 /* Show all mounted filesystems, except perhaps those that are of
677 an unselected type or are empty. */
679 static void
680 show_all_entries (void)
682 struct mount_entry *me;
684 for (me = mount_list; me; me = me->me_next)
685 show_dev (me->me_devname, me->me_mountdir, me->me_type,
686 me->me_dummy, me->me_remote);
689 /* Add FSTYPE to the list of filesystem types to display. */
691 static void
692 add_fs_type (const char *fstype)
694 struct fs_type_list *fsp;
696 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
697 fsp->fs_name = (char *) fstype;
698 fsp->fs_next = fs_select_list;
699 fs_select_list = fsp;
702 /* Add FSTYPE to the list of filesystem types to be omitted. */
704 static void
705 add_excluded_fs_type (const char *fstype)
707 struct fs_type_list *fsp;
709 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
710 fsp->fs_name = (char *) fstype;
711 fsp->fs_next = fs_exclude_list;
712 fs_exclude_list = fsp;
715 void
716 usage (int status)
718 if (status != 0)
719 fprintf (stderr, _("Try `%s --help' for more information.\n"),
720 program_name);
721 else
723 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
724 fputs (_("\
725 Show information about the filesystem on which each FILE resides,\n\
726 or all filesystems by default.\n\
728 "), stdout);
729 fputs (_("\
730 Mandatory arguments to long options are mandatory for short options too.\n\
731 "), stdout);
732 fputs (_("\
733 -a, --all include filesystems having 0 blocks\n\
734 -B, --block-size=SIZE use SIZE-byte blocks\n\
735 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
736 -H, --si likewise, but use powers of 1000 not 1024\n\
737 "), stdout);
738 fputs (_("\
739 -i, --inodes list inode information instead of block usage\n\
740 -k like --block-size=1K\n\
741 -l, --local limit listing to local filesystems\n\
742 --no-sync do not invoke sync before getting usage info (default)\n\
743 "), stdout);
744 fputs (_("\
745 -P, --portability use the POSIX output format\n\
746 --sync invoke sync before getting usage info\n\
747 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
748 -T, --print-type print filesystem type\n\
749 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
750 -v (ignored)\n\
751 "), stdout);
752 fputs (HELP_OPTION_DESCRIPTION, stdout);
753 fputs (VERSION_OPTION_DESCRIPTION, stdout);
754 fputs (_("\n\
755 SIZE may be (or may be an integer optionally followed by) one of following:\n\
756 kB 1000, K 1024, MB 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\
757 "), stdout);
758 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
760 exit (status);
764 main (int argc, char **argv)
766 int c;
767 struct stat *stats IF_LINT (= 0);
768 int n_valid_args = 0;
770 program_name = argv[0];
771 setlocale (LC_ALL, "");
772 bindtextdomain (PACKAGE, LOCALEDIR);
773 textdomain (PACKAGE);
775 atexit (close_stdout);
777 fs_select_list = NULL;
778 fs_exclude_list = NULL;
779 inode_format = 0;
780 show_all_fs = 0;
781 show_listed_fs = 0;
783 human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false,
784 &output_block_size);
786 print_type = 0;
787 posix_format = 0;
788 exit_status = 0;
790 while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
791 != -1)
793 switch (c)
795 case 0: /* Long option. */
796 break;
797 case 'a':
798 show_all_fs = 1;
799 break;
800 case 'B':
801 human_output_opts = human_options (optarg, true, &output_block_size);
802 break;
803 case 'i':
804 inode_format = 1;
805 break;
806 case 'h':
807 human_output_opts = human_autoscale | human_SI | human_base_1024;
808 output_block_size = 1;
809 break;
810 case 'H':
811 human_output_opts = human_autoscale | human_SI;
812 output_block_size = 1;
813 break;
814 case 'k':
815 human_output_opts = 0;
816 output_block_size = 1024;
817 break;
818 case 'l':
819 show_local_fs = 1;
820 break;
821 case 'm': /* obsolescent */
822 human_output_opts = 0;
823 output_block_size = 1024 * 1024;
824 break;
825 case 'T':
826 print_type = 1;
827 break;
828 case 'P':
829 posix_format = 1;
830 break;
831 case SYNC_OPTION:
832 require_sync = 1;
833 break;
834 case NO_SYNC_OPTION:
835 require_sync = 0;
836 break;
838 case 'F':
839 /* Accept -F as a synonym for -t for compatibility with Solaris. */
840 case 't':
841 add_fs_type (optarg);
842 break;
844 case 'v': /* For SysV compatibility. */
845 /* ignore */
846 break;
847 case 'x':
848 add_excluded_fs_type (optarg);
849 break;
851 case_GETOPT_HELP_CHAR;
852 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
854 default:
855 usage (EXIT_FAILURE);
859 /* Fail if the same file system type was both selected and excluded. */
861 int match = 0;
862 struct fs_type_list *fs_incl;
863 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
865 struct fs_type_list *fs_excl;
866 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
868 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
870 error (0, 0,
871 _("file system type %s both selected and excluded"),
872 quote (fs_incl->fs_name));
873 match = 1;
874 break;
878 if (match)
879 exit (EXIT_FAILURE);
883 int i;
885 /* stat all the given entries to make sure they get automounted,
886 if necessary, before reading the filesystem table. */
887 stats = (struct stat *)
888 xmalloc ((argc - optind) * sizeof (struct stat));
889 for (i = optind; i < argc; ++i)
891 if (stat (argv[i], &stats[i - optind]))
893 error (0, errno, "%s", quote (argv[i]));
894 exit_status = 1;
895 argv[i] = NULL;
897 else
899 ++n_valid_args;
904 mount_list =
905 read_filesystem_list ((fs_select_list != NULL
906 || fs_exclude_list != NULL
907 || print_type
908 || show_local_fs));
910 if (mount_list == NULL)
912 /* Couldn't read the table of mounted filesystems.
913 Fail if df was invoked with no file name arguments;
914 Otherwise, merely give a warning and proceed. */
915 const char *warning = (optind == argc ? "" : _("Warning: "));
916 int status = (optind == argc ? 1 : 0);
917 error (status, errno,
918 _("%scannot read table of mounted filesystems"), warning);
921 if (require_sync)
922 sync ();
924 if (optind == argc)
926 print_header ();
927 show_all_entries ();
929 else
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]);
944 exit (exit_status);