*** empty log message ***
[platform/upstream/coreutils.git] / src / df.c
1 /* df - summarize free disk space
2    Copyright (C) 91, 1995-2006 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 "dirname.h"
30 #include "error.h"
31 #include "fsusage.h"
32 #include "human.h"
33 #include "inttostr.h"
34 #include "mountlist.h"
35 #include "quote.h"
36 #include "save-cwd.h"
37 #include "xgetcwd.h"
38
39 /* The official name of this program (e.g., no `g' prefix).  */
40 #define PROGRAM_NAME "df"
41
42 #define AUTHORS \
43   "Torbjorn Granlund", "David MacKenzie", "Paul Eggert"
44
45 /* Name this program was run with. */
46 char *program_name;
47
48 /* If true, show inode information. */
49 static bool inode_format;
50
51 /* If true, show even file systems with zero size or
52    uninteresting types. */
53 static bool show_all_fs;
54
55 /* If true, show only local file systems.  */
56 static bool show_local_fs;
57
58 /* If true, output data for each file system corresponding to a
59    command line argument -- even if it's a dummy (automounter) entry.  */
60 static bool show_listed_fs;
61
62 /* Human-readable options for output.  */
63 static int human_output_opts;
64
65 /* The units to use when printing sizes.  */
66 static uintmax_t output_block_size;
67
68 /* If true, use the POSIX output format.  */
69 static bool posix_format;
70
71 /* If true, invoke the `sync' system call before getting any usage data.
72    Using this option can make df very slow, especially with many or very
73    busy disks.  Note that this may make a difference on some systems --
74    SunOS 4.1.3, for one.  It is *not* necessary on Linux.  */
75 static bool require_sync;
76
77 /* Desired exit status.  */
78 static int exit_status;
79
80 /* A file system type to display. */
81
82 struct fs_type_list
83 {
84   char *fs_name;
85   struct fs_type_list *fs_next;
86 };
87
88 /* Linked list of file system types to display.
89    If `fs_select_list' is NULL, list all types.
90    This table is generated dynamically from command-line options,
91    rather than hardcoding into the program what it thinks are the
92    valid file system types; let the user specify any file system type
93    they want to, and if there are any file systems of that type, they
94    will be shown.
95
96    Some file system types:
97    4.2 4.3 ufs nfs swap ignore io vm efs dbg */
98
99 static struct fs_type_list *fs_select_list;
100
101 /* Linked list of file system types to omit.
102    If the list is empty, don't exclude any types.  */
103
104 static struct fs_type_list *fs_exclude_list;
105
106 /* Linked list of mounted file systems. */
107 static struct mount_entry *mount_list;
108
109 /* If true, print file system type as well.  */
110 static bool print_type;
111
112 /* For long options that have no equivalent short option, use a
113    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
114 enum
115 {
116   NO_SYNC_OPTION = CHAR_MAX + 1,
117   /* FIXME: --kilobytes is deprecated (but not -k); remove in late 2006 */
118   KILOBYTES_LONG_OPTION,
119   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, KILOBYTES_LONG_OPTION},
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 /* Is FSTYPE a type of file system that should be listed?  */
202
203 static bool
204 selected_fstype (const char *fstype)
205 {
206   const struct fs_type_list *fsp;
207
208   if (fs_select_list == NULL || fstype == NULL)
209     return true;
210   for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
211     if (STREQ (fstype, fsp->fs_name))
212       return true;
213   return false;
214 }
215
216 /* Is FSTYPE a type of file system that should be omitted?  */
217
218 static bool
219 excluded_fstype (const char *fstype)
220 {
221   const struct fs_type_list *fsp;
222
223   if (fs_exclude_list == NULL || fstype == NULL)
224     return false;
225   for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
226     if (STREQ (fstype, fsp->fs_name))
227       return true;
228   return false;
229 }
230
231 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
232    except:
233
234     - If NEGATIVE, then N represents a negative number,
235       expressed in two's complement.
236     - Otherwise, return "-" if N is UINTMAX_MAX.  */
237
238 static char const *
239 df_readable (bool negative, uintmax_t n, char *buf,
240              uintmax_t input_units, uintmax_t output_units)
241 {
242   if (n == UINTMAX_MAX && !negative)
243     return "-";
244   else
245     {
246       char *p = human_readable (negative ? -n : n, buf + negative,
247                                 human_output_opts, input_units, output_units);
248       if (negative)
249         *--p = '-';
250       return p;
251     }
252 }
253
254 /* Display a space listing for the disk device with absolute file name DISK.
255    If MOUNT_POINT is non-NULL, it is the name of the root of the
256    file system on DISK.
257    If STAT_FILE is non-null, it is the name of a file within the file
258    system that the user originally asked for; this provides better
259    diagnostics, and sometimes it provides better results on networked
260    file systems that give different free-space results depending on
261    where in the file system you probe.
262    If FSTYPE is non-NULL, it is the type of the file system on DISK.
263    If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
264    not be able to produce statistics in this case.
265    ME_DUMMY and ME_REMOTE are the mount entry flags.  */
266
267 static void
268 show_dev (char const *disk, char const *mount_point,
269           char const *stat_file, char const *fstype,
270           bool me_dummy, bool me_remote)
271 {
272   struct fs_usage fsu;
273   char buf[3][LONGEST_HUMAN_READABLE + 2];
274   int width;
275   int use_width;
276   uintmax_t input_units;
277   uintmax_t output_units;
278   uintmax_t total;
279   uintmax_t available;
280   bool negate_available;
281   uintmax_t available_to_root;
282   uintmax_t used;
283   bool negate_used;
284   double pct = -1;
285
286   if (me_remote & show_local_fs)
287     return;
288
289   if (me_dummy & !show_all_fs & !show_listed_fs)
290     return;
291
292   if (!selected_fstype (fstype) || excluded_fstype (fstype))
293     return;
294
295   /* If MOUNT_POINT is NULL, then the file system is not mounted, and this
296      program reports on the file system that the special file is on.
297      It would be better to report on the unmounted file system,
298      but statfs doesn't do that on most systems.  */
299   if (!stat_file)
300     stat_file = mount_point ? mount_point : disk;
301
302   if (get_fs_usage (stat_file, disk, &fsu))
303     {
304       error (0, errno, "%s", quote (stat_file));
305       exit_status = EXIT_FAILURE;
306       return;
307     }
308
309   if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
310     return;
311
312   if (! disk)
313     disk = "-";                 /* unknown */
314   if (! fstype)
315     fstype = "-";               /* unknown */
316
317   /* df.c reserved 5 positions for fstype,
318      but that does not suffice for type iso9660 */
319   if (print_type)
320     {
321       size_t disk_name_len = strlen (disk);
322       size_t fstype_len = strlen (fstype);
323       if (disk_name_len + fstype_len < 18)
324         printf ("%s%*s  ", disk, 18 - (int) disk_name_len, fstype);
325       else if (!posix_format)
326         printf ("%s\n%18s  ", disk, fstype);
327       else
328         printf ("%s %s", disk, fstype);
329     }
330   else
331     {
332       if (strlen (disk) > 20 && !posix_format)
333         printf ("%s\n%20s", disk, "");
334       else
335         printf ("%-20s", disk);
336     }
337
338   if (inode_format)
339     {
340       width = 7;
341       use_width = 5;
342       input_units = output_units = 1;
343       total = fsu.fsu_files;
344       available = fsu.fsu_ffree;
345       negate_available = false;
346       available_to_root = available;
347     }
348   else
349     {
350       width = (human_output_opts & human_autoscale
351                ? 5 + ! (human_output_opts & human_base_1024)
352                : 9);
353       use_width = ((posix_format
354                     && ! (human_output_opts & human_autoscale))
355                    ? 8 : 4);
356       input_units = fsu.fsu_blocksize;
357       output_units = output_block_size;
358       total = fsu.fsu_blocks;
359       available = fsu.fsu_bavail;
360       negate_available = (fsu.fsu_bavail_top_bit_set
361                           & (available != UINTMAX_MAX));
362       available_to_root = fsu.fsu_bfree;
363     }
364
365   used = UINTMAX_MAX;
366   negate_used = false;
367   if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX)
368     {
369       used = total - available_to_root;
370       negate_used = (total < available_to_root);
371     }
372
373   printf (" %*s %*s %*s ",
374           width, df_readable (false, total,
375                               buf[0], input_units, output_units),
376           width, df_readable (negate_used, used,
377                               buf[1], input_units, output_units),
378           width, df_readable (negate_available, available,
379                               buf[2], input_units, output_units));
380
381   if (used == UINTMAX_MAX || available == UINTMAX_MAX)
382     ;
383   else if (!negate_used
384            && used <= TYPE_MAXIMUM (uintmax_t) / 100
385            && used + available != 0
386            && (used + available < used) == negate_available)
387     {
388       uintmax_t u100 = used * 100;
389       uintmax_t nonroot_total = used + available;
390       pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
391     }
392   else
393     {
394       /* The calculation cannot be done easily with integer
395          arithmetic.  Fall back on floating point.  This can suffer
396          from minor rounding errors, but doing it exactly requires
397          multiple precision arithmetic, and it's not worth the
398          aggravation.  */
399       double u = negate_used ? - (double) - used : used;
400       double a = negate_available ? - (double) - available : available;
401       double nonroot_total = u + a;
402       if (nonroot_total)
403         {
404           long int lipct = pct = u * 100 / nonroot_total;
405           double ipct = lipct;
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 file system 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 = NULL;              /* The malloced mount point.  */
445
446   if (save_cwd (&cwd) != 0)
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 file system 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) != 0)
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 true.  Otherwise, return false.  */
525 static bool
526 show_disk (char const *disk)
527 {
528   struct mount_entry const *me;
529   struct mount_entry const *best_match = NULL;
530
531   for (me = mount_list; me; me = me->me_next)
532     if (STREQ (disk, me->me_devname))
533       best_match = me;
534
535   if (best_match)
536     {
537       show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
538                 best_match->me_type, best_match->me_dummy,
539                 best_match->me_remote);
540       return true;
541     }
542
543   return false;
544 }
545
546 /* Figure out which device file or directory POINT is mounted on
547    and show its disk usage.
548    STATP must be the result of `stat (POINT, STATP)'.  */
549 static void
550 show_point (const char *point, const struct stat *statp)
551 {
552   struct stat disk_stats;
553   struct mount_entry *me;
554   struct mount_entry const *best_match = NULL;
555
556   /* If POINT is an absolute file name, see if we can find the
557      mount point without performing any extra stat calls at all.  */
558   if (*point == '/')
559     {
560       /* Find the best match: prefer non-dummies, and then prefer the
561          last match if there are ties.  */
562
563       for (me = mount_list; me; me = me->me_next)
564         if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs")
565             && (!best_match || best_match->me_dummy || !me->me_dummy))
566           best_match = me;
567     }
568
569   /* Calculate the real absolute file name for POINT, and use that to find
570      the mount point.  This avoids statting unavailable mount points,
571      which can hang df.  */
572   if (! best_match)
573     {
574       char *resolved = canonicalize_file_name (point);
575
576       if (resolved && resolved[0] == '/')
577         {
578           size_t resolved_len = strlen (resolved);
579           size_t best_match_len = 0;
580
581           for (me = mount_list; me; me = me->me_next)
582             if (!STREQ (me->me_type, "lofs")
583                 && (!best_match || best_match->me_dummy || !me->me_dummy))
584               {
585                 size_t len = strlen (me->me_mountdir);
586                 if (best_match_len <= len && len <= resolved_len
587                     && (len == 1 /* root file system */
588                         || ((len == resolved_len || resolved[len] == '/')
589                             && strncmp (me->me_mountdir, resolved, len) == 0)))
590                   {
591                     best_match = me;
592                     best_match_len = len;
593                   }
594               }
595         }
596
597       free (resolved);
598
599       if (best_match
600           && (stat (best_match->me_mountdir, &disk_stats) != 0
601               || disk_stats.st_dev != statp->st_dev))
602         best_match = NULL;
603     }
604
605   if (! best_match)
606     for (me = mount_list; me; me = me->me_next)
607       {
608         if (me->me_dev == (dev_t) -1)
609           {
610             if (stat (me->me_mountdir, &disk_stats) == 0)
611               me->me_dev = disk_stats.st_dev;
612             else
613               {
614                 /* Report only I/O errors.  Other errors might be
615                    caused by shadowed mount points, which means POINT
616                    can't possibly be on this file system.  */
617                 if (errno == EIO)
618                   {
619                     error (0, errno, "%s", quote (me->me_mountdir));
620                     exit_status = EXIT_FAILURE;
621                   }
622
623                 /* So we won't try and fail repeatedly. */
624                 me->me_dev = (dev_t) -2;
625               }
626           }
627
628         if (statp->st_dev == me->me_dev
629             && !STREQ (me->me_type, "lofs")
630             && (!best_match || best_match->me_dummy || !me->me_dummy))
631           {
632             /* Skip bogus mtab entries.  */
633             if (stat (me->me_mountdir, &disk_stats) != 0
634                 || disk_stats.st_dev != me->me_dev)
635               me->me_dev = (dev_t) -2;
636             else
637               best_match = me;
638           }
639       }
640
641   if (best_match)
642     show_dev (best_match->me_devname, best_match->me_mountdir, point,
643               best_match->me_type, best_match->me_dummy, best_match->me_remote);
644   else
645     {
646       /* We couldn't find the mount entry corresponding to POINT.  Go ahead and
647          print as much info as we can; methods that require the device to be
648          present will fail at a later point.  */
649
650       /* Find the actual mount point.  */
651       char *mp = find_mount_point (point, statp);
652       if (mp)
653         {
654           show_dev (NULL, mp, NULL, NULL, false, false);
655           free (mp);
656         }
657     }
658 }
659
660 /* Determine what kind of node NAME is and show the disk usage
661    for it.  STATP is the results of `stat' on NAME.  */
662
663 static void
664 show_entry (char const *name, struct stat const *statp)
665 {
666   if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
667       && show_disk (name))
668     return;
669
670   show_point (name, statp);
671 }
672
673 /* Show all mounted file systems, except perhaps those that are of
674    an unselected type or are empty. */
675
676 static void
677 show_all_entries (void)
678 {
679   struct mount_entry *me;
680
681   for (me = mount_list; me; me = me->me_next)
682     show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
683               me->me_dummy, me->me_remote);
684 }
685
686 /* Add FSTYPE to the list of file system types to display. */
687
688 static void
689 add_fs_type (const char *fstype)
690 {
691   struct fs_type_list *fsp;
692
693   fsp = xmalloc (sizeof *fsp);
694   fsp->fs_name = (char *) fstype;
695   fsp->fs_next = fs_select_list;
696   fs_select_list = fsp;
697 }
698
699 /* Add FSTYPE to the list of file system types to be omitted. */
700
701 static void
702 add_excluded_fs_type (const char *fstype)
703 {
704   struct fs_type_list *fsp;
705
706   fsp = xmalloc (sizeof *fsp);
707   fsp->fs_name = (char *) fstype;
708   fsp->fs_next = fs_exclude_list;
709   fs_exclude_list = fsp;
710 }
711
712 void
713 usage (int status)
714 {
715   if (status != EXIT_SUCCESS)
716     fprintf (stderr, _("Try `%s --help' for more information.\n"),
717              program_name);
718   else
719     {
720       printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
721       fputs (_("\
722 Show information about the file system on which each FILE resides,\n\
723 or all file systems by default.\n\
724 \n\
725 "), stdout);
726       fputs (_("\
727 Mandatory arguments to long options are mandatory for short options too.\n\
728 "), stdout);
729       fputs (_("\
730   -a, --all             include dummy file systems\n\
731   -B, --block-size=SIZE use SIZE-byte blocks\n\
732   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\n\
733   -H, --si              likewise, but use powers of 1000 not 1024\n\
734 "), stdout);
735       fputs (_("\
736   -i, --inodes          list inode information instead of block usage\n\
737   -k                    like --block-size=1K\n\
738   -l, --local           limit listing to local file systems\n\
739       --no-sync         do not invoke sync before getting usage info (default)\n\
740 "), stdout);
741       fputs (_("\
742   -P, --portability     use the POSIX output format\n\
743       --sync            invoke sync before getting usage info\n\
744   -t, --type=TYPE       limit listing to file systems of type TYPE\n\
745   -T, --print-type      print file system type\n\
746   -x, --exclude-type=TYPE   limit listing to file systems not of type TYPE\n\
747   -v                    (ignored)\n\
748 "), stdout);
749       fputs (HELP_OPTION_DESCRIPTION, stdout);
750       fputs (VERSION_OPTION_DESCRIPTION, stdout);
751       fputs (_("\n\
752 SIZE may be (or may be an integer optionally followed by) one of following:\n\
753 kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
754 "), stdout);
755       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
756     }
757   exit (status);
758 }
759
760 int
761 main (int argc, char **argv)
762 {
763   int c;
764   struct stat *stats IF_LINT (= 0);
765   int n_valid_args = 0;
766
767   initialize_main (&argc, &argv);
768   program_name = argv[0];
769   setlocale (LC_ALL, "");
770   bindtextdomain (PACKAGE, LOCALEDIR);
771   textdomain (PACKAGE);
772
773   atexit (close_stdout);
774
775   fs_select_list = NULL;
776   fs_exclude_list = NULL;
777   inode_format = false;
778   show_all_fs = false;
779   show_listed_fs = false;
780
781   human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false,
782                                      &output_block_size);
783
784   print_type = false;
785   posix_format = false;
786   exit_status = EXIT_SUCCESS;
787
788   while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
789          != -1)
790     {
791       switch (c)
792         {
793         case 'a':
794           show_all_fs = true;
795           break;
796         case 'B':
797           human_output_opts = human_options (optarg, true, &output_block_size);
798           break;
799         case 'i':
800           inode_format = true;
801           break;
802         case 'h':
803           human_output_opts = human_autoscale | human_SI | human_base_1024;
804           output_block_size = 1;
805           break;
806         case 'H':
807           human_output_opts = human_autoscale | human_SI;
808           output_block_size = 1;
809           break;
810         case KILOBYTES_LONG_OPTION:
811           error (0, 0,
812                  _("the --kilobytes option is deprecated; use -k instead"));
813           /* fall through */
814         case 'k':
815           human_output_opts = 0;
816           output_block_size = 1024;
817           break;
818         case 'l':
819           show_local_fs = true;
820           break;
821         case 'm': /* obsolescent */
822           human_output_opts = 0;
823           output_block_size = 1024 * 1024;
824           break;
825         case 'T':
826           print_type = true;
827           break;
828         case 'P':
829           posix_format = true;
830           break;
831         case SYNC_OPTION:
832           require_sync = true;
833           break;
834         case NO_SYNC_OPTION:
835           require_sync = false;
836           break;
837
838         case 'F':
839           /* Accept -F as a synonym for -t for compatibility with Solaris.  */
840         case 't':
841           add_fs_type (optarg);
842           break;
843
844         case 'v':               /* For SysV compatibility. */
845           /* ignore */
846           break;
847         case 'x':
848           add_excluded_fs_type (optarg);
849           break;
850
851         case_GETOPT_HELP_CHAR;
852         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
853
854         default:
855           usage (EXIT_FAILURE);
856         }
857     }
858
859   /* Fail if the same file system type was both selected and excluded.  */
860   {
861     bool match = false;
862     struct fs_type_list *fs_incl;
863     for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
864       {
865         struct fs_type_list *fs_excl;
866         for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
867           {
868             if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
869               {
870                 error (0, 0,
871                        _("file system type %s both selected and excluded"),
872                        quote (fs_incl->fs_name));
873                 match = true;
874                 break;
875               }
876           }
877       }
878     if (match)
879       exit (EXIT_FAILURE);
880   }
881
882   if (optind < argc)
883     {
884       int i;
885
886       /* stat all the given entries to make sure they get automounted,
887          if necessary, before reading the file system table.  */
888       stats = xnmalloc (argc - optind, sizeof *stats);
889       for (i = optind; i < argc; ++i)
890         {
891           if (stat (argv[i], &stats[i - optind]))
892             {
893               error (0, errno, "%s", quote (argv[i]));
894               exit_status = EXIT_FAILURE;
895               argv[i] = NULL;
896             }
897           else
898             {
899               ++n_valid_args;
900             }
901         }
902     }
903
904   mount_list =
905     read_file_system_list ((fs_select_list != NULL
906                             || fs_exclude_list != NULL
907                             || print_type
908                             || show_local_fs));
909
910   if (mount_list == NULL)
911     {
912       /* Couldn't read the table of mounted file systems.
913          Fail if df was invoked with no file name arguments;
914          Otherwise, merely give a warning and proceed.  */
915       const char *warning = (optind < argc ? _("Warning: ") : "");
916       int status = (optind < argc ? 0 : EXIT_FAILURE);
917       error (status, errno,
918              _("%scannot read table of mounted file systems"), warning);
919     }
920
921   if (require_sync)
922     sync ();
923
924   if (optind < argc)
925     {
926       int i;
927
928       /* Display explicitly requested empty file systems. */
929       show_listed_fs = true;
930
931       if (n_valid_args > 0)
932         print_header ();
933
934       for (i = optind; i < argc; ++i)
935         if (argv[i])
936           show_entry (argv[i], &stats[i - optind]);
937     }
938   else
939     {
940       print_header ();
941       show_all_entries ();
942     }
943
944   exit (exit_status);
945 }