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