*** empty log message ***
[coreutils.git] / src / df.c
blobb44498171fdcf1c0f2de7084b190f66868eebc69
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2001 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, except return "-" if the argument is -1,
217 and if NEGATIVE is 1 then N represents a negative number, expressed
218 in two's complement. */
219 static char const *
220 df_readable (int negative, uintmax_t n, char *buf,
221 int from_block_size, int t_output_block_size,
222 enum human_inexact_style s)
224 if (n == -1)
225 return "-";
226 else
228 char *p = human_readable_inexact (negative ? - n : n,
229 buf + negative, from_block_size,
230 t_output_block_size,
231 negative ? - s : s);
232 if (negative)
233 *--p = '-';
234 return p;
238 /* Display a space listing for the disk device with absolute path DISK.
239 If MOUNT_POINT is non-NULL, it is the path of the root of the
240 filesystem on DISK.
241 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
242 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
243 not be able to produce statistics in this case.
244 ME_DUMMY and ME_REMOTE are the mount entry flags. */
246 static void
247 show_dev (const char *disk, const char *mount_point, const char *fstype,
248 int me_dummy, int me_remote)
250 struct fs_usage fsu;
251 const char *stat_file;
252 char buf[3][LONGEST_HUMAN_READABLE + 2];
253 int width;
254 int use_width;
255 int input_units;
256 int output_units;
257 uintmax_t total;
258 uintmax_t available;
259 int negate_available;
260 uintmax_t available_to_root;
261 uintmax_t used;
262 int negate_used;
263 double pct = -1;
265 if (me_remote && show_local_fs)
266 return;
268 if (me_dummy && show_all_fs == 0 && !show_listed_fs)
269 return;
271 if (!selected_fstype (fstype) || excluded_fstype (fstype))
272 return;
274 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
275 program reports on the filesystem that the special file is on.
276 It would be better to report on the unmounted filesystem,
277 but statfs doesn't do that on most systems. */
278 stat_file = mount_point ? mount_point : disk;
280 if (get_fs_usage (stat_file, disk, &fsu))
282 error (0, errno, "%s", quote (stat_file));
283 exit_status = 1;
284 return;
287 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
288 return;
290 if (! disk)
291 disk = "-"; /* unknown */
292 if (! fstype)
293 fstype = "-"; /* unknown */
295 /* df.c reserved 5 positions for fstype,
296 but that does not suffice for type iso9660 */
297 if (print_type)
299 int disk_name_len = (int) strlen (disk);
300 int fstype_len = (int) strlen (fstype);
301 if (disk_name_len + fstype_len + 2 < 20)
302 printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
303 else if (!posix_format)
304 printf ("%s\n%18s ", disk, fstype);
305 else
306 printf ("%s %s", disk, fstype);
308 else
310 if ((int) strlen (disk) > 20 && !posix_format)
311 printf ("%s\n%20s", disk, "");
312 else
313 printf ("%-20s", disk);
316 if (inode_format)
318 width = 7;
319 use_width = 5;
320 input_units = 1;
321 output_units = output_block_size < 0 ? output_block_size : 1;
322 total = fsu.fsu_files;
323 available = fsu.fsu_ffree;
324 negate_available = 0;
325 available_to_root = available;
327 else
329 width = output_block_size < 0 ? 5 + (output_block_size == -1000) : 9;
330 use_width = posix_format ? 8 : 4;
331 input_units = fsu.fsu_blocksize;
332 output_units = output_block_size;
333 total = fsu.fsu_blocks;
334 available = fsu.fsu_bavail;
335 negate_available = fsu.fsu_bavail_top_bit_set;
336 available_to_root = fsu.fsu_bfree;
339 used = -1;
340 negate_used = 0;
341 if (total != -1 && available_to_root != -1)
343 used = total - available_to_root;
344 if (total < available_to_root)
346 negate_used = 1;
347 used = - used;
351 printf (" %*s %*s %*s ",
352 width, df_readable (0, total,
353 buf[0], input_units, output_units,
354 (posix_format
355 ? human_ceiling
356 : human_round_to_even)),
357 width, df_readable (negate_used, used,
358 buf[1], input_units, output_units,
359 human_ceiling),
360 width, df_readable (negate_available, available,
361 buf[2], input_units, output_units,
362 posix_format ? human_ceiling : human_floor));
364 if (used == -1 || available == -1)
366 else if (!negate_used
367 && used <= TYPE_MAXIMUM (uintmax_t) / 100
368 && used + available != 0
369 && (used + available < used) == negate_available)
371 uintmax_t u100 = used * 100;
372 uintmax_t nonroot_total = used + available;
373 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
375 else
377 /* The calculation cannot be done easily with integer
378 arithmetic. Fall back on floating point. This can suffer
379 from minor rounding errors, but doing it exactly requires
380 multiple precision arithmetic, and it's not worth the
381 aggravation. */
382 double u = negate_used ? - (double) - used : used;
383 double a = negate_available ? - (double) - available : available;
384 double nonroot_total = u + a;
385 if (nonroot_total)
387 double ipct;
388 pct = u * 100 / nonroot_total;
389 ipct = (long) pct;
391 /* Like `pct = ceil (dpct);', but avoid ceil so that
392 the math library needn't be linked. */
393 if (ipct - 1 < pct && pct <= ipct + 1)
394 pct = ipct + (ipct < pct);
398 if (0 <= pct)
399 printf ("%*.0f%%", use_width - 1, pct);
400 else
401 printf ("%*s", use_width, "- ");
403 if (mount_point)
405 #ifdef HIDE_AUTOMOUNT_PREFIX
406 /* Don't print the first directory name in MOUNT_POINT if it's an
407 artifact of an automounter. This is a bit too aggressive to be
408 the default. */
409 if (strncmp ("/auto/", mount_point, 6) == 0)
410 mount_point += 5;
411 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
412 mount_point += 8;
413 #endif
414 printf (" %s", mount_point);
416 putchar ('\n');
419 /* Identify the directory, if any, that device
420 DISK is mounted on, and show its disk usage. */
422 static void
423 show_disk (const char *disk)
425 struct mount_entry *me;
427 for (me = mount_list; me; me = me->me_next)
428 if (STREQ (disk, me->me_devname))
430 show_dev (me->me_devname, me->me_mountdir, me->me_type,
431 me->me_dummy, me->me_remote);
432 return;
434 /* No filesystem is mounted on DISK. */
435 show_dev (disk, (char *) NULL, (char *) NULL, 0, 0);
438 /* Return the root mountpoint of the filesystem on which FILE exists, in
439 malloced storage. FILE_STAT should be the result of stating FILE. */
440 static char *
441 find_mount_point (const char *file, const struct stat *file_stat)
443 struct saved_cwd cwd;
444 struct stat last_stat;
445 char *mp = 0; /* The malloced mount point path. */
447 if (save_cwd (&cwd))
448 return NULL;
450 if (S_ISDIR (file_stat->st_mode))
451 /* FILE is a directory, so just chdir there directly. */
453 last_stat = *file_stat;
454 if (chdir (file) < 0)
455 return NULL;
457 else
458 /* FILE is some other kind of file, we need to use its directory. */
460 char *dir = dir_name (file);
461 int rv = chdir (dir);
462 free (dir);
464 if (rv < 0)
465 return NULL;
467 if (stat (".", &last_stat) < 0)
468 goto done;
471 /* Now walk up FILE's parents until we find another filesystem or /,
472 chdiring as we go. LAST_STAT holds stat information for the last place
473 we visited. */
474 for (;;)
476 struct stat st;
477 if (stat ("..", &st) < 0)
478 goto done;
479 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
480 /* cwd is the mount point. */
481 break;
482 if (chdir ("..") < 0)
483 goto done;
484 last_stat = st;
487 /* Finally reached a mount point, see what it's called. */
488 mp = xgetcwd ();
490 done:
491 /* Restore the original cwd. */
493 int save_errno = errno;
494 if (restore_cwd (&cwd, 0, mp))
495 exit (1); /* We're scrod. */
496 free_cwd (&cwd);
497 errno = save_errno;
500 return mp;
503 /* Figure out which device file or directory POINT is mounted on
504 and show its disk usage.
505 STATP is the results of `stat' on POINT. */
506 static void
507 show_point (const char *point, const struct stat *statp)
509 struct stat disk_stats;
510 struct mount_entry *me;
511 struct mount_entry *matching_dummy = NULL;
512 char *needs_freeing = NULL;
514 /* If POINT is an absolute path name, see if we can find the
515 mount point without performing any extra stat calls at all. */
516 if (*point == '/')
518 for (me = mount_list; me; me = me->me_next)
520 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs"))
522 /* Prefer non-dummy entries. */
523 if (! me->me_dummy)
524 goto show_me;
525 matching_dummy = me;
529 if (matching_dummy)
530 goto show_matching_dummy;
533 /* Ideally, the following mess of #if'd code would be in a separate
534 file, and there'd be a single function call here. FIXME, someday. */
536 #if HAVE_REALPATH || HAVE_RESOLVEPATH || HAVE_CANONICALIZE_FILE_NAME
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_CANONICALIZE_FILE_NAME
547 resolved = canonicalize_file_name (abspoint);
548 resolved_len = resolved ? strlen (resolved) : -1;
549 # else
550 # if HAVE_RESOLVEPATH
551 /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
552 relative names into absolute ones, so prepend the working
553 directory if the path is not absolute. */
555 if (*point != '/')
557 static char const *wd;
559 if (! wd)
561 struct stat pwd_stats;
562 struct stat dot_stats;
564 /* Use PWD if it is correct; this is usually cheaper than
565 xgetcwd. */
566 wd = getenv ("PWD");
567 if (! (wd
568 && stat (wd, &pwd_stats) == 0
569 && stat (".", &dot_stats) == 0
570 && SAME_INODE (pwd_stats, dot_stats)))
571 wd = xgetcwd ();
574 if (wd)
576 needs_freeing = path_concat (wd, point, NULL);
577 if (needs_freeing)
578 abspoint = needs_freeing;
581 # endif
583 # if HAVE_RESOLVEPATH
585 size_t resolved_size = strlen (abspoint);
586 while (1)
588 resolved_size = 2 * resolved_size + 1;
589 resolved = xmalloc (resolved_size);
590 resolved_len = resolvepath (abspoint, resolved, resolved_size);
591 if (resolved_len < resolved_size)
592 break;
593 free (resolved);
596 # else
597 /* Use realpath only as a last resort.
598 It provides a very poor interface. */
599 resolved = xmalloc (PATH_MAX + 1);
600 resolved = (char *) realpath (abspoint, resolved);
601 resolved_len = resolved ? strlen (resolved) : -1;
602 # endif
603 # endif
605 if (1 <= resolved_len && resolved[0] == '/')
607 size_t best_match_len = 0;
609 for (me = mount_list; me; me = me->me_next)
610 if (! me->me_dummy)
612 size_t len = strlen (me->me_mountdir);
613 if (best_match_len < len && len <= resolved_len
614 && (len == 1 /* root file system */
615 || ((len == resolved_len || resolved[len] == '/')
616 && strncmp (me->me_mountdir, resolved, len) == 0)))
618 best_match = me;
619 best_match_len = len;
624 if (resolved)
625 free (resolved);
627 if (best_match && !STREQ (best_match->me_type, "lofs")
628 && stat (best_match->me_mountdir, &disk_stats) == 0
629 && disk_stats.st_dev == statp->st_dev)
631 me = best_match;
632 goto show_me;
635 #endif
637 for (me = mount_list; me; me = me->me_next)
639 if (me->me_dev == (dev_t) -1)
641 if (stat (me->me_mountdir, &disk_stats) == 0)
642 me->me_dev = disk_stats.st_dev;
643 else
645 error (0, errno, "%s", quote (me->me_mountdir));
646 exit_status = 1;
647 /* So we won't try and fail repeatedly. */
648 me->me_dev = (dev_t) -2;
652 if (statp->st_dev == me->me_dev)
654 /* Skip bogus mtab entries. */
655 if (stat (me->me_mountdir, &disk_stats) != 0
656 || disk_stats.st_dev != me->me_dev)
658 me->me_dev = (dev_t) -2;
659 continue;
662 /* Prefer non-dummy entries. */
663 if (! me->me_dummy)
664 goto show_me;
665 matching_dummy = me;
669 if (matching_dummy)
670 goto show_matching_dummy;
672 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
673 print as much info as we can; methods that require the device to be
674 present will fail at a later point. */
676 /* Find the actual mount point. */
677 char *mp = find_mount_point (point, statp);
678 if (mp)
680 show_dev (0, mp, 0, 0, 0);
681 free (mp);
683 else
684 error (0, errno, "%s", quote (point));
687 goto free_then_return;
689 show_matching_dummy:
690 me = matching_dummy;
691 show_me:
692 show_dev (me->me_devname, me->me_mountdir, me->me_type, me->me_dummy,
693 me->me_remote);
694 free_then_return:
695 if (needs_freeing)
696 free (needs_freeing);
699 /* Determine what kind of node PATH is and show the disk usage
700 for it. STATP is the results of `stat' on PATH. */
702 static void
703 show_entry (const char *path, const struct stat *statp)
705 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
706 show_disk (path);
707 else
708 show_point (path, statp);
711 /* Show all mounted filesystems, except perhaps those that are of
712 an unselected type or are empty. */
714 static void
715 show_all_entries (void)
717 struct mount_entry *me;
719 for (me = mount_list; me; me = me->me_next)
720 show_dev (me->me_devname, me->me_mountdir, me->me_type,
721 me->me_dummy, me->me_remote);
724 /* Add FSTYPE to the list of filesystem types to display. */
726 static void
727 add_fs_type (const char *fstype)
729 struct fs_type_list *fsp;
731 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
732 fsp->fs_name = (char *) fstype;
733 fsp->fs_next = fs_select_list;
734 fs_select_list = fsp;
737 /* Add FSTYPE to the list of filesystem types to be omitted. */
739 static void
740 add_excluded_fs_type (const char *fstype)
742 struct fs_type_list *fsp;
744 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
745 fsp->fs_name = (char *) fstype;
746 fsp->fs_next = fs_exclude_list;
747 fs_exclude_list = fsp;
750 void
751 usage (int status)
753 if (status != 0)
754 fprintf (stderr, _("Try `%s --help' for more information.\n"),
755 program_name);
756 else
758 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
759 fputs (_("\
760 Show information about the filesystem on which each FILE resides,\n\
761 or all filesystems by default.\n\
763 "), stdout);
764 fputs (_("\
765 Mandatory arguments to long options are mandatory for short options too.\n\
766 "), stdout);
767 fputs (_("\
768 -a, --all include filesystems having 0 blocks\n\
769 -B, --block-size=SIZE use SIZE-byte blocks\n\
770 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
771 -H, --si likewise, but use powers of 1000 not 1024\n\
772 "), stdout);
773 fputs (_("\
774 -i, --inodes list inode information instead of block usage\n\
775 -k like --block-size=1K\n\
776 -l, --local limit listing to local filesystems\n\
777 --no-sync do not invoke sync before getting usage info (default)\n\
778 "), stdout);
779 fputs (_("\
780 -P, --portability use the POSIX output format\n\
781 --sync invoke sync before getting usage info\n\
782 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
783 -T, --print-type print filesystem type\n\
784 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
785 -v (ignored)\n\
786 "), stdout);
787 fputs (HELP_OPTION_DESCRIPTION, stdout);
788 fputs (VERSION_OPTION_DESCRIPTION, stdout);
789 fputs (_("\n\
790 SIZE may be (or may be an integer optionally followed by) one of following:\n\
791 kB 1000, K 1024, MB 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\
792 "), stdout);
793 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
795 exit (status);
799 main (int argc, char **argv)
801 int c;
802 struct stat *stats IF_LINT (= 0);
803 int n_valid_args = 0;
805 program_name = argv[0];
806 setlocale (LC_ALL, "");
807 bindtextdomain (PACKAGE, LOCALEDIR);
808 textdomain (PACKAGE);
810 atexit (close_stdout);
812 fs_select_list = NULL;
813 fs_exclude_list = NULL;
814 inode_format = 0;
815 show_all_fs = 0;
816 show_listed_fs = 0;
818 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
820 print_type = 0;
821 posix_format = 0;
822 exit_status = 0;
824 while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
825 != -1)
827 switch (c)
829 case 0: /* Long option. */
830 break;
831 case 'a':
832 show_all_fs = 1;
833 break;
834 case 'B':
835 human_block_size (optarg, 1, &output_block_size);
836 break;
837 case 'i':
838 inode_format = 1;
839 break;
840 case 'h':
841 output_block_size = -1024;
842 break;
843 case 'H':
844 output_block_size = -1000;
845 break;
846 case 'k':
847 output_block_size = 1024;
848 break;
849 case 'l':
850 show_local_fs = 1;
851 break;
852 case 'm': /* obsolescent */
853 output_block_size = 1024 * 1024;
854 break;
855 case 'T':
856 print_type = 1;
857 break;
858 case 'P':
859 posix_format = 1;
860 break;
861 case SYNC_OPTION:
862 require_sync = 1;
863 break;
864 case NO_SYNC_OPTION:
865 require_sync = 0;
866 break;
868 case 'F':
869 /* Accept -F as a synonym for -t for compatibility with Solaris. */
870 case 't':
871 add_fs_type (optarg);
872 break;
874 case 'v': /* For SysV compatibility. */
875 /* ignore */
876 break;
877 case 'x':
878 add_excluded_fs_type (optarg);
879 break;
881 case_GETOPT_HELP_CHAR;
882 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
884 default:
885 usage (1);
889 /* Fail if the same file system type was both selected and excluded. */
891 int match = 0;
892 struct fs_type_list *fs_incl;
893 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
895 struct fs_type_list *fs_excl;
896 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
898 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
900 error (0, 0,
901 _("file system type %s both selected and excluded"),
902 quote (fs_incl->fs_name));
903 match = 1;
904 break;
908 if (match)
909 exit (1);
913 int i;
915 /* stat all the given entries to make sure they get automounted,
916 if necessary, before reading the filesystem table. */
917 stats = (struct stat *)
918 xmalloc ((argc - optind) * sizeof (struct stat));
919 for (i = optind; i < argc; ++i)
921 if (stat (argv[i], &stats[i - optind]))
923 error (0, errno, "%s", quote (argv[i]));
924 exit_status = 1;
925 argv[i] = NULL;
927 else
929 ++n_valid_args;
934 mount_list =
935 read_filesystem_list ((fs_select_list != NULL
936 || fs_exclude_list != NULL
937 || print_type
938 || show_local_fs));
940 if (mount_list == NULL)
942 /* Couldn't read the table of mounted filesystems.
943 Fail if df was invoked with no file name arguments;
944 Otherwise, merely give a warning and proceed. */
945 const char *warning = (optind == argc ? "" : _("Warning: "));
946 int status = (optind == argc ? 1 : 0);
947 error (status, errno,
948 _("%scannot read table of mounted filesystems"), warning);
951 if (require_sync)
952 sync ();
954 if (optind == argc)
956 print_header ();
957 show_all_entries ();
959 else
961 int i;
963 /* Display explicitly requested empty filesystems. */
964 show_listed_fs = 1;
966 if (n_valid_args > 0)
967 print_header ();
969 for (i = optind; i < argc; ++i)
970 if (argv[i])
971 show_entry (argv[i], &stats[i - optind]);
974 exit (exit_status);