*** empty log message ***
[coreutils.git] / src / df.c
blobad49e4244a64ae75dccbafadba9604c834fd36da
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2000 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 #if HAVE_INTTYPES_H
28 # include <inttypes.h>
29 #endif
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <getopt.h>
33 #include <assert.h>
35 #include "system.h"
36 #include "dirname.h"
37 #include "error.h"
38 #include "fsusage.h"
39 #include "human.h"
40 #include "mountlist.h"
41 #include "path-concat.h"
42 #include "quote.h"
43 #include "save-cwd.h"
45 /* The official name of this program (e.g., no `g' prefix). */
46 #define PROGRAM_NAME "df"
48 #define AUTHORS \
49 "Torbjorn Granlund, David MacKenzie, Larry McVoy, and Paul Eggert"
51 void strip_trailing_slashes ();
52 char *xgetcwd ();
54 /* Name this program was run with. */
55 char *program_name;
57 /* If nonzero, show inode information. */
58 static int inode_format;
60 /* If nonzero, show even filesystems with zero size or
61 uninteresting types. */
62 static int show_all_fs;
64 /* If nonzero, show only local filesystems. */
65 static int show_local_fs;
67 /* If nonzero, output data for each filesystem corresponding to a
68 command line argument -- even if it's a dummy (automounter) entry. */
69 static int show_listed_fs;
71 /* If positive, the units to use when printing sizes;
72 if negative, the human-readable base. */
73 static int output_block_size;
75 /* If nonzero, use the POSIX output format. */
76 static int posix_format;
78 /* If nonzero, invoke the `sync' system call before getting any usage data.
79 Using this option can make df very slow, especially with many or very
80 busy disks. Note that this may make a difference on some systems --
81 SunOs4.1.3, for one. It is *not* necessary on Linux. */
82 static int require_sync = 0;
84 /* Nonzero if errors have occurred. */
85 static int exit_status;
87 /* A filesystem type to display. */
89 struct fs_type_list
91 char *fs_name;
92 struct fs_type_list *fs_next;
95 /* Linked list of filesystem types to display.
96 If `fs_select_list' is NULL, list all types.
97 This table is generated dynamically from command-line options,
98 rather than hardcoding into the program what it thinks are the
99 valid filesystem types; let the user specify any filesystem type
100 they want to, and if there are any filesystems of that type, they
101 will be shown.
103 Some filesystem types:
104 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
106 static struct fs_type_list *fs_select_list;
108 /* Linked list of filesystem types to omit.
109 If the list is empty, don't exclude any types. */
111 static struct fs_type_list *fs_exclude_list;
113 /* Linked list of mounted filesystems. */
114 static struct mount_entry *mount_list;
116 /* If nonzero, print filesystem type as well. */
117 static int print_type;
119 /* For long options that have no equivalent short option, use a
120 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
121 enum
123 SYNC_OPTION = CHAR_MAX + 1,
124 NO_SYNC_OPTION,
125 BLOCK_SIZE_OPTION
128 static struct option const long_options[] =
130 {"all", no_argument, NULL, 'a'},
131 {"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
132 {"inodes", no_argument, NULL, 'i'},
133 {"human-readable", no_argument, NULL, 'h'},
134 {"si", no_argument, NULL, 'H'},
135 {"kilobytes", no_argument, NULL, 'k'},
136 {"local", no_argument, NULL, 'l'},
137 {"megabytes", no_argument, NULL, 'm'},
138 {"portability", no_argument, NULL, 'P'},
139 {"print-type", no_argument, NULL, 'T'},
140 {"sync", no_argument, NULL, SYNC_OPTION},
141 {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
142 {"type", required_argument, NULL, 't'},
143 {"exclude-type", required_argument, NULL, 'x'},
144 {GETOPT_HELP_OPTION_DECL},
145 {GETOPT_VERSION_OPTION_DECL},
146 {NULL, 0, NULL, 0}
149 static void
150 print_header (void)
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 (output_block_size < 0)
162 printf (_(" Size Used Avail Use%%"));
163 else if (posix_format)
164 printf (_(" %4d-blocks Used Available Capacity"), output_block_size);
165 else
167 char buf[LONGEST_HUMAN_READABLE + 1];
168 char *p = human_readable (output_block_size, buf, 1, -1024);
170 /* Replace e.g. "1.0k" by "1k". */
171 size_t plen = strlen (p);
172 if (3 <= plen && strncmp (p + plen - 3, ".0", 2) == 0)
173 strcpy (p + plen - 3, p + plen - 1);
175 printf (_(" %4s-blocks Used Available Use%%"), p);
178 printf (_(" Mounted on\n"));
181 /* If FSTYPE is a type of filesystem that should be listed,
182 return nonzero, else zero. */
184 static int
185 selected_fstype (const char *fstype)
187 const struct fs_type_list *fsp;
189 if (fs_select_list == NULL || fstype == NULL)
190 return 1;
191 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
192 if (STREQ (fstype, fsp->fs_name))
193 return 1;
194 return 0;
197 /* If FSTYPE is a type of filesystem that should be omitted,
198 return nonzero, else zero. */
200 static int
201 excluded_fstype (const char *fstype)
203 const struct fs_type_list *fsp;
205 if (fs_exclude_list == NULL || fstype == NULL)
206 return 0;
207 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
208 if (STREQ (fstype, fsp->fs_name))
209 return 1;
210 return 0;
213 /* Like human_readable_inexact, except return "-" if the argument is -1,
214 and if NEGATIVE is 1 then N represents a negative number, expressed
215 in two's complement. */
216 static char const *
217 df_readable (int negative, uintmax_t n, char *buf,
218 int from_block_size, int t_output_block_size,
219 enum human_inexact_style s)
221 if (n == -1)
222 return "-";
223 else
225 char *p = human_readable_inexact (negative ? - n : n,
226 buf + negative, from_block_size,
227 t_output_block_size,
228 negative ? - s : s);
229 if (negative)
230 *--p = '-';
231 return p;
235 /* Display a space listing for the disk device with absolute path DISK.
236 If MOUNT_POINT is non-NULL, it is the path of the root of the
237 filesystem on DISK.
238 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
239 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
240 not be able to produce statistics in this case.
241 ME_DUMMY and ME_REMOTE are the mount entry flags. */
243 static void
244 show_dev (const char *disk, const char *mount_point, const char *fstype,
245 int me_dummy, int me_remote)
247 struct fs_usage fsu;
248 const char *stat_file;
249 char buf[3][LONGEST_HUMAN_READABLE + 2];
250 int width;
251 int use_width;
252 int input_units;
253 int output_units;
254 uintmax_t total;
255 uintmax_t available;
256 int negate_available;
257 uintmax_t available_to_root;
258 uintmax_t used;
259 int negate_used;
260 double pct = -1;
262 if (me_remote && show_local_fs)
263 return;
265 if (me_dummy && show_all_fs == 0 && !show_listed_fs)
266 return;
268 if (!selected_fstype (fstype) || excluded_fstype (fstype))
269 return;
271 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
272 program reports on the filesystem that the special file is on.
273 It would be better to report on the unmounted filesystem,
274 but statfs doesn't do that on most systems. */
275 stat_file = mount_point ? mount_point : disk;
277 if (get_fs_usage (stat_file, disk, &fsu))
279 error (0, errno, "%s", quote (stat_file));
280 exit_status = 1;
281 return;
284 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
285 return;
287 if (! disk)
288 disk = "-"; /* unknown */
289 if (! fstype)
290 fstype = "-"; /* unknown */
292 /* df.c reserved 5 positions for fstype,
293 but that does not suffice for type iso9660 */
294 if (print_type)
296 int disk_name_len = (int) strlen (disk);
297 int fstype_len = (int) strlen (fstype);
298 if (disk_name_len + fstype_len + 2 < 20)
299 printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
300 else if (!posix_format)
301 printf ("%s\n%18s ", disk, fstype);
302 else
303 printf ("%s %s", disk, fstype);
305 else
307 if ((int) strlen (disk) > 20 && !posix_format)
308 printf ("%s\n%20s", disk, "");
309 else
310 printf ("%-20s", disk);
313 if (inode_format)
315 width = 7;
316 use_width = 5;
317 input_units = 1;
318 output_units = output_block_size < 0 ? output_block_size : 1;
319 total = fsu.fsu_files;
320 available = fsu.fsu_ffree;
321 negate_available = 0;
322 available_to_root = available;
324 else
326 width = output_block_size < 0 ? 5 : 9;
327 use_width = posix_format ? 8 : 4;
328 input_units = fsu.fsu_blocksize;
329 output_units = output_block_size;
330 total = fsu.fsu_blocks;
331 available = fsu.fsu_bavail;
332 negate_available = fsu.fsu_bavail_top_bit_set;
333 available_to_root = fsu.fsu_bfree;
336 used = -1;
337 negate_used = 0;
338 if (total != -1 && available_to_root != -1)
340 used = total - available_to_root;
341 if (total < available_to_root)
343 negate_used = 1;
344 used = - used;
348 printf (" %*s %*s %*s ",
349 width, df_readable (0, total,
350 buf[0], input_units, output_units,
351 (posix_format
352 ? human_ceiling
353 : human_round_to_even)),
354 width, df_readable (negate_used, used,
355 buf[1], input_units, output_units,
356 human_ceiling),
357 width, df_readable (negate_available, available,
358 buf[2], input_units, output_units,
359 posix_format ? human_ceiling : human_floor));
361 if (used == -1 || available == -1)
363 else if (!negate_used
364 && used <= TYPE_MAXIMUM (uintmax_t) / 100
365 && used + available != 0
366 && (used + available < used) == negate_available)
368 uintmax_t u100 = used * 100;
369 uintmax_t nonroot_total = used + available;
370 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
372 else
374 /* The calculation cannot be done easily with integer
375 arithmetic. Fall back on floating point. This can suffer
376 from minor rounding errors, but doing it exactly requires
377 multiple precision arithmetic, and it's not worth the
378 aggravation. */
379 double u = negate_used ? - (double) - used : used;
380 double a = negate_available ? - (double) - available : available;
381 double nonroot_total = u + a;
382 if (nonroot_total)
384 double ipct;
385 pct = u * 100 / nonroot_total;
386 ipct = (long) pct;
388 /* Like `pct = ceil (dpct);', but avoid ceil so that
389 the math library needn't be linked. */
390 if (ipct - 1 < pct && pct <= ipct + 1)
391 pct = ipct + (ipct < pct);
395 if (0 <= pct)
396 printf ("%*.0f%%", use_width - 1, pct);
397 else
398 printf ("%*s", use_width, "- ");
400 if (mount_point)
402 #ifdef HIDE_AUTOMOUNT_PREFIX
403 /* Don't print the first directory name in MOUNT_POINT if it's an
404 artifact of an automounter. This is a bit too aggressive to be
405 the default. */
406 if (strncmp ("/auto/", mount_point, 6) == 0)
407 mount_point += 5;
408 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
409 mount_point += 8;
410 #endif
411 printf (" %s", mount_point);
413 putchar ('\n');
416 /* Identify the directory, if any, that device
417 DISK is mounted on, and show its disk usage. */
419 static void
420 show_disk (const char *disk)
422 struct mount_entry *me;
424 for (me = mount_list; me; me = me->me_next)
425 if (STREQ (disk, me->me_devname))
427 show_dev (me->me_devname, me->me_mountdir, me->me_type,
428 me->me_dummy, me->me_remote);
429 return;
431 /* No filesystem is mounted on DISK. */
432 show_dev (disk, (char *) NULL, (char *) NULL, 0, 0);
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 static char *
438 find_mount_point (const char *file, const struct stat *file_stat)
440 struct saved_cwd cwd;
441 struct stat last_stat;
442 char *mp = 0; /* The malloced mount point path. */
444 if (save_cwd (&cwd))
445 return NULL;
447 if (S_ISDIR (file_stat->st_mode))
448 /* FILE is a directory, so just chdir there directly. */
450 last_stat = *file_stat;
451 if (chdir (file) < 0)
452 return NULL;
454 else
455 /* FILE is some other kind of file, we need to use its directory. */
457 int rv;
458 char *tmp = xstrdup (file);
459 char *dir;
461 strip_trailing_slashes (tmp);
462 dir = dir_name (tmp);
463 free (tmp);
464 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 (1); /* We're scrod. */
499 free_cwd (&cwd);
500 errno = save_errno;
503 return mp;
506 /* Figure out which device file or directory POINT is mounted on
507 and show its disk usage.
508 STATP is the results of `stat' on POINT. */
509 static void
510 show_point (const char *point, const struct stat *statp)
512 struct stat disk_stats;
513 struct mount_entry *me;
514 struct mount_entry *matching_dummy = NULL;
515 char *needs_freeing = NULL;
517 /* If POINT is an absolute path name, see if we can find the
518 mount point without performing any extra stat calls at all. */
519 if (*point == '/')
521 for (me = mount_list; me; me = me->me_next)
523 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs"))
525 /* Prefer non-dummy entries. */
526 if (! me->me_dummy)
527 goto show_me;
528 matching_dummy = me;
532 if (matching_dummy)
533 goto show_matching_dummy;
536 #if HAVE_REALPATH || HAVE_RESOLVEPATH
537 /* Calculate the real absolute path for POINT, and use that to find
538 the mount point. This avoids statting unavailable mount points,
539 which can hang df. */
541 char const *abspoint = point;
542 char *resolved;
543 ssize_t resolved_len;
544 struct mount_entry *best_match = NULL;
546 # if HAVE_RESOLVEPATH
547 /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
548 relative names into absolute ones, so prepend the working
549 directory if the path is not absolute. */
551 if (*point != '/')
553 static char const *wd;
555 if (! wd)
557 struct stat pwd_stats;
558 struct stat dot_stats;
560 /* Use PWD if it is correct; this is usually cheaper than
561 xgetcwd. */
562 wd = getenv ("PWD");
563 if (! (wd
564 && stat (wd, &pwd_stats) == 0
565 && stat (".", &dot_stats) == 0
566 && SAME_INODE (pwd_stats, dot_stats)))
567 wd = xgetcwd ();
570 if (wd)
572 needs_freeing = path_concat (wd, point, NULL);
573 if (needs_freeing)
574 abspoint = needs_freeing;
577 # endif
579 # if HAVE_RESOLVEPATH
581 size_t resolved_size = strlen (abspoint);
584 resolved_size = 2 * resolved_size + 1;
585 resolved = alloca (resolved_size);
586 resolved_len = resolvepath (abspoint, resolved, resolved_size);
588 while (resolved_len == resolved_size);
590 # else
591 resolved = alloca (PATH_MAX + 1);
592 resolved = (char *) realpath (abspoint, resolved);
593 resolved_len = resolved ? strlen (resolved) : -1;
594 # endif
596 if (1 <= resolved_len && resolved[0] == '/')
598 size_t best_match_len = 0;
600 for (me = mount_list; me; me = me->me_next)
601 if (! me->me_dummy)
603 size_t len = strlen (me->me_mountdir);
604 if (best_match_len < len && len <= resolved_len
605 && (len == 1 /* root file system */
606 || ((len == resolved_len || resolved[len] == '/')
607 && strncmp (me->me_mountdir, resolved, len) == 0)))
609 best_match = me;
610 best_match_len = len;
615 if (best_match && !STREQ (best_match->me_type, "lofs")
616 && stat (best_match->me_mountdir, &disk_stats) == 0
617 && disk_stats.st_dev == statp->st_dev)
619 me = best_match;
620 goto show_me;
623 #endif
625 for (me = mount_list; me; me = me->me_next)
627 if (me->me_dev == (dev_t) -1)
629 if (stat (me->me_mountdir, &disk_stats) == 0)
630 me->me_dev = disk_stats.st_dev;
631 else
633 error (0, errno, "%s", quote (me->me_mountdir));
634 exit_status = 1;
635 /* So we won't try and fail repeatedly. */
636 me->me_dev = (dev_t) -2;
640 if (statp->st_dev == me->me_dev)
642 /* Skip bogus mtab entries. */
643 if (stat (me->me_mountdir, &disk_stats) != 0
644 || disk_stats.st_dev != me->me_dev)
646 me->me_dev = (dev_t) -2;
647 continue;
650 /* Prefer non-dummy entries. */
651 if (! me->me_dummy)
652 goto show_me;
653 matching_dummy = me;
657 if (matching_dummy)
658 goto show_matching_dummy;
660 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
661 print as much info as we can; methods that require the device to be
662 present will fail at a later point. */
664 /* Find the actual mount point. */
665 char *mp = find_mount_point (point, statp);
666 if (mp)
668 show_dev (0, mp, 0, 0, 0);
669 free (mp);
671 else
672 error (0, errno, "%s", quote (point));
675 goto free_then_return;
677 show_matching_dummy:
678 me = matching_dummy;
679 show_me:
680 show_dev (me->me_devname, me->me_mountdir, me->me_type, me->me_dummy,
681 me->me_remote);
682 free_then_return:
683 if (needs_freeing)
684 free (needs_freeing);
687 /* Determine what kind of node PATH is and show the disk usage
688 for it. STATP is the results of `stat' on PATH. */
690 static void
691 show_entry (const char *path, const struct stat *statp)
693 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
694 show_disk (path);
695 else
696 show_point (path, statp);
699 /* Show all mounted filesystems, except perhaps those that are of
700 an unselected type or are empty. */
702 static void
703 show_all_entries (void)
705 struct mount_entry *me;
707 for (me = mount_list; me; me = me->me_next)
708 show_dev (me->me_devname, me->me_mountdir, me->me_type,
709 me->me_dummy, me->me_remote);
712 /* Add FSTYPE to the list of filesystem types to display. */
714 static void
715 add_fs_type (const char *fstype)
717 struct fs_type_list *fsp;
719 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
720 fsp->fs_name = (char *) fstype;
721 fsp->fs_next = fs_select_list;
722 fs_select_list = fsp;
725 /* Add FSTYPE to the list of filesystem types to be omitted. */
727 static void
728 add_excluded_fs_type (const char *fstype)
730 struct fs_type_list *fsp;
732 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
733 fsp->fs_name = (char *) fstype;
734 fsp->fs_next = fs_exclude_list;
735 fs_exclude_list = fsp;
738 void
739 usage (int status)
741 if (status != 0)
742 fprintf (stderr, _("Try `%s --help' for more information.\n"),
743 program_name);
744 else
746 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
747 printf (_("\
748 Show information about the filesystem on which each FILE resides,\n\
749 or all filesystems by default.\n\
751 -a, --all include filesystems having 0 blocks\n\
752 --block-size=SIZE use SIZE-byte blocks\n\
753 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
754 -H, --si likewise, but use powers of 1000 not 1024\n\
755 -i, --inodes list inode information instead of block usage\n\
756 -k, --kilobytes like --block-size=1024\n\
757 -l, --local limit listing to local filesystems\n\
758 -m, --megabytes like --block-size=1048576\n\
759 --no-sync do not invoke sync before getting usage info (default)\n\
760 -P, --portability use the POSIX output format\n\
761 --sync invoke sync before getting usage info\n\
762 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
763 -T, --print-type print filesystem type\n\
764 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
765 -v (ignored)\n\
766 --help display this help and exit\n\
767 --version output version information and exit\n\
768 "));
769 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
771 exit (status);
775 main (int argc, char **argv)
777 int c;
778 struct stat *stats IF_LINT (= 0);
779 int n_valid_args = 0;
781 program_name = argv[0];
782 setlocale (LC_ALL, "");
783 bindtextdomain (PACKAGE, LOCALEDIR);
784 textdomain (PACKAGE);
786 atexit (close_stdout);
788 fs_select_list = NULL;
789 fs_exclude_list = NULL;
790 inode_format = 0;
791 show_all_fs = 0;
792 show_listed_fs = 0;
794 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
796 print_type = 0;
797 posix_format = 0;
798 exit_status = 0;
800 while ((c = getopt_long (argc, argv, "aiF:hHklmPTt:vx:", long_options, NULL))
801 != -1)
803 switch (c)
805 case 0: /* Long option. */
806 break;
807 case 'a':
808 show_all_fs = 1;
809 break;
810 case 'i':
811 inode_format = 1;
812 break;
813 case 'h':
814 output_block_size = -1024;
815 break;
816 case 'H':
817 output_block_size = -1000;
818 break;
819 case 'k':
820 output_block_size = 1024;
821 break;
822 case 'l':
823 show_local_fs = 1;
824 break;
825 case 'm':
826 output_block_size = 1024 * 1024;
827 break;
828 case 'T':
829 print_type = 1;
830 break;
831 case 'P':
832 posix_format = 1;
833 break;
834 case SYNC_OPTION:
835 require_sync = 1;
836 break;
837 case NO_SYNC_OPTION:
838 require_sync = 0;
839 break;
841 case BLOCK_SIZE_OPTION:
842 human_block_size (optarg, 1, &output_block_size);
843 break;
845 case 'F':
846 /* Accept -F as a synonym for -t for compatibility with Solaris. */
847 case 't':
848 add_fs_type (optarg);
849 break;
851 case 'v': /* For SysV compatibility. */
852 /* ignore */
853 break;
854 case 'x':
855 add_excluded_fs_type (optarg);
856 break;
858 case_GETOPT_HELP_CHAR;
859 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
861 default:
862 usage (1);
866 /* Fail if the same file system type was both selected and excluded. */
868 int match = 0;
869 struct fs_type_list *fs_incl;
870 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
872 struct fs_type_list *fs_excl;
873 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
875 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
877 error (0, 0,
878 _("file system type %s both selected and excluded"),
879 quote (fs_incl->fs_name));
880 match = 1;
881 break;
885 if (match)
886 exit (1);
890 int i;
892 /* stat all the given entries to make sure they get automounted,
893 if necessary, before reading the filesystem table. */
894 stats = (struct stat *)
895 xmalloc ((argc - optind) * sizeof (struct stat));
896 for (i = optind; i < argc; ++i)
898 if (stat (argv[i], &stats[i - optind]))
900 error (0, errno, "%s", quote (argv[i]));
901 exit_status = 1;
902 argv[i] = NULL;
904 else
906 ++n_valid_args;
911 mount_list =
912 read_filesystem_list ((fs_select_list != NULL
913 || fs_exclude_list != NULL
914 || print_type
915 || show_local_fs));
917 if (mount_list == NULL)
919 /* Couldn't read the table of mounted filesystems.
920 Fail if df was invoked with no file name arguments;
921 Otherwise, merely give a warning and proceed. */
922 const char *warning = (optind == argc ? "" : _("Warning: "));
923 int status = (optind == argc ? 1 : 0);
924 error (status, errno,
925 _("%scannot read table of mounted filesystems"), warning);
928 if (require_sync)
929 sync ();
931 if (optind == argc)
933 print_header ();
934 show_all_entries ();
936 else
938 int i;
940 /* Display explicitly requested empty filesystems. */
941 show_listed_fs = 1;
943 if (n_valid_args > 0)
944 print_header ();
946 for (i = optind; i < argc; ++i)
947 if (argv[i])
948 show_entry (argv[i], &stats[i - optind]);
951 exit (exit_status);