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