TODO: add an item for a chmod optimization
[platform/upstream/coreutils.git] / src / df.c
1 /* df - summarize free disk space
2    Copyright (C) 91, 1995-2008 Free Software Foundation, Inc.
3
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 3 of the License, or
7    (at your option) any later version.
8
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.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
18    --human-readable and --megabyte options added by lm@sgi.com.
19    --si and large file support added by eggert@twinsun.com.  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <getopt.h>
25
26 #include "system.h"
27 #include "canonicalize.h"
28 #include "error.h"
29 #include "fsusage.h"
30 #include "human.h"
31 #include "mountlist.h"
32 #include "quote.h"
33 #include "save-cwd.h"
34 #include "xgetcwd.h"
35
36 /* The official name of this program (e.g., no `g' prefix).  */
37 #define PROGRAM_NAME "df"
38
39 #define AUTHORS \
40   proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
41   proper_name ("David MacKenzie"), \
42   proper_name ("Paul Eggert")
43
44 /* If true, show inode information. */
45 static bool inode_format;
46
47 /* If true, show even file systems with zero size or
48    uninteresting types. */
49 static bool show_all_fs;
50
51 /* If true, show only local file systems.  */
52 static bool show_local_fs;
53
54 /* If true, output data for each file system corresponding to a
55    command line argument -- even if it's a dummy (automounter) entry.  */
56 static bool show_listed_fs;
57
58 /* Human-readable options for output.  */
59 static int human_output_opts;
60
61 /* The units to use when printing sizes.  */
62 static uintmax_t output_block_size;
63
64 /* If true, use the POSIX output format.  */
65 static bool posix_format;
66
67 /* True if a file system has been processed for output.  */
68 static bool file_systems_processed;
69
70 /* If true, invoke the `sync' system call before getting any usage data.
71    Using this option can make df very slow, especially with many or very
72    busy disks.  Note that this may make a difference on some systems --
73    SunOS 4.1.3, for one.  It is *not* necessary on Linux.  */
74 static bool require_sync;
75
76 /* Desired exit status.  */
77 static int exit_status;
78
79 /* A file system type to display. */
80
81 struct fs_type_list
82 {
83   char *fs_name;
84   struct fs_type_list *fs_next;
85 };
86
87 /* Linked list of file system types to display.
88    If `fs_select_list' is NULL, list all types.
89    This table is generated dynamically from command-line options,
90    rather than hardcoding into the program what it thinks are the
91    valid file system types; let the user specify any file system type
92    they want to, and if there are any file systems of that type, they
93    will be shown.
94
95    Some file system types:
96    4.2 4.3 ufs nfs swap ignore io vm efs dbg */
97
98 static struct fs_type_list *fs_select_list;
99
100 /* Linked list of file system types to omit.
101    If the list is empty, don't exclude any types.  */
102
103 static struct fs_type_list *fs_exclude_list;
104
105 /* Linked list of mounted file systems. */
106 static struct mount_entry *mount_list;
107
108 /* If true, print file system type as well.  */
109 static bool print_type;
110
111 /* For long options that have no equivalent short option, use a
112    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
113 enum
114 {
115   NO_SYNC_OPTION = CHAR_MAX + 1,
116   SYNC_OPTION
117 };
118
119 static struct option const long_options[] =
120 {
121   {"all", no_argument, NULL, 'a'},
122   {"block-size", required_argument, NULL, 'B'},
123   {"inodes", no_argument, NULL, 'i'},
124   {"human-readable", no_argument, NULL, 'h'},
125   {"si", no_argument, NULL, 'H'},
126   {"local", no_argument, NULL, 'l'},
127   {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */
128   {"portability", no_argument, NULL, 'P'},
129   {"print-type", no_argument, NULL, 'T'},
130   {"sync", no_argument, NULL, SYNC_OPTION},
131   {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
132   {"type", required_argument, NULL, 't'},
133   {"exclude-type", required_argument, NULL, 'x'},
134   {GETOPT_HELP_OPTION_DECL},
135   {GETOPT_VERSION_OPTION_DECL},
136   {NULL, 0, NULL, 0}
137 };
138
139 static void
140 print_header (void)
141 {
142   char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
143
144   if (print_type)
145     fputs (_("Filesystem    Type"), stdout);
146   else
147     fputs (_("Filesystem        "), stdout);
148
149   if (inode_format)
150     printf (_("    Inodes   IUsed   IFree IUse%%"));
151   else if (human_output_opts & human_autoscale)
152     {
153       if (human_output_opts & human_base_1024)
154         printf (_("    Size  Used Avail Use%%"));
155       else
156         printf (_("     Size   Used  Avail Use%%"));
157     }
158   else if (posix_format)
159     printf (_(" %s-blocks      Used Available Capacity"),
160             umaxtostr (output_block_size, buf));
161   else
162     {
163       int opts = (human_suppress_point_zero
164                   | human_autoscale | human_SI
165                   | (human_output_opts
166                      & (human_group_digits | human_base_1024 | human_B)));
167
168       /* Prefer the base that makes the human-readable value more exact,
169          if there is a difference.  */
170
171       uintmax_t q1000 = output_block_size;
172       uintmax_t q1024 = output_block_size;
173       bool divisible_by_1000;
174       bool divisible_by_1024;
175
176       do
177         {
178           divisible_by_1000 = q1000 % 1000 == 0;  q1000 /= 1000;
179           divisible_by_1024 = q1024 % 1024 == 0;  q1024 /= 1024;
180         }
181       while (divisible_by_1000 & divisible_by_1024);
182
183       if (divisible_by_1000 < divisible_by_1024)
184         opts |= human_base_1024;
185       if (divisible_by_1024 < divisible_by_1000)
186         opts &= ~human_base_1024;
187       if (! (opts & human_base_1024))
188         opts |= human_B;
189
190       printf (_(" %4s-blocks      Used Available Use%%"),
191               human_readable (output_block_size, buf, opts, 1, 1));
192     }
193
194   printf (_(" Mounted on\n"));
195 }
196
197 /* Is FSTYPE a type of file system that should be listed?  */
198
199 static bool
200 selected_fstype (const char *fstype)
201 {
202   const struct fs_type_list *fsp;
203
204   if (fs_select_list == NULL || fstype == NULL)
205     return true;
206   for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
207     if (STREQ (fstype, fsp->fs_name))
208       return true;
209   return false;
210 }
211
212 /* Is FSTYPE a type of file system that should be omitted?  */
213
214 static bool
215 excluded_fstype (const char *fstype)
216 {
217   const struct fs_type_list *fsp;
218
219   if (fs_exclude_list == NULL || fstype == NULL)
220     return false;
221   for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
222     if (STREQ (fstype, fsp->fs_name))
223       return true;
224   return false;
225 }
226
227 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
228    except:
229
230     - If NEGATIVE, then N represents a negative number,
231       expressed in two's complement.
232     - Otherwise, return "-" if N is UINTMAX_MAX.  */
233
234 static char const *
235 df_readable (bool negative, uintmax_t n, char *buf,
236              uintmax_t input_units, uintmax_t output_units)
237 {
238   if (n == UINTMAX_MAX && !negative)
239     return "-";
240   else
241     {
242       char *p = human_readable (negative ? -n : n, buf + negative,
243                                 human_output_opts, input_units, output_units);
244       if (negative)
245         *--p = '-';
246       return p;
247     }
248 }
249
250 /* Display a space listing for the disk device with absolute file name DISK.
251    If MOUNT_POINT is non-NULL, it is the name of the root of the
252    file system on DISK.
253    If STAT_FILE is non-null, it is the name of a file within the file
254    system that the user originally asked for; this provides better
255    diagnostics, and sometimes it provides better results on networked
256    file systems that give different free-space results depending on
257    where in the file system you probe.
258    If FSTYPE is non-NULL, it is the type of the file system on DISK.
259    If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
260    not be able to produce statistics in this case.
261    ME_DUMMY and ME_REMOTE are the mount entry flags.  */
262
263 static void
264 show_dev (char const *disk, char const *mount_point,
265           char const *stat_file, char const *fstype,
266           bool me_dummy, bool me_remote)
267 {
268   struct fs_usage fsu;
269   char buf[3][LONGEST_HUMAN_READABLE + 2];
270   int width;
271   int col1_adjustment = 0;
272   int use_width;
273   uintmax_t input_units;
274   uintmax_t output_units;
275   uintmax_t total;
276   uintmax_t available;
277   bool negate_available;
278   uintmax_t available_to_root;
279   uintmax_t used;
280   bool negate_used;
281   double pct = -1;
282
283   if (me_remote & show_local_fs)
284     return;
285
286   if (me_dummy & !show_all_fs & !show_listed_fs)
287     return;
288
289   if (!selected_fstype (fstype) || excluded_fstype (fstype))
290     return;
291
292   /* If MOUNT_POINT is NULL, then the file system is not mounted, and this
293      program reports on the file system that the special file is on.
294      It would be better to report on the unmounted file system,
295      but statfs doesn't do that on most systems.  */
296   if (!stat_file)
297     stat_file = mount_point ? mount_point : disk;
298
299   if (get_fs_usage (stat_file, disk, &fsu))
300     {
301       error (0, errno, "%s", quote (stat_file));
302       exit_status = EXIT_FAILURE;
303       return;
304     }
305
306   if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
307     return;
308
309   if (! file_systems_processed)
310     {
311       file_systems_processed = true;
312       print_header ();
313     }
314
315   if (! disk)
316     disk = "-";                 /* unknown */
317   if (! fstype)
318     fstype = "-";               /* unknown */
319
320   /* df.c reserved 5 positions for fstype,
321      but that does not suffice for type iso9660 */
322   if (print_type)
323     {
324       size_t disk_name_len = strlen (disk);
325       size_t fstype_len = strlen (fstype);
326       if (disk_name_len + fstype_len < 18)
327         printf ("%s%*s  ", disk, 18 - (int) disk_name_len, fstype);
328       else if (!posix_format)
329         printf ("%s\n%18s  ", disk, fstype);
330       else
331         printf ("%s %s", disk, fstype);
332     }
333   else
334     {
335       if (strlen (disk) > 20 && !posix_format)
336         printf ("%s\n%20s", disk, "");
337       else
338         printf ("%-20s", disk);
339     }
340
341   if (inode_format)
342     {
343       width = 7;
344       use_width = 5;
345       input_units = output_units = 1;
346       total = fsu.fsu_files;
347       available = fsu.fsu_ffree;
348       negate_available = false;
349       available_to_root = available;
350     }
351   else
352     {
353       if (human_output_opts & human_autoscale)
354         width = 5 + ! (human_output_opts & human_base_1024);
355       else
356         {
357           width = 9;
358           if (posix_format)
359             {
360               uintmax_t b;
361               col1_adjustment = -3;
362               for (b = output_block_size; 9 < b; b /= 10)
363                 col1_adjustment++;
364             }
365         }
366       use_width = ((posix_format
367                     && ! (human_output_opts & human_autoscale))
368                    ? 8 : 4);
369       input_units = fsu.fsu_blocksize;
370       output_units = output_block_size;
371       total = fsu.fsu_blocks;
372       available = fsu.fsu_bavail;
373       negate_available = (fsu.fsu_bavail_top_bit_set
374                           & (available != UINTMAX_MAX));
375       available_to_root = fsu.fsu_bfree;
376     }
377
378   used = UINTMAX_MAX;
379   negate_used = false;
380   if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX)
381     {
382       used = total - available_to_root;
383       negate_used = (total < available_to_root);
384     }
385
386   printf (" %*s %*s %*s ",
387           width + col1_adjustment,
388           df_readable (false, total,
389                        buf[0], input_units, output_units),
390           width, df_readable (negate_used, used,
391                               buf[1], input_units, output_units),
392           width, df_readable (negate_available, available,
393                               buf[2], input_units, output_units));
394
395   if (used == UINTMAX_MAX || available == UINTMAX_MAX)
396     ;
397   else if (!negate_used
398            && used <= TYPE_MAXIMUM (uintmax_t) / 100
399            && used + available != 0
400            && (used + available < used) == negate_available)
401     {
402       uintmax_t u100 = used * 100;
403       uintmax_t nonroot_total = used + available;
404       pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
405     }
406   else
407     {
408       /* The calculation cannot be done easily with integer
409          arithmetic.  Fall back on floating point.  This can suffer
410          from minor rounding errors, but doing it exactly requires
411          multiple precision arithmetic, and it's not worth the
412          aggravation.  */
413       double u = negate_used ? - (double) - used : used;
414       double a = negate_available ? - (double) - available : available;
415       double nonroot_total = u + a;
416       if (nonroot_total)
417         {
418           long int lipct = pct = u * 100 / nonroot_total;
419           double ipct = lipct;
420
421           /* Like `pct = ceil (dpct);', but avoid ceil so that
422              the math library needn't be linked.  */
423           if (ipct - 1 < pct && pct <= ipct + 1)
424             pct = ipct + (ipct < pct);
425         }
426     }
427
428   if (0 <= pct)
429     printf ("%*.0f%%", use_width - 1, pct);
430   else
431     printf ("%*s", use_width, "- ");
432
433   if (mount_point)
434     {
435 #ifdef HIDE_AUTOMOUNT_PREFIX
436       /* Don't print the first directory name in MOUNT_POINT if it's an
437          artifact of an automounter.  This is a bit too aggressive to be
438          the default.  */
439       if (strncmp ("/auto/", mount_point, 6) == 0)
440         mount_point += 5;
441       else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
442         mount_point += 8;
443 #endif
444       printf (" %s", mount_point);
445     }
446   putchar ('\n');
447 }
448
449 /* Return the root mountpoint of the file system on which FILE exists, in
450    malloced storage.  FILE_STAT should be the result of stating FILE.
451    Give a diagnostic and return NULL if unable to determine the mount point.
452    Exit if unable to restore current working directory.  */
453 static char *
454 find_mount_point (const char *file, const struct stat *file_stat)
455 {
456   struct saved_cwd cwd;
457   struct stat last_stat;
458   char *mp = NULL;              /* The malloced mount point.  */
459
460   if (save_cwd (&cwd) != 0)
461     {
462       error (0, errno, _("cannot get current directory"));
463       return NULL;
464     }
465
466   if (S_ISDIR (file_stat->st_mode))
467     /* FILE is a directory, so just chdir there directly.  */
468     {
469       last_stat = *file_stat;
470       if (chdir (file) < 0)
471         {
472           error (0, errno, _("cannot change to directory %s"), quote (file));
473           return NULL;
474         }
475     }
476   else
477     /* FILE is some other kind of file; use its directory.  */
478     {
479       char *xdir = dir_name (file);
480       char *dir;
481       ASSIGN_STRDUPA (dir, xdir);
482       free (xdir);
483
484       if (chdir (dir) < 0)
485         {
486           error (0, errno, _("cannot change to directory %s"), quote (dir));
487           return NULL;
488         }
489
490       if (stat (".", &last_stat) < 0)
491         {
492           error (0, errno, _("cannot stat current directory (now %s)"),
493                  quote (dir));
494           goto done;
495         }
496     }
497
498   /* Now walk up FILE's parents until we find another file system or /,
499      chdiring as we go.  LAST_STAT holds stat information for the last place
500      we visited.  */
501   for (;;)
502     {
503       struct stat st;
504       if (stat ("..", &st) < 0)
505         {
506           error (0, errno, _("cannot stat %s"), quote (".."));
507           goto done;
508         }
509       if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
510         /* cwd is the mount point.  */
511         break;
512       if (chdir ("..") < 0)
513         {
514           error (0, errno, _("cannot change to directory %s"), quote (".."));
515           goto done;
516         }
517       last_stat = st;
518     }
519
520   /* Finally reached a mount point, see what it's called.  */
521   mp = xgetcwd ();
522
523 done:
524   /* Restore the original cwd.  */
525   {
526     int save_errno = errno;
527     if (restore_cwd (&cwd) != 0)
528       error (EXIT_FAILURE, errno,
529              _("failed to return to initial working directory"));
530     free_cwd (&cwd);
531     errno = save_errno;
532   }
533
534   return mp;
535 }
536
537 /* If DISK corresponds to a mount point, show its usage
538    and return true.  Otherwise, return false.  */
539 static bool
540 show_disk (char const *disk)
541 {
542   struct mount_entry const *me;
543   struct mount_entry const *best_match = NULL;
544
545   for (me = mount_list; me; me = me->me_next)
546     if (STREQ (disk, me->me_devname))
547       best_match = me;
548
549   if (best_match)
550     {
551       show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
552                 best_match->me_type, best_match->me_dummy,
553                 best_match->me_remote);
554       return true;
555     }
556
557   return false;
558 }
559
560 /* Figure out which device file or directory POINT is mounted on
561    and show its disk usage.
562    STATP must be the result of `stat (POINT, STATP)'.  */
563 static void
564 show_point (const char *point, const struct stat *statp)
565 {
566   struct stat disk_stats;
567   struct mount_entry *me;
568   struct mount_entry const *best_match = NULL;
569
570   /* If POINT is an absolute file name, see if we can find the
571      mount point without performing any extra stat calls at all.  */
572   if (*point == '/')
573     {
574       /* Find the best match: prefer non-dummies, and then prefer the
575          last match if there are ties.  */
576
577       for (me = mount_list; me; me = me->me_next)
578         if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs")
579             && (!best_match || best_match->me_dummy || !me->me_dummy))
580           best_match = me;
581     }
582
583   /* Calculate the real absolute file name for POINT, and use that to find
584      the mount point.  This avoids statting unavailable mount points,
585      which can hang df.  */
586   if (! best_match)
587     {
588       char *resolved = canonicalize_file_name (point);
589
590       if (resolved && resolved[0] == '/')
591         {
592           size_t resolved_len = strlen (resolved);
593           size_t best_match_len = 0;
594
595           for (me = mount_list; me; me = me->me_next)
596             if (!STREQ (me->me_type, "lofs")
597                 && (!best_match || best_match->me_dummy || !me->me_dummy))
598               {
599                 size_t len = strlen (me->me_mountdir);
600                 if (best_match_len <= len && len <= resolved_len
601                     && (len == 1 /* root file system */
602                         || ((len == resolved_len || resolved[len] == '/')
603                             && strncmp (me->me_mountdir, resolved, len) == 0)))
604                   {
605                     best_match = me;
606                     best_match_len = len;
607                   }
608               }
609         }
610
611       free (resolved);
612
613       if (best_match
614           && (stat (best_match->me_mountdir, &disk_stats) != 0
615               || disk_stats.st_dev != statp->st_dev))
616         best_match = NULL;
617     }
618
619   if (! best_match)
620     for (me = mount_list; me; me = me->me_next)
621       {
622         if (me->me_dev == (dev_t) -1)
623           {
624             if (stat (me->me_mountdir, &disk_stats) == 0)
625               me->me_dev = disk_stats.st_dev;
626             else
627               {
628                 /* Report only I/O errors.  Other errors might be
629                    caused by shadowed mount points, which means POINT
630                    can't possibly be on this file system.  */
631                 if (errno == EIO)
632                   {
633                     error (0, errno, "%s", quote (me->me_mountdir));
634                     exit_status = EXIT_FAILURE;
635                   }
636
637                 /* So we won't try and fail repeatedly. */
638                 me->me_dev = (dev_t) -2;
639               }
640           }
641
642         if (statp->st_dev == me->me_dev
643             && !STREQ (me->me_type, "lofs")
644             && (!best_match || best_match->me_dummy || !me->me_dummy))
645           {
646             /* Skip bogus mtab entries.  */
647             if (stat (me->me_mountdir, &disk_stats) != 0
648                 || disk_stats.st_dev != me->me_dev)
649               me->me_dev = (dev_t) -2;
650             else
651               best_match = me;
652           }
653       }
654
655   if (best_match)
656     show_dev (best_match->me_devname, best_match->me_mountdir, point,
657               best_match->me_type, best_match->me_dummy, best_match->me_remote);
658   else
659     {
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.  */
663
664       /* Find the actual mount point.  */
665       char *mp = find_mount_point (point, statp);
666       if (mp)
667         {
668           show_dev (NULL, mp, NULL, NULL, false, false);
669           free (mp);
670         }
671     }
672 }
673
674 /* Determine what kind of node NAME is and show the disk usage
675    for it.  STATP is the results of `stat' on NAME.  */
676
677 static void
678 show_entry (char const *name, struct stat const *statp)
679 {
680   if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
681       && show_disk (name))
682     return;
683
684   show_point (name, statp);
685 }
686
687 /* Show all mounted file systems, except perhaps those that are of
688    an unselected type or are empty. */
689
690 static void
691 show_all_entries (void)
692 {
693   struct mount_entry *me;
694
695   for (me = mount_list; me; me = me->me_next)
696     show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
697               me->me_dummy, me->me_remote);
698 }
699
700 /* Add FSTYPE to the list of file system types to display. */
701
702 static void
703 add_fs_type (const char *fstype)
704 {
705   struct fs_type_list *fsp;
706
707   fsp = xmalloc (sizeof *fsp);
708   fsp->fs_name = (char *) fstype;
709   fsp->fs_next = fs_select_list;
710   fs_select_list = fsp;
711 }
712
713 /* Add FSTYPE to the list of file system types to be omitted. */
714
715 static void
716 add_excluded_fs_type (const char *fstype)
717 {
718   struct fs_type_list *fsp;
719
720   fsp = xmalloc (sizeof *fsp);
721   fsp->fs_name = (char *) fstype;
722   fsp->fs_next = fs_exclude_list;
723   fs_exclude_list = fsp;
724 }
725
726 void
727 usage (int status)
728 {
729   if (status != EXIT_SUCCESS)
730     fprintf (stderr, _("Try `%s --help' for more information.\n"),
731              program_name);
732   else
733     {
734       printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
735       fputs (_("\
736 Show information about the file system on which each FILE resides,\n\
737 or all file systems by default.\n\
738 \n\
739 "), stdout);
740       fputs (_("\
741 Mandatory arguments to long options are mandatory for short options too.\n\
742 "), stdout);
743       fputs (_("\
744   -a, --all             include dummy file systems\n\
745   -B, --block-size=SIZE  use SIZE-byte blocks\n\
746   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\n\
747   -H, --si              likewise, but use powers of 1000 not 1024\n\
748 "), stdout);
749       fputs (_("\
750   -i, --inodes          list inode information instead of block usage\n\
751   -k                    like --block-size=1K\n\
752   -l, --local           limit listing to local file systems\n\
753       --no-sync         do not invoke sync before getting usage info (default)\n\
754 "), stdout);
755       fputs (_("\
756   -P, --portability     use the POSIX output format\n\
757       --sync            invoke sync before getting usage info\n\
758   -t, --type=TYPE       limit listing to file systems of type TYPE\n\
759   -T, --print-type      print file system type\n\
760   -x, --exclude-type=TYPE   limit listing to file systems not of type TYPE\n\
761   -v                    (ignored)\n\
762 "), stdout);
763       fputs (HELP_OPTION_DESCRIPTION, stdout);
764       fputs (VERSION_OPTION_DESCRIPTION, stdout);
765       fputs (_("\n\
766 SIZE may be (or may be an integer optionally followed by) one of following:\n\
767 kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
768 "), stdout);
769       emit_bug_reporting_address ();
770     }
771   exit (status);
772 }
773
774 int
775 main (int argc, char **argv)
776 {
777   struct stat *stats IF_LINT (= 0);
778
779   initialize_main (&argc, &argv);
780   set_program_name (argv[0]);
781   setlocale (LC_ALL, "");
782   bindtextdomain (PACKAGE, LOCALEDIR);
783   textdomain (PACKAGE);
784
785   atexit (close_stdout);
786
787   fs_select_list = NULL;
788   fs_exclude_list = NULL;
789   inode_format = false;
790   show_all_fs = false;
791   show_listed_fs = false;
792   human_output_opts = -1;
793   print_type = false;
794   file_systems_processed = false;
795   posix_format = false;
796   exit_status = EXIT_SUCCESS;
797
798   for (;;)
799     {
800       int oi = -1;
801       int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options,
802                            &oi);
803       if (c == -1)
804         break;
805
806       switch (c)
807         {
808         case 'a':
809           show_all_fs = true;
810           break;
811         case 'B':
812           {
813             enum strtol_error e = human_options (optarg, &human_output_opts,
814                                                  &output_block_size);
815             if (e != LONGINT_OK)
816               xstrtol_fatal (e, oi, c, long_options, optarg);
817           }
818           break;
819         case 'i':
820           inode_format = true;
821           break;
822         case 'h':
823           human_output_opts = human_autoscale | human_SI | human_base_1024;
824           output_block_size = 1;
825           break;
826         case 'H':
827           human_output_opts = human_autoscale | human_SI;
828           output_block_size = 1;
829           break;
830         case 'k':
831           human_output_opts = 0;
832           output_block_size = 1024;
833           break;
834         case 'l':
835           show_local_fs = true;
836           break;
837         case 'm': /* obsolescent */
838           human_output_opts = 0;
839           output_block_size = 1024 * 1024;
840           break;
841         case 'T':
842           print_type = true;
843           break;
844         case 'P':
845           posix_format = true;
846           break;
847         case SYNC_OPTION:
848           require_sync = true;
849           break;
850         case NO_SYNC_OPTION:
851           require_sync = false;
852           break;
853
854         case 'F':
855           /* Accept -F as a synonym for -t for compatibility with Solaris.  */
856         case 't':
857           add_fs_type (optarg);
858           break;
859
860         case 'v':               /* For SysV compatibility. */
861           /* ignore */
862           break;
863         case 'x':
864           add_excluded_fs_type (optarg);
865           break;
866
867         case_GETOPT_HELP_CHAR;
868         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
869
870         default:
871           usage (EXIT_FAILURE);
872         }
873     }
874
875   if (human_output_opts == -1)
876     {
877       if (posix_format)
878         {
879           human_output_opts = 0;
880           output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024);
881         }
882       else
883         human_options (getenv ("DF_BLOCK_SIZE"),
884                        &human_output_opts, &output_block_size);
885     }
886
887   /* Fail if the same file system type was both selected and excluded.  */
888   {
889     bool match = false;
890     struct fs_type_list *fs_incl;
891     for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
892       {
893         struct fs_type_list *fs_excl;
894         for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
895           {
896             if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
897               {
898                 error (0, 0,
899                        _("file system type %s both selected and excluded"),
900                        quote (fs_incl->fs_name));
901                 match = true;
902                 break;
903               }
904           }
905       }
906     if (match)
907       exit (EXIT_FAILURE);
908   }
909
910   if (optind < argc)
911     {
912       int i;
913
914       /* stat all the given entries to make sure they get automounted,
915          if necessary, before reading the file system table.  */
916       stats = xnmalloc (argc - optind, sizeof *stats);
917       for (i = optind; i < argc; ++i)
918         {
919           if (stat (argv[i], &stats[i - optind]))
920             {
921               error (0, errno, "%s", quote (argv[i]));
922               exit_status = EXIT_FAILURE;
923               argv[i] = NULL;
924             }
925         }
926     }
927
928   mount_list =
929     read_file_system_list ((fs_select_list != NULL
930                             || fs_exclude_list != NULL
931                             || print_type
932                             || show_local_fs));
933
934   if (mount_list == NULL)
935     {
936       /* Couldn't read the table of mounted file systems.
937          Fail if df was invoked with no file name arguments;
938          Otherwise, merely give a warning and proceed.  */
939       int status =          (optind < argc ? 0 : EXIT_FAILURE);
940       const char *warning = (optind < argc ? _("Warning: ") : "");
941       error (status, errno, "%s%s", warning,
942              _("cannot read table of mounted file systems"));
943     }
944
945   if (require_sync)
946     sync ();
947
948   if (optind < argc)
949     {
950       int i;
951
952       /* Display explicitly requested empty file systems. */
953       show_listed_fs = true;
954
955       for (i = optind; i < argc; ++i)
956         if (argv[i])
957           show_entry (argv[i], &stats[i - optind]);
958     }
959   else
960     show_all_entries ();
961
962   if (! file_systems_processed)
963     error (EXIT_FAILURE, 0, _("no file systems processed"));
964
965   exit (exit_status);
966 }