Most .c files (AUTHORS): Revert the WRITTEN_BY/AUTHORS change
[platform/upstream/coreutils.git] / src / stat.c
1 /* stat.c -- display file or filesystem status
2    Copyright (C) 2001, 2002, 2003 Free Software Foundation.
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 Michael Meskes.  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #if HAVE_SYS_STATVFS_H && HAVE_STRUCT_STATVFS_F_BASETYPE
27 # include <sys/statvfs.h>
28 #elif HAVE_SYS_VFS_H
29 # include <sys/vfs.h>
30 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
31 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
32    It does have statvfs.h, but shouldn't use it, since it doesn't
33    HAVE_STRUCT_STATVFS_F_BASETYPE.  So find a clean way to fix it.  */
34 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
35 # include <sys/param.h>
36 # include <sys/mount.h>
37 # if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
38 /* Ultrix 4.4 needs these for the declaration of struct statfs.  */
39 #  include <netinet/in.h>
40 #  include <nfs/nfs_clnt.h>
41 #  include <nfs/vfs.h>
42 # endif
43 #endif
44
45 #include "system.h"
46
47 #include "error.h"
48 #include "filemode.h"
49 #include "file-type.h"
50 #include "fs.h"
51 #include "getopt.h"
52 #include "quote.h"
53 #include "quotearg.h"
54 #include "strftime.h"
55 #include "xreadlink.h"
56
57 #define NAMEMAX_FORMAT PRIuMAX
58
59 #if HAVE_STRUCT_STATVFS_F_BASETYPE
60 # define STRUCT_STATVFS struct statvfs
61 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS
62 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
63 #  define SB_F_NAMEMAX(S) ((uintmax_t) ((S)->f_namemax))
64 # endif
65 #else
66 # define STRUCT_STATVFS struct statfs
67 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS
68 # if HAVE_STRUCT_STATFS_F_NAMELEN
69 #  define SB_F_NAMEMAX(S) ((uintmax_t) ((S)->f_namelen))
70 # endif
71 #endif
72
73 #ifndef SB_F_NAMEMAX
74 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen.  */
75 # define SB_F_NAMEMAX(S) "*"
76 # undef NAMEMAX_FORMAT
77 # define NAMEMAX_FORMAT "s"
78 #endif
79
80 #if HAVE_STRUCT_STATVFS_F_BASETYPE
81 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
82 #else
83 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
84 #  define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
85 # endif
86 #endif
87
88 #define PROGRAM_NAME "stat"
89
90 #define AUTHORS "Michael Meskes"
91
92 static struct option const long_options[] = {
93   {"link", no_argument, 0, 'l'}, /* deprecated.  FIXME: remove in 2003 */
94   {"dereference", no_argument, 0, 'L'},
95   {"format", required_argument, 0, 'c'},
96   {"filesystem", no_argument, 0, 'f'},
97   {"terse", no_argument, 0, 't'},
98   {GETOPT_HELP_OPTION_DECL},
99   {GETOPT_VERSION_OPTION_DECL},
100   {NULL, 0, NULL, 0}
101 };
102
103 /* Nonzero means we should exit with EXIT_FAILURE upon completion.  */
104 static int G_fail;
105
106 char *program_name;
107
108 /* Return the type of the specified file system.
109    Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
110    Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
111    Still others have neither and have to get by with f_type (Linux).  */
112 static char *
113 human_fstype (STRUCT_STATVFS const *statfsbuf)
114 {
115 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
116   /* Cast away the `const' attribute.  */
117   return (char *) statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
118 #else
119   char const *type;
120   switch (statfsbuf->f_type)
121     {
122 # if defined __linux__
123
124       /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
125          statements must be followed by a hexadecimal constant in
126          a comment.  The S_MAGIC_... name and constant are automatically
127          combined to produce the #define directives in fs.h.  */
128
129     case S_MAGIC_AFFS: /* 0xADFF */
130       type = "affs";
131       break;
132     case S_MAGIC_DEVPTS: /* 0x1CD1 */
133       type = "devpts";
134       break;
135     case S_MAGIC_EXT: /* 0x137D */
136       type = "ext";
137       break;
138     case S_MAGIC_EXT2_OLD: /* 0xEF51 */
139       type = "ext2";
140       break;
141     case S_MAGIC_EXT2: /* 0xEF53 */
142       type = "ext2/ext3";
143       break;
144     case S_MAGIC_HPFS: /* 0xF995E849 */
145       type = "hpfs";
146       break;
147     case S_MAGIC_ISOFS: /* 0x9660 */
148       type = "isofs";
149       break;
150     case S_MAGIC_ISOFS_WIN: /* 0x4000 */
151       type = "isofs";
152       break;
153     case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
154       type = "isofs";
155       break;
156     case S_MAGIC_MINIX: /* 0x137F */
157       type = "minix";
158       break;
159     case S_MAGIC_MINIX_30: /* 0x138F */
160       type = "minix (30 char.)";
161       break;
162     case S_MAGIC_MINIX_V2: /* 0x2468 */
163       type = "minix v2";
164       break;
165     case S_MAGIC_MINIX_V2_30: /* 0x2478 */
166       type = "minix v2 (30 char.)";
167       break;
168     case S_MAGIC_MSDOS: /* 0x4d44 */
169       type = "msdos";
170       break;
171     case S_MAGIC_FAT: /* 0x4006 */
172       type = "fat";
173       break;
174     case S_MAGIC_NCP: /* 0x564c */
175       type = "novell";
176       break;
177     case S_MAGIC_NFS: /* 0x6969 */
178       type = "nfs";
179       break;
180     case S_MAGIC_PROC: /* 0x9fa0 */
181       type = "proc";
182       break;
183     case S_MAGIC_SMB: /* 0x517B */
184       type = "smb";
185       break;
186     case S_MAGIC_XENIX: /* 0x012FF7B4 */
187       type = "xenix";
188       break;
189     case S_MAGIC_SYSV4: /* 0x012FF7B5 */
190       type = "sysv4";
191       break;
192     case S_MAGIC_SYSV2: /* 0x012FF7B6 */
193       type = "sysv2";
194       break;
195     case S_MAGIC_COH: /* 0x012FF7B7 */
196       type = "coh";
197       break;
198     case S_MAGIC_UFS: /* 0x00011954 */
199       type = "ufs";
200       break;
201     case S_MAGIC_XIAFS: /* 0x012FD16D */
202       type = "xia";
203       break;
204     case S_MAGIC_NTFS: /* 0x5346544e */
205       type = "ntfs";
206       break;
207     case S_MAGIC_TMPFS: /* 0x1021994 */
208       type = "tmpfs";
209       break;
210     case S_MAGIC_REISERFS: /* 0x52654973 */
211       type = "reiserfs";
212       break;
213     case S_MAGIC_CRAMFS: /* 0x28cd3d45 */
214       type = "cramfs";
215       break;
216     case S_MAGIC_ROMFS: /* 0x7275 */
217       type = "romfs";
218       break;
219 # elif __GNU__
220     case FSTYPE_UFS:
221       type = "ufs";
222       break;
223     case FSTYPE_NFS:
224       type = "nfs";
225       break;
226     case FSTYPE_GFS:
227       type = "gfs";
228       break;
229     case FSTYPE_LFS:
230       type = "lfs";
231       break;
232     case FSTYPE_SYSV:
233       type = "sysv";
234       break;
235     case FSTYPE_FTP:
236       type = "ftp";
237       break;
238     case FSTYPE_TAR:
239       type = "tar";
240       break;
241     case FSTYPE_AR:
242       type = "ar";
243       break;
244     case FSTYPE_CPIO:
245       type = "cpio";
246       break;
247     case FSTYPE_MSLOSS:
248       type = "msloss";
249       break;
250     case FSTYPE_CPM:
251       type = "cpm";
252       break;
253     case FSTYPE_HFS:
254       type = "hfs";
255       break;
256     case FSTYPE_DTFS:
257       type = "dtfs";
258       break;
259     case FSTYPE_GRFS:
260       type = "grfs";
261       break;
262     case FSTYPE_TERM:
263       type = "term";
264       break;
265     case FSTYPE_DEV:
266       type = "dev";
267       break;
268     case FSTYPE_PROC:
269       type = "proc";
270       break;
271     case FSTYPE_IFSOCK:
272       type = "ifsock";
273       break;
274     case FSTYPE_AFS:
275       type = "afs";
276       break;
277     case FSTYPE_DFS:
278       type = "dfs";
279       break;
280     case FSTYPE_PROC9:
281       type = "proc9";
282       break;
283     case FSTYPE_SOCKET:
284       type = "socket";
285       break;
286     case FSTYPE_MISC:
287       type = "misc";
288       break;
289     case FSTYPE_EXT2FS:
290       type = "ext2/ext3";
291       break;
292     case FSTYPE_HTTP:
293       type = "http";
294       break;
295     case FSTYPE_MEMFS:
296       type = "memfs";
297       break;
298     case FSTYPE_ISO9660:
299       type = "iso9660";
300       break;
301 # endif
302     default:
303       type = NULL;
304       break;
305     }
306
307   if (type)
308     return (char *) type;
309
310   {
311     static char buf[sizeof "UNKNOWN (0x%x)" - 2
312                     + 2 * sizeof (statfsbuf->f_type)];
313     sprintf (buf, "UNKNOWN (0x%x)", statfsbuf->f_type);
314     return buf;
315   }
316 #endif
317 }
318
319 static char *
320 human_access (struct stat const *statbuf)
321 {
322   static char modebuf[11];
323   mode_string (statbuf->st_mode, modebuf);
324   modebuf[10] = 0;
325   return modebuf;
326 }
327
328 static char *
329 human_time (time_t const *t)
330 {
331   static char str[80];
332   struct tm *tm = localtime (t);
333   if (tm == NULL)
334     {
335       G_fail = 1;
336       return (char *) _("*** invalid date/time ***");
337     }
338   nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, 0);
339   return str;
340 }
341
342 /* print statfs info */
343 static void
344 print_statfs (char *pformat, char m, char const *filename,
345               void const *data)
346 {
347   STRUCT_STATVFS const *statfsbuf = data;
348
349   switch (m)
350     {
351     case 'n':
352       strcat (pformat, "s");
353       printf (pformat, filename);
354       break;
355
356     case 'i':
357 #if HAVE_STRUCT_STATXFS_F_FSID___VAL
358       strcat (pformat, "x %-8x");
359       printf (pformat, statfsbuf->f_fsid.__val[0], /* u_long */
360               statfsbuf->f_fsid.__val[1]);
361 #else
362       strcat (pformat, "Lx");
363       printf (pformat, statfsbuf->f_fsid);
364 #endif
365       break;
366
367     case 'l':
368       strcat (pformat, NAMEMAX_FORMAT);
369       printf (pformat, SB_F_NAMEMAX (statfsbuf));
370       break;
371     case 't':
372 #if HAVE_STRUCT_STATXFS_F_TYPE
373       strcat (pformat, "lx");
374       printf (pformat, (long int) (statfsbuf->f_type));  /* no equiv. */
375 #else
376       fputc ('*', stdout);
377 #endif
378       break;
379     case 'T':
380       strcat (pformat, "s");
381       printf (pformat, human_fstype (statfsbuf));
382       break;
383     case 'b':
384       strcat (pformat, PRIdMAX);
385       printf (pformat, (intmax_t) (statfsbuf->f_blocks));
386       break;
387     case 'f':
388       strcat (pformat, PRIdMAX);
389       printf (pformat, (intmax_t) (statfsbuf->f_bfree));
390       break;
391     case 'a':
392       strcat (pformat, PRIdMAX);
393       printf (pformat, (intmax_t) (statfsbuf->f_bavail));
394       break;
395     case 's':
396       strcat (pformat, "ld");
397       printf (pformat, (long int) (statfsbuf->f_bsize));
398       break;
399     case 'c':
400       strcat (pformat, PRIdMAX);
401       printf (pformat, (intmax_t) (statfsbuf->f_files));
402       break;
403     case 'd':
404       strcat (pformat, PRIdMAX);
405       printf (pformat, (intmax_t) (statfsbuf->f_ffree));
406       break;
407
408     default:
409       strcat (pformat, "c");
410       printf (pformat, m);
411       break;
412     }
413 }
414
415 /* print stat info */
416 static void
417 print_stat (char *pformat, char m, char const *filename, void const *data)
418 {
419   struct stat *statbuf = (struct stat *) data;
420   struct passwd *pw_ent;
421   struct group *gw_ent;
422
423   switch (m)
424     {
425     case 'n':
426       strcat (pformat, "s");
427       printf (pformat, filename);
428       break;
429     case 'N':
430       strcat (pformat, "s");
431       if (S_ISLNK (statbuf->st_mode))
432         {
433           char *linkname = xreadlink (filename);
434           if (linkname == NULL)
435             {
436               error (0, errno, _("cannot read symbolic link %s"),
437                      quote (filename));
438               return;
439             }
440           /*printf("\"%s\" -> \"%s\"", filename, linkname); */
441           printf (pformat, quote (filename));
442           printf (" -> ");
443           printf (pformat, quote (linkname));
444         }
445       else
446         {
447           printf (pformat, quote (filename));
448         }
449       break;
450     case 'd':
451       strcat (pformat, "d");
452       printf (pformat, (int) statbuf->st_dev);
453       break;
454     case 'D':
455       strcat (pformat, "x");
456       printf (pformat, (int) statbuf->st_dev);
457       break;
458     case 'i':
459       strcat (pformat, "d");
460       printf (pformat, (int) statbuf->st_ino);
461       break;
462     case 'a':
463       strcat (pformat, "o");
464       printf (pformat, statbuf->st_mode & 07777);
465       break;
466     case 'A':
467       strcat (pformat, "s");
468       printf (pformat, human_access (statbuf));
469       break;
470     case 'f':
471       strcat (pformat, "x");
472       printf (pformat, statbuf->st_mode);
473       break;
474     case 'F':
475       strcat (pformat, "s");
476       printf (pformat, file_type (statbuf));
477       break;
478     case 'h':
479       strcat (pformat, "d");
480       printf (pformat, (int) statbuf->st_nlink);
481       break;
482     case 'u':
483       strcat (pformat, "d");
484       printf (pformat, statbuf->st_uid);
485       break;
486     case 'U':
487       strcat (pformat, "s");
488       setpwent ();
489       pw_ent = getpwuid (statbuf->st_uid);
490       printf (pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN");
491       break;
492     case 'g':
493       strcat (pformat, "d");
494       printf (pformat, statbuf->st_gid);
495       break;
496     case 'G':
497       strcat (pformat, "s");
498       setgrent ();
499       gw_ent = getgrgid (statbuf->st_gid);
500       printf (pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
501       break;
502     case 't':
503       strcat (pformat, "x");
504       printf (pformat, major (statbuf->st_rdev));
505       break;
506     case 'T':
507       strcat (pformat, "x");
508       printf (pformat, minor (statbuf->st_rdev));
509       break;
510     case 's':
511       strcat (pformat, PRIuMAX);
512       printf (pformat, (uintmax_t) (statbuf->st_size));
513       break;
514     case 'B':
515       strcat (pformat, "u");
516       printf (pformat, (unsigned int) ST_NBLOCKSIZE);
517       break;
518     case 'b':
519       strcat (pformat, "u");
520       printf (pformat, (unsigned int) ST_NBLOCKS (*statbuf));
521       break;
522     case 'o':
523       strcat (pformat, "d");
524       printf (pformat, (int) statbuf->st_blksize);
525       break;
526     case 'x':
527       strcat (pformat, "s");
528       printf (pformat, human_time (&(statbuf->st_atime)));
529       break;
530     case 'X':
531       strcat (pformat, "d");
532       printf (pformat, (int) statbuf->st_atime);
533       break;
534     case 'y':
535       strcat (pformat, "s");
536       printf (pformat, human_time (&(statbuf->st_mtime)));
537       break;
538     case 'Y':
539       strcat (pformat, "d");
540       printf (pformat, (int) statbuf->st_mtime);
541       break;
542     case 'z':
543       strcat (pformat, "s");
544       printf (pformat, human_time (&(statbuf->st_ctime)));
545       break;
546     case 'Z':
547       strcat (pformat, "d");
548       printf (pformat, (int) statbuf->st_ctime);
549       break;
550     default:
551       strcat (pformat, "c");
552       printf (pformat, m);
553       break;
554     }
555 }
556
557 static void
558 print_it (char const *masterformat, char const *filename,
559           void (*print_func) (char *, char, char const *, void const *),
560           void const *data)
561 {
562   char *b;
563
564   /* create a working copy of the format string */
565   char *format = xstrdup (masterformat);
566
567   char *dest = xmalloc (strlen (format) + 1);
568
569   b = format;
570   while (b)
571     {
572       char *p = strchr (b, '%');
573       if (p != NULL)
574         {
575           size_t len;
576           *p++ = '\0';
577           fputs (b, stdout);
578
579           len = strspn (p, "#-+.I 0123456789");
580           dest[0] = '%';
581           memcpy (dest + 1, p, len);
582           dest[1 + len] = 0;
583           p += len;
584
585           b = p + 1;
586           switch (*p)
587             {
588             case '\0':
589               b = NULL;
590               /* fall through */
591             case '%':
592               putchar ('%');
593               break;
594             default:
595               print_func (dest, *p, filename, data);
596               break;
597             }
598         }
599       else
600         {
601           fputs (b, stdout);
602           b = NULL;
603         }
604     }
605   free (format);
606   free (dest);
607   fputc ('\n', stdout);
608 }
609
610 /* stat the filesystem and print what we find */
611 static void
612 do_statfs (char const *filename, int terse, char const *format)
613 {
614   STRUCT_STATVFS statfsbuf;
615   int i = statfs (filename, &statfsbuf);
616
617   if (i == -1)
618     {
619       error (0, errno, _("cannot read file system information for %s"),
620              quote (filename));
621       return;
622     }
623
624   if (format == NULL)
625     {
626       format = (terse
627                 ? "%n %i %l %t %b %f %a %s %c %d"
628                 : "  File: \"%n\"\n"
629                 "    ID: %-8i Namelen: %-7l Type: %T\n"
630                 "Blocks: Total: %-10b Free: %-10f Available: %-10a Size: %s\n"
631                 "Inodes: Total: %-10c Free: %-10d");
632     }
633
634   print_it (format, filename, print_statfs, &statfsbuf);
635 }
636
637 /* stat the file and print what we find */
638 static void
639 do_stat (char const *filename, int follow_links, int terse,
640          char const *format)
641 {
642   struct stat statbuf;
643   int i = ((follow_links == 1)
644            ? stat (filename, &statbuf)
645            : lstat (filename, &statbuf));
646
647   if (i == -1)
648     {
649       error (0, errno, _("cannot stat %s"), quote (filename));
650       return;
651     }
652
653   if (format == NULL)
654     {
655       if (terse != 0)
656         {
657           format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
658         }
659       else
660         {
661           /* tmp hack to match orignal output until conditional implemented */
662           i = statbuf.st_mode & S_IFMT;
663           if (i == S_IFCHR || i == S_IFBLK)
664             {
665               format =
666                 "  File: %N\n"
667                 "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
668                 "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
669                 " Device type: %t,%T\n"
670                 "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
671                 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
672             }
673           else
674             {
675               format =
676                 "  File: %N\n"
677                 "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
678                 "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
679                 "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
680                 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
681             }
682         }
683     }
684   print_it (format, filename, print_stat, &statbuf);
685 }
686
687 void
688 usage (int status)
689 {
690   if (status != 0)
691     fprintf (stderr, _("Try `%s --help' for more information.\n"),
692              program_name);
693   else
694     {
695       printf (_("Usage: %s [OPTION] FILE...\n"), program_name);
696       fputs (_("\
697 Display file or filesystem status.\n\
698 \n\
699   -f, --filesystem      display filesystem status instead of file status\n\
700   -c  --format=FORMAT   use the specified FORMAT instead of the default\n\
701   -L, --dereference     follow links\n\
702   -t, --terse           print the information in terse form\n\
703 "), stdout);
704       fputs (HELP_OPTION_DESCRIPTION, stdout);
705       fputs (VERSION_OPTION_DESCRIPTION, stdout);
706
707       fputs (_("\n\
708 The valid format sequences for files (without --filesystem):\n\
709 \n\
710   %A   Access rights in human readable form\n\
711   %a   Access rights in octal\n\
712   %B   The size in bytes of each block reported by `%b'\n\
713   %b   Number of blocks allocated (see %B)\n\
714 "), stdout);
715       fputs (_("\
716   %D   Device number in hex\n\
717   %d   Device number in decimal\n\
718   %F   File type\n\
719   %f   Raw mode in hex\n\
720   %G   Group name of owner\n\
721   %g   Group ID of owner\n\
722 "), stdout);
723       fputs (_("\
724   %h   Number of hard links\n\
725   %i   Inode number\n\
726   %N   Quoted File name with dereference if symbolic link\n\
727   %n   File name\n\
728   %o   IO block size\n\
729   %s   Total size, in bytes\n\
730   %T   Minor device type in hex\n\
731   %t   Major device type in hex\n\
732 "), stdout);
733       fputs (_("\
734   %U   User name of owner\n\
735   %u   User ID of owner\n\
736   %X   Time of last access as seconds since Epoch\n\
737   %x   Time of last access\n\
738   %Y   Time of last modification as seconds since Epoch\n\
739   %y   Time of last modification\n\
740   %Z   Time of last change as seconds since Epoch\n\
741   %z   Time of last change\n\
742 \n\
743 "), stdout);
744
745       fputs (_("\
746 Valid format sequences for file systems:\n\
747 \n\
748   %a   Free blocks available to non-superuser\n\
749   %b   Total data blocks in file system\n\
750   %c   Total file nodes in file system\n\
751   %d   Free file nodes in file system\n\
752   %f   Free blocks in file system\n\
753 "), stdout);
754       fputs (_("\
755   %i   File System id in hex\n\
756   %l   Maximum length of filenames\n\
757   %n   File name\n\
758   %s   Optimal transfer block size\n\
759   %T   Type in human readable form\n\
760   %t   Type in hex\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   int i;
772   int follow_links = 0;
773   int fs = 0;
774   int terse = 0;
775   char *format = NULL;
776
777   initialize_main (&argc, &argv);
778   program_name = argv[0];
779   setlocale (LC_ALL, "");
780   bindtextdomain (PACKAGE, LOCALEDIR);
781   textdomain (PACKAGE);
782
783   atexit (close_stdout);
784
785   while ((c = getopt_long (argc, argv, "c:fLlt", long_options, NULL)) != -1)
786     {
787       switch (c)
788         {
789         case 'c':
790           format = optarg;
791           break;
792
793         case 'l': /* deprecated */
794           error (0, 0, _("Warning: `-l' is deprecated; use `-L' instead"));
795           /* fall through */
796         case 'L':
797           follow_links = 1;
798           break;
799
800         case 'f':
801           fs = 1;
802           break;
803
804         case 't':
805           terse = 1;
806           break;
807
808         case_GETOPT_HELP_CHAR;
809
810         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
811
812         default:
813           usage (EXIT_FAILURE);
814         }
815     }
816
817   if (argc == optind)
818     {
819       error (0, 0, _("too few arguments"));
820       usage (EXIT_FAILURE);
821     }
822
823   for (i = optind; i < argc; i++)
824     {
825       if (fs == 0)
826         do_stat (argv[i], follow_links, terse, format);
827       else
828         do_statfs (argv[i], terse, format);
829     }
830
831   exit (G_fail ? EXIT_FAILURE : EXIT_SUCCESS);
832 }