*** empty log message ***
[coreutils.git] / src / df.c
blob05364c82def7edf22d5803155ea69a9c68027d0d
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2002 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 char *xgetcwd ();
53 /* Name this program was run with. */
54 char *program_name;
56 /* If nonzero, show inode information. */
57 static int inode_format;
59 /* If nonzero, show even filesystems with zero size or
60 uninteresting types. */
61 static int show_all_fs;
63 /* If nonzero, show only local filesystems. */
64 static int show_local_fs;
66 /* If nonzero, output data for each filesystem corresponding to a
67 command line argument -- even if it's a dummy (automounter) entry. */
68 static int show_listed_fs;
70 /* If positive, the units to use when printing sizes;
71 if negative, the human-readable base. */
72 static int 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 printf (_("Filesystem "));
152 if (print_type)
153 printf (_(" Type"));
154 else
155 printf (" ");
157 if (inode_format)
158 printf (_(" Inodes IUsed IFree IUse%%"));
159 else if (output_block_size < 0)
161 if (output_block_size == -1000)
162 printf (_(" Size Used Avail Use%%"));
163 else
164 printf (_(" Size Used Avail Use%%"));
166 else if (posix_format)
167 printf (_(" %4d-blocks Used Available Capacity"), output_block_size);
168 else
170 char buf[LONGEST_HUMAN_READABLE + 1];
171 char *p = human_readable (output_block_size, buf, 1, -1024);
173 /* Replace e.g. "1.0K" by "1K". */
174 size_t plen = strlen (p);
175 if (3 <= plen && strncmp (p + plen - 3, ".0", 2) == 0)
176 strcpy (p + plen - 3, p + plen - 1);
178 printf (_(" %4s-blocks Used Available Use%%"), p);
181 printf (_(" Mounted on\n"));
184 /* If FSTYPE is a type of filesystem that should be listed,
185 return nonzero, else zero. */
187 static int
188 selected_fstype (const char *fstype)
190 const struct fs_type_list *fsp;
192 if (fs_select_list == NULL || fstype == NULL)
193 return 1;
194 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
195 if (STREQ (fstype, fsp->fs_name))
196 return 1;
197 return 0;
200 /* If FSTYPE is a type of filesystem that should be omitted,
201 return nonzero, else zero. */
203 static int
204 excluded_fstype (const char *fstype)
206 const struct fs_type_list *fsp;
208 if (fs_exclude_list == NULL || fstype == NULL)
209 return 0;
210 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
211 if (STREQ (fstype, fsp->fs_name))
212 return 1;
213 return 0;
216 /* Like human_readable_inexact with a human_ceiling
217 human_inexact_style, except return "-" if the argument is -1, and
218 if NEGATIVE is 1 then N represents a negative number, expressed in
219 two's complement. */
221 static char const *
222 df_readable (int negative, uintmax_t n, char *buf,
223 int from_block_size, int t_output_block_size)
225 if (n == -1)
226 return "-";
227 else
229 char *p = human_readable_inexact (negative ? - n : n,
230 buf + negative, from_block_size,
231 t_output_block_size,
232 human_ceiling);
233 if (negative)
234 *--p = '-';
235 return p;
239 /* Display a space listing for the disk device with absolute path DISK.
240 If MOUNT_POINT is non-NULL, it is the path of the root of the
241 filesystem on DISK.
242 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
243 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
244 not be able to produce statistics in this case.
245 ME_DUMMY and ME_REMOTE are the mount entry flags. */
247 static void
248 show_dev (const char *disk, const char *mount_point, const char *fstype,
249 int me_dummy, int me_remote)
251 struct fs_usage fsu;
252 const char *stat_file;
253 char buf[3][LONGEST_HUMAN_READABLE + 2];
254 int width;
255 int use_width;
256 int input_units;
257 int output_units;
258 uintmax_t total;
259 uintmax_t available;
260 int negate_available;
261 uintmax_t available_to_root;
262 uintmax_t used;
263 int negate_used;
264 double pct = -1;
266 if (me_remote && show_local_fs)
267 return;
269 if (me_dummy && show_all_fs == 0 && !show_listed_fs)
270 return;
272 if (!selected_fstype (fstype) || excluded_fstype (fstype))
273 return;
275 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
276 program reports on the filesystem that the special file is on.
277 It would be better to report on the unmounted filesystem,
278 but statfs doesn't do that on most systems. */
279 stat_file = mount_point ? mount_point : disk;
281 if (get_fs_usage (stat_file, disk, &fsu))
283 error (0, errno, "%s", quote (stat_file));
284 exit_status = 1;
285 return;
288 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
289 return;
291 if (! disk)
292 disk = "-"; /* unknown */
293 if (! fstype)
294 fstype = "-"; /* unknown */
296 /* df.c reserved 5 positions for fstype,
297 but that does not suffice for type iso9660 */
298 if (print_type)
300 int disk_name_len = (int) strlen (disk);
301 int fstype_len = (int) strlen (fstype);
302 if (disk_name_len + fstype_len + 2 < 20)
303 printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
304 else if (!posix_format)
305 printf ("%s\n%18s ", disk, fstype);
306 else
307 printf ("%s %s", disk, fstype);
309 else
311 if ((int) strlen (disk) > 20 && !posix_format)
312 printf ("%s\n%20s", disk, "");
313 else
314 printf ("%-20s", disk);
317 if (inode_format)
319 width = 7;
320 use_width = 5;
321 input_units = 1;
322 output_units = output_block_size < 0 ? output_block_size : 1;
323 total = fsu.fsu_files;
324 available = fsu.fsu_ffree;
325 negate_available = 0;
326 available_to_root = available;
328 else
330 width = output_block_size < 0 ? 5 + (output_block_size == -1000) : 9;
331 use_width = (posix_format && 0 <= output_block_size) ? 8 : 4;
332 input_units = fsu.fsu_blocksize;
333 output_units = output_block_size;
334 total = fsu.fsu_blocks;
335 available = fsu.fsu_bavail;
336 negate_available = fsu.fsu_bavail_top_bit_set;
337 available_to_root = fsu.fsu_bfree;
340 used = -1;
341 negate_used = 0;
342 if (total != -1 && available_to_root != -1)
344 used = total - available_to_root;
345 if (total < available_to_root)
347 negate_used = 1;
348 used = - used;
352 printf (" %*s %*s %*s ",
353 width, df_readable (0, total,
354 buf[0], input_units, output_units),
355 width, df_readable (negate_used, used,
356 buf[1], input_units, output_units),
357 width, df_readable (negate_available, available,
358 buf[2], input_units, output_units));
360 if (used == -1 || available == -1)
362 else if (!negate_used
363 && used <= TYPE_MAXIMUM (uintmax_t) / 100
364 && used + available != 0
365 && (used + available < used) == negate_available)
367 uintmax_t u100 = used * 100;
368 uintmax_t nonroot_total = used + available;
369 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
371 else
373 /* The calculation cannot be done easily with integer
374 arithmetic. Fall back on floating point. This can suffer
375 from minor rounding errors, but doing it exactly requires
376 multiple precision arithmetic, and it's not worth the
377 aggravation. */
378 double u = negate_used ? - (double) - used : used;
379 double a = negate_available ? - (double) - available : available;
380 double nonroot_total = u + a;
381 if (nonroot_total)
383 double ipct;
384 pct = u * 100 / nonroot_total;
385 ipct = (long) pct;
387 /* Like `pct = ceil (dpct);', but avoid ceil so that
388 the math library needn't be linked. */
389 if (ipct - 1 < pct && pct <= ipct + 1)
390 pct = ipct + (ipct < pct);
394 if (0 <= pct)
395 printf ("%*.0f%%", use_width - 1, pct);
396 else
397 printf ("%*s", use_width, "- ");
399 if (mount_point)
401 #ifdef HIDE_AUTOMOUNT_PREFIX
402 /* Don't print the first directory name in MOUNT_POINT if it's an
403 artifact of an automounter. This is a bit too aggressive to be
404 the default. */
405 if (strncmp ("/auto/", mount_point, 6) == 0)
406 mount_point += 5;
407 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
408 mount_point += 8;
409 #endif
410 printf (" %s", mount_point);
412 putchar ('\n');
415 /* Identify the directory, if any, that device
416 DISK is mounted on, and show its disk usage. */
418 static void
419 show_disk (const char *disk)
421 struct mount_entry *me;
423 for (me = mount_list; me; me = me->me_next)
424 if (STREQ (disk, me->me_devname))
426 show_dev (me->me_devname, me->me_mountdir, me->me_type,
427 me->me_dummy, me->me_remote);
428 return;
430 /* No filesystem is mounted on DISK. */
431 show_dev (disk, (char *) NULL, (char *) NULL, 0, 0);
434 /* Return the root mountpoint of the filesystem on which FILE exists, in
435 malloced storage. FILE_STAT should be the result of stating FILE. */
436 static char *
437 find_mount_point (const char *file, const struct stat *file_stat)
439 struct saved_cwd cwd;
440 struct stat last_stat;
441 char *mp = 0; /* The malloced mount point path. */
443 if (save_cwd (&cwd))
444 return NULL;
446 if (S_ISDIR (file_stat->st_mode))
447 /* FILE is a directory, so just chdir there directly. */
449 last_stat = *file_stat;
450 if (chdir (file) < 0)
451 return NULL;
453 else
454 /* FILE is some other kind of file, we need to use its directory. */
456 char *dir = dir_name (file);
457 int rv = chdir (dir);
458 free (dir);
460 if (rv < 0)
461 return NULL;
463 if (stat (".", &last_stat) < 0)
464 goto done;
467 /* Now walk up FILE's parents until we find another filesystem or /,
468 chdiring as we go. LAST_STAT holds stat information for the last place
469 we visited. */
470 for (;;)
472 struct stat st;
473 if (stat ("..", &st) < 0)
474 goto done;
475 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
476 /* cwd is the mount point. */
477 break;
478 if (chdir ("..") < 0)
479 goto done;
480 last_stat = st;
483 /* Finally reached a mount point, see what it's called. */
484 mp = xgetcwd ();
486 done:
487 /* Restore the original cwd. */
489 int save_errno = errno;
490 if (restore_cwd (&cwd, 0, mp))
491 exit (1); /* We're scrod. */
492 free_cwd (&cwd);
493 errno = save_errno;
496 return mp;
499 /* Figure out which device file or directory POINT is mounted on
500 and show its disk usage.
501 STATP is the results of `stat' on POINT. */
502 static void
503 show_point (const char *point, const struct stat *statp)
505 struct stat disk_stats;
506 struct mount_entry *me;
507 struct mount_entry *matching_dummy = NULL;
508 char *needs_freeing = NULL;
510 /* If POINT is an absolute path name, see if we can find the
511 mount point without performing any extra stat calls at all. */
512 if (*point == '/')
514 for (me = mount_list; me; me = me->me_next)
516 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs"))
518 /* Prefer non-dummy entries. */
519 if (! me->me_dummy)
520 goto show_me;
521 matching_dummy = me;
525 if (matching_dummy)
526 goto show_matching_dummy;
529 /* Ideally, the following mess of #if'd code would be in a separate
530 file, and there'd be a single function call here. FIXME, someday. */
532 #if HAVE_REALPATH || HAVE_RESOLVEPATH || HAVE_CANONICALIZE_FILE_NAME
533 /* Calculate the real absolute path for POINT, and use that to find
534 the mount point. This avoids statting unavailable mount points,
535 which can hang df. */
537 char const *abspoint = point;
538 char *resolved;
539 ssize_t resolved_len;
540 struct mount_entry *best_match = NULL;
542 # if HAVE_CANONICALIZE_FILE_NAME
543 resolved = canonicalize_file_name (abspoint);
544 resolved_len = resolved ? strlen (resolved) : -1;
545 # else
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);
582 while (1)
584 resolved_size = 2 * resolved_size + 1;
585 resolved = xmalloc (resolved_size);
586 resolved_len = resolvepath (abspoint, resolved, resolved_size);
587 if (resolved_len < resolved_size)
588 break;
589 free (resolved);
592 # else
593 /* Use realpath only as a last resort.
594 It provides a very poor interface. */
595 resolved = xmalloc (PATH_MAX + 1);
596 resolved = (char *) realpath (abspoint, resolved);
597 resolved_len = resolved ? strlen (resolved) : -1;
598 # endif
599 # endif
601 if (1 <= resolved_len && resolved[0] == '/')
603 size_t best_match_len = 0;
605 for (me = mount_list; me; me = me->me_next)
606 if (! me->me_dummy)
608 size_t len = strlen (me->me_mountdir);
609 if (best_match_len < len && len <= resolved_len
610 && (len == 1 /* root file system */
611 || ((len == resolved_len || resolved[len] == '/')
612 && strncmp (me->me_mountdir, resolved, len) == 0)))
614 best_match = me;
615 best_match_len = len;
620 if (resolved)
621 free (resolved);
623 if (best_match && !STREQ (best_match->me_type, "lofs")
624 && stat (best_match->me_mountdir, &disk_stats) == 0
625 && disk_stats.st_dev == statp->st_dev)
627 me = best_match;
628 goto show_me;
631 #endif
633 for (me = mount_list; me; me = me->me_next)
635 if (me->me_dev == (dev_t) -1)
637 if (stat (me->me_mountdir, &disk_stats) == 0)
638 me->me_dev = disk_stats.st_dev;
639 else
641 error (0, errno, "%s", quote (me->me_mountdir));
642 exit_status = 1;
643 /* So we won't try and fail repeatedly. */
644 me->me_dev = (dev_t) -2;
648 if (statp->st_dev == me->me_dev)
650 /* Skip bogus mtab entries. */
651 if (stat (me->me_mountdir, &disk_stats) != 0
652 || disk_stats.st_dev != me->me_dev)
654 me->me_dev = (dev_t) -2;
655 continue;
658 /* Prefer non-dummy entries. */
659 if (! me->me_dummy)
660 goto show_me;
661 matching_dummy = me;
665 if (matching_dummy)
666 goto show_matching_dummy;
668 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
669 print as much info as we can; methods that require the device to be
670 present will fail at a later point. */
672 /* Find the actual mount point. */
673 char *mp = find_mount_point (point, statp);
674 if (mp)
676 show_dev (0, mp, 0, 0, 0);
677 free (mp);
679 else
680 error (0, errno, "%s", quote (point));
683 goto free_then_return;
685 show_matching_dummy:
686 me = matching_dummy;
687 show_me:
688 show_dev (me->me_devname, me->me_mountdir, me->me_type, me->me_dummy,
689 me->me_remote);
690 free_then_return:
691 if (needs_freeing)
692 free (needs_freeing);
695 /* Determine what kind of node PATH is and show the disk usage
696 for it. STATP is the results of `stat' on PATH. */
698 static void
699 show_entry (const char *path, const struct stat *statp)
701 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
702 show_disk (path);
703 else
704 show_point (path, statp);
707 /* Show all mounted filesystems, except perhaps those that are of
708 an unselected type or are empty. */
710 static void
711 show_all_entries (void)
713 struct mount_entry *me;
715 for (me = mount_list; me; me = me->me_next)
716 show_dev (me->me_devname, me->me_mountdir, me->me_type,
717 me->me_dummy, me->me_remote);
720 /* Add FSTYPE to the list of filesystem types to display. */
722 static void
723 add_fs_type (const char *fstype)
725 struct fs_type_list *fsp;
727 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
728 fsp->fs_name = (char *) fstype;
729 fsp->fs_next = fs_select_list;
730 fs_select_list = fsp;
733 /* Add FSTYPE to the list of filesystem types to be omitted. */
735 static void
736 add_excluded_fs_type (const char *fstype)
738 struct fs_type_list *fsp;
740 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
741 fsp->fs_name = (char *) fstype;
742 fsp->fs_next = fs_exclude_list;
743 fs_exclude_list = fsp;
746 void
747 usage (int status)
749 if (status != 0)
750 fprintf (stderr, _("Try `%s --help' for more information.\n"),
751 program_name);
752 else
754 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
755 fputs (_("\
756 Show information about the filesystem on which each FILE resides,\n\
757 or all filesystems by default.\n\
759 "), stdout);
760 fputs (_("\
761 Mandatory arguments to long options are mandatory for short options too.\n\
762 "), stdout);
763 fputs (_("\
764 -a, --all include filesystems having 0 blocks\n\
765 -B, --block-size=SIZE use SIZE-byte blocks\n\
766 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
767 -H, --si likewise, but use powers of 1000 not 1024\n\
768 "), stdout);
769 fputs (_("\
770 -i, --inodes list inode information instead of block usage\n\
771 -k like --block-size=1K\n\
772 -l, --local limit listing to local filesystems\n\
773 --no-sync do not invoke sync before getting usage info (default)\n\
774 "), stdout);
775 fputs (_("\
776 -P, --portability use the POSIX output format\n\
777 --sync invoke sync before getting usage info\n\
778 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
779 -T, --print-type print filesystem type\n\
780 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
781 -v (ignored)\n\
782 "), stdout);
783 fputs (HELP_OPTION_DESCRIPTION, stdout);
784 fputs (VERSION_OPTION_DESCRIPTION, stdout);
785 fputs (_("\n\
786 SIZE may be (or may be an integer optionally followed by) one of following:\n\
787 kB 1000, K 1024, MB 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\
788 "), stdout);
789 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
791 exit (status);
795 main (int argc, char **argv)
797 int c;
798 struct stat *stats IF_LINT (= 0);
799 int n_valid_args = 0;
801 program_name = argv[0];
802 setlocale (LC_ALL, "");
803 bindtextdomain (PACKAGE, LOCALEDIR);
804 textdomain (PACKAGE);
806 atexit (close_stdout);
808 fs_select_list = NULL;
809 fs_exclude_list = NULL;
810 inode_format = 0;
811 show_all_fs = 0;
812 show_listed_fs = 0;
814 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
816 print_type = 0;
817 posix_format = 0;
818 exit_status = 0;
820 while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
821 != -1)
823 switch (c)
825 case 0: /* Long option. */
826 break;
827 case 'a':
828 show_all_fs = 1;
829 break;
830 case 'B':
831 human_block_size (optarg, 1, &output_block_size);
832 break;
833 case 'i':
834 inode_format = 1;
835 break;
836 case 'h':
837 output_block_size = -1024;
838 break;
839 case 'H':
840 output_block_size = -1000;
841 break;
842 case 'k':
843 output_block_size = 1024;
844 break;
845 case 'l':
846 show_local_fs = 1;
847 break;
848 case 'm': /* obsolescent */
849 output_block_size = 1024 * 1024;
850 break;
851 case 'T':
852 print_type = 1;
853 break;
854 case 'P':
855 posix_format = 1;
856 break;
857 case SYNC_OPTION:
858 require_sync = 1;
859 break;
860 case NO_SYNC_OPTION:
861 require_sync = 0;
862 break;
864 case 'F':
865 /* Accept -F as a synonym for -t for compatibility with Solaris. */
866 case 't':
867 add_fs_type (optarg);
868 break;
870 case 'v': /* For SysV compatibility. */
871 /* ignore */
872 break;
873 case 'x':
874 add_excluded_fs_type (optarg);
875 break;
877 case_GETOPT_HELP_CHAR;
878 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
880 default:
881 usage (1);
885 /* Fail if the same file system type was both selected and excluded. */
887 int match = 0;
888 struct fs_type_list *fs_incl;
889 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
891 struct fs_type_list *fs_excl;
892 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
894 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
896 error (0, 0,
897 _("file system type %s both selected and excluded"),
898 quote (fs_incl->fs_name));
899 match = 1;
900 break;
904 if (match)
905 exit (1);
909 int i;
911 /* stat all the given entries to make sure they get automounted,
912 if necessary, before reading the filesystem table. */
913 stats = (struct stat *)
914 xmalloc ((argc - optind) * sizeof (struct stat));
915 for (i = optind; i < argc; ++i)
917 if (stat (argv[i], &stats[i - optind]))
919 error (0, errno, "%s", quote (argv[i]));
920 exit_status = 1;
921 argv[i] = NULL;
923 else
925 ++n_valid_args;
930 mount_list =
931 read_filesystem_list ((fs_select_list != NULL
932 || fs_exclude_list != NULL
933 || print_type
934 || show_local_fs));
936 if (mount_list == NULL)
938 /* Couldn't read the table of mounted filesystems.
939 Fail if df was invoked with no file name arguments;
940 Otherwise, merely give a warning and proceed. */
941 const char *warning = (optind == argc ? "" : _("Warning: "));
942 int status = (optind == argc ? 1 : 0);
943 error (status, errno,
944 _("%scannot read table of mounted filesystems"), warning);
947 if (require_sync)
948 sync ();
950 if (optind == argc)
952 print_header ();
953 show_all_entries ();
955 else
957 int i;
959 /* Display explicitly requested empty filesystems. */
960 show_listed_fs = 1;
962 if (n_valid_args > 0)
963 print_header ();
965 for (i = optind; i < argc; ++i)
966 if (argv[i])
967 show_entry (argv[i], &stats[i - optind]);
970 exit (exit_status);