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