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