(main): Declare to be of type int, not void.
[coreutils.git] / src / df.c
blob12aca2ffb0a57c4bc2b0b9745203669fbf95e707
1 /* df - summarize free disk space
2 Copyright (C) 1991, 1995 Free Software Foundation, Inc.
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.
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.
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
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
19 --human-readable and --megabyte options added by lm@sgi.com. */
21 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <getopt.h>
25 #include <assert.h>
27 #include "mountlist.h"
28 #include "fsusage.h"
29 #include "system.h"
30 #include "error.h"
32 char *xmalloc ();
33 char *xstrdup ();
35 static int selected_fstype __P ((char *fstype));
36 static int excluded_fstype __P ((char *fstype));
37 static void add_excluded_fs_type __P ((char *fstype));
38 static void add_fs_type __P ((char *fstype));
39 static void print_header __P ((void));
40 static void show_entry __P ((char *path, struct stat *statp));
41 static void show_all_entries __P ((void));
42 static void show_dev __P ((char *disk, char *mount_point, char *fstype));
43 static void show_disk __P ((char *disk));
44 static void show_point __P ((char *point, struct stat *statp));
45 static void usage __P ((int status));
47 /* The maximum length of a human-readable string. Be pessimistic
48 and assume `int' is 64-bits wide. Converting 2^63 - 1 gives the
49 14-character string, 8796093022208G. The number being converted
50 is the number of 1024-byte blocks, so we divide by 1024 * 1024. */
51 #define LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS 14
53 /* Name this program was run with. */
54 char *program_name;
56 /* If nonzero, show inode information. */
57 static int inode_format;
59 /* If nonzero, show even filesystems with zero size or
60 uninteresting types. */
61 static int show_all_fs;
63 /* If nonzero, output data for each filesystem corresponding to a
64 command line argument -- even if it's a dummy (automounter) entry. */
65 static int show_listed_fs;
67 /* If nonzero, use variable sized printouts instead of 512-byte blocks. */
68 static int human_blocks;
70 /* If nonzero, use 1K blocks instead of 512-byte blocks. */
71 static int kilobyte_blocks;
73 /* If nonzero, use 1M blocks instead of 512-byte blocks. */
74 static int megabyte_blocks;
76 /* If nonzero, use the POSIX output format. */
77 static int posix_format;
79 /* If nonzero, invoke the `sync' system call before getting any usage data.
80 Using this option can make df very slow, especially with many or very
81 busy disks. Default to nonzero because the sync call does make a
82 difference on some systems -- SunOs4.1.3, for one. I have been assured
83 that it is *not* necessary on Linux, so there should be a way to
84 configure this. FIXME. */
85 static int require_sync = 1;
87 /* Nonzero if errors have occurred. */
88 static int exit_status;
90 /* A filesystem type to display. */
92 struct fs_type_list
94 char *fs_name;
95 struct fs_type_list *fs_next;
98 /* Linked list of filesystem types to display.
99 If `fs_select_list' is NULL, list all types.
100 This table is generated dynamically from command-line options,
101 rather than hardcoding into the program what it thinks are the
102 valid filesystem types; let the user specify any filesystem type
103 they want to, and if there are any filesystems of that type, they
104 will be shown.
106 Some filesystem types:
107 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
109 static struct fs_type_list *fs_select_list;
111 /* Linked list of filesystem types to omit.
112 If the list is empty, don't exclude any types. */
114 static struct fs_type_list *fs_exclude_list;
116 /* Linked list of mounted filesystems. */
117 static struct mount_entry *mount_list;
119 /* If nonzero, display usage information and exit. */
120 static int show_help;
122 /* If nonzero, print the version on standard output and exit. */
123 static int show_version;
125 /* If nonzero, print filesystem type as well. */
126 static int print_type;
128 static struct option const long_options[] =
130 {"all", no_argument, &show_all_fs, 1},
131 {"inodes", no_argument, &inode_format, 1},
132 {"human", no_argument, 0, 'h'},
133 {"kilobytes", no_argument, 0, 'k'},
134 {"megabytes", no_argument, 0, 'm'},
135 {"portability", no_argument, &posix_format, 1},
136 {"print-type", no_argument, &print_type, 1},
137 {"sync", no_argument, 0, 129},
138 {"no-sync", no_argument, 0, 130},
139 {"type", required_argument, 0, 't'},
140 {"exclude-type", required_argument, 0, 'x'},
141 {"help", no_argument, &show_help, 1},
142 {"version", no_argument, &show_version, 1},
143 {NULL, 0, NULL, 0}
146 void
147 main (int argc, char **argv)
149 int i;
150 struct stat *stats;
152 program_name = argv[0];
153 setlocale (LC_ALL, "");
154 bindtextdomain (PACKAGE, LOCALEDIR);
155 textdomain (PACKAGE);
157 fs_select_list = NULL;
158 fs_exclude_list = NULL;
159 inode_format = 0;
160 show_all_fs = 0;
161 show_listed_fs = 0;
163 if (getenv ("POSIXLY_CORRECT"))
164 kilobyte_blocks = 0;
165 else
167 char *bs;
168 kilobyte_blocks = 1;
169 if ((bs = getenv ("BLOCKSIZE"))
170 && strncmp (bs, "HUMAN", sizeof ("HUMAN") - 1) == 0)
171 human_blocks = 1;
174 print_type = 0;
175 posix_format = 0;
176 exit_status = 0;
178 while ((i = getopt_long (argc, argv, "aihkmPTt:vx:", long_options, NULL))
179 != EOF)
181 switch (i)
183 case 0: /* Long option. */
184 break;
185 case 'a':
186 show_all_fs = 1;
187 break;
188 case 'i':
189 inode_format = 1;
190 break;
191 case 'h':
192 human_blocks = 1;
193 kilobyte_blocks = 1;
194 megabyte_blocks = 0;
195 break;
196 case 'k':
197 human_blocks = 0;
198 kilobyte_blocks = 1;
199 megabyte_blocks = 0;
200 break;
201 case 'm':
202 human_blocks = 0;
203 kilobyte_blocks = 0;
204 megabyte_blocks = 1;
205 break;
206 case 'T':
207 print_type = 1;
208 break;
209 case 'P':
210 posix_format = 1;
211 break;
212 case 129:
213 require_sync = 1;
214 break;
215 case 130:
216 require_sync = 0;
217 break;
218 case 't':
219 add_fs_type (optarg);
220 break;
221 case 'v': /* For SysV compatibility. */
222 break;
223 case 'x':
224 add_excluded_fs_type (optarg);
225 break;
226 default:
227 usage (1);
231 if (show_version)
233 printf ("df - %s\n", PACKAGE_VERSION);
234 exit (0);
237 if (show_help)
238 usage (0);
240 /* Fail if the same file system type was both selected and excluded. */
242 int match = 0;
243 struct fs_type_list *i;
244 for (i = fs_select_list; i; i = i->fs_next)
246 struct fs_type_list *j;
247 for (j = fs_exclude_list; j; j = j->fs_next)
249 if (strcmp (i->fs_name, j->fs_name) == 0)
251 error (0, 0,
252 _("file system type `%s' both selected and excluded"),
253 i->fs_name);
254 match = 1;
255 break;
259 if (match)
260 exit (1);
263 if (optind == argc)
265 #ifdef lint
266 /* Suppress `used before initialized' warning. */
267 stats = NULL;
268 #endif
270 else
272 /* stat all the given entries to make sure they get automounted,
273 if necessary, before reading the filesystem table. */
274 stats = (struct stat *)
275 xmalloc ((argc - optind) * sizeof (struct stat));
276 for (i = optind; i < argc; ++i)
277 if (stat (argv[i], &stats[i - optind]))
279 error (0, errno, "%s", argv[i]);
280 exit_status = 1;
281 argv[i] = NULL;
285 mount_list =
286 read_filesystem_list ((fs_select_list != NULL
287 || fs_exclude_list != NULL
288 || print_type),
289 show_all_fs);
291 if (mount_list == NULL)
292 error (1, errno, _("cannot read table of mounted filesystems"));
294 print_header ();
295 if (require_sync)
296 sync ();
298 if (optind == argc)
299 show_all_entries ();
300 else
302 /* Display explicitly requested empty filesystems. */
303 show_listed_fs = 1;
305 for (i = optind; i < argc; ++i)
306 if (argv[i])
307 show_entry (argv[i], &stats[i - optind]);
310 exit (exit_status);
313 static void
314 print_header (void)
316 printf ("Filesystem ");
318 if (print_type)
319 printf (" Type");
320 else
321 printf (" ");
323 if (inode_format)
324 printf (" Inodes IUsed IFree %%IUsed");
325 else
326 if (megabyte_blocks)
327 printf (" MB-blocks Used Available Capacity");
328 else if (human_blocks)
329 printf (" Size Used Avail Capacity");
330 else
331 printf (" %s Used Available Capacity",
332 kilobyte_blocks ? "1024-blocks" : " 512-blocks");
333 printf (" Mounted on\n");
336 /* Show all mounted filesystems, except perhaps those that are of
337 an unselected type or are empty. */
339 static void
340 show_all_entries (void)
342 struct mount_entry *me;
344 for (me = mount_list; me; me = me->me_next)
345 show_dev (me->me_devname, me->me_mountdir, me->me_type);
348 /* Determine what kind of node PATH is and show the disk usage
349 for it. STATP is the results of `stat' on PATH. */
351 static void
352 show_entry (char *path, struct stat *statp)
354 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
355 show_disk (path);
356 else
357 show_point (path, statp);
360 /* Identify the directory, if any, that device
361 DISK is mounted on, and show its disk usage. */
363 static void
364 show_disk (char *disk)
366 struct mount_entry *me;
368 for (me = mount_list; me; me = me->me_next)
369 if (!strcmp (disk, me->me_devname))
371 show_dev (me->me_devname, me->me_mountdir, me->me_type);
372 return;
374 /* No filesystem is mounted on DISK. */
375 show_dev (disk, (char *) NULL, (char *) NULL);
378 /* Figure out which device file or directory POINT is mounted on
379 and show its disk usage.
380 STATP is the results of `stat' on POINT. */
382 static void
383 show_point (char *point, struct stat *statp)
385 struct stat disk_stats;
386 struct mount_entry *me;
388 for (me = mount_list; me; me = me->me_next)
390 if (me->me_dev == (dev_t) -1)
392 if (stat (me->me_mountdir, &disk_stats) == 0)
393 me->me_dev = disk_stats.st_dev;
394 else
396 error (0, errno, "%s", me->me_mountdir);
397 exit_status = 1;
398 /* So we won't try and fail repeatedly. */
399 me->me_dev = (dev_t) -2;
403 if (statp->st_dev == me->me_dev)
405 /* Skip bogus mtab entries. */
406 if (stat (me->me_mountdir, &disk_stats) != 0 ||
407 disk_stats.st_dev != me->me_dev)
408 continue;
409 show_dev (me->me_devname, me->me_mountdir, me->me_type);
410 return;
413 error (0, 0, _("cannot find mount point for %s"), point);
414 exit_status = 1;
417 /* Convert N_1K_BYTE_BLOCKS to a more readable string than %d would.
418 Most people visually process strings of 3-4 digits effectively,
419 but longer strings of digits are more prone to misinterpretation.
420 Hence, converting to an abbreviated form usually improves readability.
421 Use a suffix indicating multiples of 1024 (M) and 1024*1024 (G).
422 For example, 8500 would be converted to 8.3M, 133456345 to 127G,
423 and so on. Numbers smaller than 1024 get the `K' suffix. */
425 static char *
426 human_readable_1k_blocks (int n_1k_byte_blocks, char *buf, int buf_len)
428 const char *suffix;
429 double amt;
430 char *p;
432 assert (buf_len > LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS);
434 p = buf;
435 amt = n_1k_byte_blocks;
437 if (amt >= 1024 * 1024)
439 amt /= (1024 * 1024);
440 suffix = "G";
442 else if (amt >= 1024)
444 amt /= 1024;
445 suffix = "M";
447 else
449 suffix = "K";
452 if (amt >= 10)
454 sprintf (p, "%4.0f%s", amt, suffix);
456 else if (amt == 0)
458 strcpy (p, "0");
460 else
462 sprintf (p, "%4.1f%s", amt, suffix);
464 return (p);
467 /* Display a space listing for the disk device with absolute path DISK.
468 If MOUNT_POINT is non-NULL, it is the path of the root of the
469 filesystem on DISK.
470 If FSTYPE is non-NULL, it is the type of the filesystem on DISK. */
472 static void
473 show_dev (char *disk, char *mount_point, char *fstype)
475 struct fs_usage fsu;
476 long blocks_used;
477 long blocks_percent_used;
478 long inodes_used;
479 long inodes_percent_used;
480 char *stat_file;
482 if (!selected_fstype (fstype) || excluded_fstype (fstype))
483 return;
485 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
486 program reports on the filesystem that the special file is on.
487 It would be better to report on the unmounted filesystem,
488 but statfs doesn't do that on most systems. */
489 stat_file = mount_point ? mount_point : disk;
491 if (get_fs_usage (stat_file, disk, &fsu))
493 error (0, errno, "%s", stat_file);
494 exit_status = 1;
495 return;
498 if (megabyte_blocks)
500 fsu.fsu_blocks /= 2*1024;
501 fsu.fsu_bfree /= 2*1024;
502 fsu.fsu_bavail /= 2*1024;
504 else if (kilobyte_blocks)
506 fsu.fsu_blocks /= 2;
507 fsu.fsu_bfree /= 2;
508 fsu.fsu_bavail /= 2;
511 if (fsu.fsu_blocks == 0)
513 if (!show_all_fs && !show_listed_fs)
514 return;
515 blocks_used = fsu.fsu_bavail = blocks_percent_used = 0;
517 else
519 blocks_used = fsu.fsu_blocks - fsu.fsu_bfree;
520 blocks_percent_used = (long)
521 (blocks_used * 100.0 / (blocks_used + fsu.fsu_bavail) + 0.5);
524 if (fsu.fsu_files == 0)
526 inodes_used = fsu.fsu_ffree = inodes_percent_used = 0;
528 else
530 inodes_used = fsu.fsu_files - fsu.fsu_ffree;
531 inodes_percent_used = (long)
532 (inodes_used * 100.0 / fsu.fsu_files + 0.5);
535 printf ((print_type ? "%-13s" : "%-20s"), disk);
536 if (strlen (disk) > (print_type ? 13 : 20) && !posix_format)
537 printf ((print_type ? "\n%13s" : "\n%20s"), "");
539 if (print_type)
540 printf (" %-5s ", fstype);
542 if (inode_format)
543 printf (" %7ld %7ld %7ld %5ld%%",
544 fsu.fsu_files, inodes_used, fsu.fsu_ffree, inodes_percent_used);
545 else if (human_blocks)
547 char buf[3][LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1];
548 printf (" %4s %4s %5s %5ld%% ",
549 human_readable_1k_blocks (fsu.fsu_blocks, buf[0],
550 LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1),
551 human_readable_1k_blocks (blocks_used, buf[1],
552 LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1),
553 human_readable_1k_blocks (fsu.fsu_bavail, buf[2],
554 LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1),
555 blocks_percent_used);
557 else
558 printf (" %7ld %7ld %7ld %5ld%% ",
559 fsu.fsu_blocks, blocks_used, fsu.fsu_bavail, blocks_percent_used);
561 if (mount_point)
563 #ifdef HIDE_AUTOMOUNT_PREFIX
564 /* Don't print the first directory name in MOUNT_POINT if it's an
565 artifact of an automounter. This is a bit too aggressive to be
566 the default. */
567 if (strncmp ("/auto/", mount_point, 6) == 0)
568 mount_point += 5;
569 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
570 mount_point += 8;
571 #endif
572 printf (" %s", mount_point);
574 putchar ('\n');
577 /* Add FSTYPE to the list of filesystem types to display. */
579 static void
580 add_fs_type (char *fstype)
582 struct fs_type_list *fsp;
584 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
585 fsp->fs_name = fstype;
586 fsp->fs_next = fs_select_list;
587 fs_select_list = fsp;
590 /* Add FSTYPE to the list of filesystem types to be omitted. */
592 static void
593 add_excluded_fs_type (char *fstype)
595 struct fs_type_list *fsp;
597 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
598 fsp->fs_name = fstype;
599 fsp->fs_next = fs_exclude_list;
600 fs_exclude_list = fsp;
603 /* If FSTYPE is a type of filesystem that should be listed,
604 return nonzero, else zero. */
606 static int
607 selected_fstype (char *fstype)
609 struct fs_type_list *fsp;
611 if (fs_select_list == NULL || fstype == NULL)
612 return 1;
613 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
614 if (!strcmp (fstype, fsp->fs_name))
615 return 1;
616 return 0;
619 /* If FSTYPE is a type of filesystem that should be omitted,
620 return nonzero, else zero. */
622 static int
623 excluded_fstype (char *fstype)
625 struct fs_type_list *fsp;
627 if (fs_exclude_list == NULL || fstype == NULL)
628 return 0;
629 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
630 if (!strcmp (fstype, fsp->fs_name))
631 return 1;
632 return 0;
635 static void
636 usage (int status)
638 if (status != 0)
639 fprintf (stderr, _("Try `%s --help' for more information.\n"),
640 program_name);
641 else
643 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
644 printf (_("\
645 Show information about the filesystem on which each FILE resides,\n\
646 or all filesystems by default.\n\
648 -a, --all include filesystems having 0 blocks\n\
649 -h, --human print sizes in human readable format (e.g. 1K 234M 2G)\n\
650 -i, --inodes list inode information instead of block usage\n\
651 -k, --kilobytes use 1024-byte blocks, not 512 despite POSIXLY_CORRECT\n\
652 -m, --megabytes use 1024K-byte blocks, not 512 despite POSIXLY_CORRECT\n\
653 --sync invoke sync before getting usage info (default)\n\
654 --no-sync do not invoke sync before getting usage info\n\
655 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
656 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
657 -v (ignored)\n\
658 -P, --portability use the POSIX output format\n\
659 FIXME: this should override or conflict with --human and --megabytes\n\
660 FIXME-bug: currently, even with --portability, blocksize is 1024 bytes\n\
661 FIXME-bug: make sure that's allowed by POSIX.\n\
662 -T, --print-type print filesystem type\n\
663 --help display this help and exit\n\
664 --version output version information and exit\n"));
666 exit (status);