TODO: add an item for a chmod optimization
[platform/upstream/coreutils.git] / src / stat.c
1 /* stat.c -- display file or file system status
2    Copyright (C) 2001-2008 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 Michael Meskes.  */
18
19 #include <config.h>
20
21 /* Keep this conditional in sync with the similar conditional in
22    ../m4/stat-prog.m4.  */
23 #if (STAT_STATVFS \
24      && (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
25          || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
26 # define USE_STATVFS 1
27 #else
28 # define USE_STATVFS 0
29 #endif
30
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #if USE_STATVFS
37 # include <sys/statvfs.h>
38 #elif HAVE_SYS_VFS_H
39 # include <sys/vfs.h>
40 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
41 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
42    It does have statvfs.h, but shouldn't use it, since it doesn't
43    HAVE_STRUCT_STATVFS_F_BASETYPE.  So find a clean way to fix it.  */
44 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
45 # include <sys/param.h>
46 # include <sys/mount.h>
47 # if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
48 /* Ultrix 4.4 needs these for the declaration of struct statfs.  */
49 #  include <netinet/in.h>
50 #  include <nfs/nfs_clnt.h>
51 #  include <nfs/vfs.h>
52 # endif
53 #elif HAVE_OS_H /* BeOS */
54 # include <fs_info.h>
55 #endif
56 #include <selinux/selinux.h>
57
58 #include "system.h"
59
60 #include "error.h"
61 #include "filemode.h"
62 #include "file-type.h"
63 #include "fs.h"
64 #include "getopt.h"
65 #include "quote.h"
66 #include "quotearg.h"
67 #include "stat-time.h"
68 #include "strftime.h"
69 #include "areadlink.h"
70
71 #define alignof(type) offsetof (struct { char c; type x; }, x)
72
73 #if USE_STATVFS
74 # define STRUCT_STATVFS struct statvfs
75 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
76 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
77 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
78 #  define SB_F_NAMEMAX(S) ((S)->f_namemax)
79 # endif
80 # define STATFS statvfs
81 # define STATFS_FRSIZE(S) ((S)->f_frsize)
82 #else
83 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
84 # if HAVE_STRUCT_STATFS_F_NAMELEN
85 #  define SB_F_NAMEMAX(S) ((S)->f_namelen)
86 # endif
87 # define STATFS statfs
88 # if HAVE_OS_H /* BeOS */
89 /* BeOS has a statvfs function, but it does not return sensible values
90    for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
91    f_fstypename.  Use 'struct fs_info' instead.  */
92 static int
93 statfs (char const *filename, struct fs_info *buf)
94 {
95   dev_t device = dev_for_path (filename);
96   if (device < 0)
97     {
98       errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
99                : device == B_BAD_VALUE ? EINVAL
100                : device == B_NAME_TOO_LONG ? ENAMETOOLONG
101                : device == B_NO_MEMORY ? ENOMEM
102                : device == B_FILE_ERROR ? EIO
103                : 0);
104       return -1;
105     }
106   /* If successful, buf->dev will be == device.  */
107   return fs_stat_dev (device, buf);
108 }
109 #  define f_fsid dev
110 #  define f_blocks total_blocks
111 #  define f_bfree free_blocks
112 #  define f_bavail free_blocks
113 #  define f_bsize io_size
114 #  define f_files total_nodes
115 #  define f_ffree free_nodes
116 #  define STRUCT_STATVFS struct fs_info
117 #  define STRUCT_STATXFS_F_FSID_IS_INTEGER true
118 #  define STATFS_FRSIZE(S) ((S)->block_size)
119 # else
120 #  define STRUCT_STATVFS struct statfs
121 #  define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
122 #  define STATFS_FRSIZE(S) 0
123 # endif
124 #endif
125
126 #ifdef SB_F_NAMEMAX
127 # define OUT_NAMEMAX out_uint
128 #else
129 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen.  */
130 # define SB_F_NAMEMAX(S) "*"
131 # define OUT_NAMEMAX out_string
132 #endif
133
134 #if HAVE_STRUCT_STATVFS_F_BASETYPE
135 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
136 #else
137 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
138 #  define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
139 # elif HAVE_OS_H /* BeOS */
140 #  define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
141 # endif
142 #endif
143
144 /* FIXME: these are used by printf.c, too */
145 #define isodigit(c) ('0' <= (c) && (c) <= '7')
146 #define octtobin(c) ((c) - '0')
147 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
148                      (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
149
150 #define PROGRAM_NAME "stat"
151
152 #define AUTHORS proper_name ("Michael Meskes")
153
154 enum
155 {
156   PRINTF_OPTION = CHAR_MAX + 1
157 };
158
159 static struct option const long_options[] =
160 {
161   {"context", no_argument, 0, 'Z'},
162   {"dereference", no_argument, NULL, 'L'},
163   {"file-system", no_argument, NULL, 'f'},
164
165   /* obsolete and undocumented alias: FIXME: remove in 2009 */
166   {"filesystem", no_argument, NULL, 'f'},
167
168   {"format", required_argument, NULL, 'c'},
169   {"printf", required_argument, NULL, PRINTF_OPTION},
170   {"terse", no_argument, NULL, 't'},
171   {GETOPT_HELP_OPTION_DECL},
172   {GETOPT_VERSION_OPTION_DECL},
173   {NULL, 0, NULL, 0}
174 };
175
176 /* Whether to follow symbolic links;  True for --dereference (-L).  */
177 static bool follow_links;
178
179 /* Whether to interpret backslash-escape sequences.
180    True for --printf=FMT, not for --format=FMT (-c).  */
181 static bool interpret_backslash_escapes;
182
183 /* The trailing delimiter string:
184    "" for --printf=FMT, "\n" for --format=FMT (-c).  */
185 static char const *trailing_delim = "";
186
187 /* Return the type of the specified file system.
188    Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
189    Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
190    Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
191    Still others have neither and have to get by with f_type (Linux).
192    But f_type may only exist in statfs (Cygwin).  */
193 static char const *
194 human_fstype (STRUCT_STATVFS const *statfsbuf)
195 {
196 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
197   return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
198 #else
199   switch (statfsbuf->f_type)
200     {
201 # if defined __linux__
202
203       /* Compare with what's in libc:
204          f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
205          sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
206            | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
207              -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
208            | sort > sym_libc
209          perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
210              -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
211            | sort > sym_stat
212          diff -u sym_stat sym_libc
213       */
214
215       /* Also sync from the list in "man 2 statfs".  */
216
217       /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
218          statements must be followed by a hexadecimal constant in
219          a comment.  The S_MAGIC_... name and constant are automatically
220          combined to produce the #define directives in fs.h.  */
221
222     case S_MAGIC_ADFS: /* 0xADF5 */
223       return "adfs";
224     case S_MAGIC_AFFS: /* 0xADFF */
225       return "affs";
226     case S_MAGIC_AUTOFS: /* 0x187 */
227       return "autofs";
228     case S_MAGIC_BEFS: /* 0x42465331 */
229       return "befs";
230     case S_MAGIC_BFS: /* 0x1BADFACE */
231       return "bfs";
232     case S_MAGIC_BINFMT_MISC: /* 0x42494e4d */
233       return "binfmt_misc";
234     case S_MAGIC_CODA: /* 0x73757245 */
235       return "coda";
236     case S_MAGIC_COH: /* 0x012FF7B7 */
237       return "coh";
238     case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
239       return "cramfs";
240     case S_MAGIC_DEVFS: /* 0x1373 */
241       return "devfs";
242     case S_MAGIC_DEVPTS: /* 0x1CD1 */
243       return "devpts";
244     case S_MAGIC_EFS: /* 0x414A53 */
245       return "efs";
246     case S_MAGIC_EXT: /* 0x137D */
247       return "ext";
248     case S_MAGIC_EXT2: /* 0xEF53 */
249       return "ext2/ext3";
250     case S_MAGIC_EXT2_OLD: /* 0xEF51 */
251       return "ext2";
252     case S_MAGIC_FAT: /* 0x4006 */
253       return "fat";
254     case S_MAGIC_FUSECTL: /* 0x65735543 */
255       return "fusectl";
256     case S_MAGIC_HPFS: /* 0xF995E849 */
257       return "hpfs";
258     case S_MAGIC_HUGETLBFS: /* 0x958458f6 */
259       return "hugetlbfs";
260     case S_MAGIC_ISOFS: /* 0x9660 */
261       return "isofs";
262     case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
263       return "isofs";
264     case S_MAGIC_ISOFS_WIN: /* 0x4000 */
265       return "isofs";
266     case S_MAGIC_JFFS2: /* 0x72B6 */
267       return "jffs2";
268     case S_MAGIC_JFFS: /* 0x07C0 */
269       return "jffs";
270     case S_MAGIC_JFS: /* 0x3153464A */
271       return "jfs";
272     case S_MAGIC_MINIX: /* 0x137F */
273       return "minix";
274     case S_MAGIC_MINIX_30: /* 0x138F */
275       return "minix (30 char.)";
276     case S_MAGIC_MINIX_V2: /* 0x2468 */
277       return "minix v2";
278     case S_MAGIC_MINIX_V2_30: /* 0x2478 */
279       return "minix v2 (30 char.)";
280     case S_MAGIC_MSDOS: /* 0x4D44 */
281       return "msdos";
282     case S_MAGIC_NCP: /* 0x564C */
283       return "novell";
284     case S_MAGIC_NFS: /* 0x6969 */
285       return "nfs";
286     case S_MAGIC_NFSD: /* 0x6E667364 */
287       return "nfsd";
288     case S_MAGIC_NTFS: /* 0x5346544E */
289       return "ntfs";
290     case S_MAGIC_OPENPROM: /* 0x9fa1 */
291       return "openprom";
292     case S_MAGIC_PROC: /* 0x9FA0 */
293       return "proc";
294     case S_MAGIC_QNX4: /* 0x002F */
295       return "qnx4";
296     case S_MAGIC_RAMFS: /* 0x858458F6 */
297       return "ramfs";
298     case S_MAGIC_REISERFS: /* 0x52654973 */
299       return "reiserfs";
300     case S_MAGIC_ROMFS: /* 0x7275 */
301       return "romfs";
302     case S_MAGIC_SMB: /* 0x517B */
303       return "smb";
304     case S_MAGIC_SQUASHFS: /* 0x73717368 */
305       return "squashfs";
306     case S_MAGIC_SYSFS: /* 0x62656572 */
307       return "sysfs";
308     case S_MAGIC_SYSV2: /* 0x012FF7B6 */
309       return "sysv2";
310     case S_MAGIC_SYSV4: /* 0x012FF7B5 */
311       return "sysv4";
312     case S_MAGIC_TMPFS: /* 0x1021994 */
313       return "tmpfs";
314     case S_MAGIC_UDF: /* 0x15013346 */
315       return "udf";
316     case S_MAGIC_UFS: /* 0x00011954 */
317       return "ufs";
318     case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
319       return "ufs";
320     case S_MAGIC_USBDEVFS: /* 0x9FA2 */
321       return "usbdevfs";
322     case S_MAGIC_VXFS: /* 0xA501FCF5 */
323       return "vxfs";
324     case S_MAGIC_XENIX: /* 0x012FF7B4 */
325       return "xenix";
326     case S_MAGIC_XFS: /* 0x58465342 */
327       return "xfs";
328     case S_MAGIC_XIAFS: /* 0x012FD16D */
329       return "xia";
330
331 # elif __GNU__
332     case FSTYPE_UFS:
333       return "ufs";
334     case FSTYPE_NFS:
335       return "nfs";
336     case FSTYPE_GFS:
337       return "gfs";
338     case FSTYPE_LFS:
339       return "lfs";
340     case FSTYPE_SYSV:
341       return "sysv";
342     case FSTYPE_FTP:
343       return "ftp";
344     case FSTYPE_TAR:
345       return "tar";
346     case FSTYPE_AR:
347       return "ar";
348     case FSTYPE_CPIO:
349       return "cpio";
350     case FSTYPE_MSLOSS:
351       return "msloss";
352     case FSTYPE_CPM:
353       return "cpm";
354     case FSTYPE_HFS:
355       return "hfs";
356     case FSTYPE_DTFS:
357       return "dtfs";
358     case FSTYPE_GRFS:
359       return "grfs";
360     case FSTYPE_TERM:
361       return "term";
362     case FSTYPE_DEV:
363       return "dev";
364     case FSTYPE_PROC:
365       return "proc";
366     case FSTYPE_IFSOCK:
367       return "ifsock";
368     case FSTYPE_AFS:
369       return "afs";
370     case FSTYPE_DFS:
371       return "dfs";
372     case FSTYPE_PROC9:
373       return "proc9";
374     case FSTYPE_SOCKET:
375       return "socket";
376     case FSTYPE_MISC:
377       return "misc";
378     case FSTYPE_EXT2FS:
379       return "ext2/ext3";
380     case FSTYPE_HTTP:
381       return "http";
382     case FSTYPE_MEMFS:
383       return "memfs";
384     case FSTYPE_ISO9660:
385       return "iso9660";
386 # endif
387     default:
388       {
389         unsigned long int type = statfsbuf->f_type;
390         static char buf[sizeof "UNKNOWN (0x%lx)" - 3
391                         + (sizeof type * CHAR_BIT + 3) / 4];
392         sprintf (buf, "UNKNOWN (0x%lx)", type);
393         return buf;
394       }
395     }
396 #endif
397 }
398
399 static char *
400 human_access (struct stat const *statbuf)
401 {
402   static char modebuf[12];
403   filemodestring (statbuf, modebuf);
404   modebuf[10] = 0;
405   return modebuf;
406 }
407
408 static char *
409 human_time (struct timespec t)
410 {
411   static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
412                        (INT_STRLEN_BOUND (int) /* YYYY */
413                         + 1 /* because YYYY might equal INT_MAX + 1900 */
414                         + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
415   struct tm const *tm = localtime (&t.tv_sec);
416   if (tm == NULL)
417     return timetostr (t.tv_sec, str);
418   nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
419   return str;
420 }
421
422 static void
423 out_string (char *pformat, size_t prefix_len, char const *arg)
424 {
425   strcpy (pformat + prefix_len, "s");
426   printf (pformat, arg);
427 }
428 static void
429 out_int (char *pformat, size_t prefix_len, intmax_t arg)
430 {
431   strcpy (pformat + prefix_len, PRIdMAX);
432   printf (pformat, arg);
433 }
434 static void
435 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
436 {
437   strcpy (pformat + prefix_len, PRIuMAX);
438   printf (pformat, arg);
439 }
440 static void
441 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
442 {
443   strcpy (pformat + prefix_len, PRIoMAX);
444   printf (pformat, arg);
445 }
446 static void
447 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
448 {
449   strcpy (pformat + prefix_len, PRIxMAX);
450   printf (pformat, arg);
451 }
452
453 /* Very specialized function (modifies FORMAT), just so as to avoid
454    duplicating this code between both print_statfs and print_stat.  */
455 static void
456 out_file_context (char const *filename, char *pformat, size_t prefix_len)
457 {
458   char *scontext;
459   if ((follow_links
460        ? getfilecon (filename, &scontext)
461        : lgetfilecon (filename, &scontext)) < 0)
462     {
463       error (0, errno, _("failed to get security context of %s"),
464              quote (filename));
465       scontext = NULL;
466     }
467   strcpy (pformat + prefix_len, "s");
468   printf (pformat, (scontext ? scontext : "?"));
469   if (scontext)
470     freecon (scontext);
471 }
472
473 /* print statfs info */
474 static void
475 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
476               void const *data)
477 {
478   STRUCT_STATVFS const *statfsbuf = data;
479
480   switch (m)
481     {
482     case 'n':
483       out_string (pformat, prefix_len, filename);
484       break;
485
486     case 'i':
487       {
488 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
489         uintmax_t fsid = statfsbuf->f_fsid;
490 #else
491         typedef unsigned int fsid_word;
492         verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
493         verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
494         verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
495         fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
496
497         /* Assume a little-endian word order, as that is compatible
498            with glibc's statvfs implementation.  */
499         uintmax_t fsid = 0;
500         int words = sizeof statfsbuf->f_fsid / sizeof *p;
501         int i;
502         for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
503           {
504             uintmax_t u = p[words - 1 - i];
505             fsid |= u << (i * CHAR_BIT * sizeof *p);
506           }
507 #endif
508         out_uint_x (pformat, prefix_len, fsid);
509       }
510       break;
511
512     case 'l':
513       OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
514       break;
515     case 't':
516 #if HAVE_STRUCT_STATXFS_F_TYPE
517       out_uint_x (pformat, prefix_len, statfsbuf->f_type);
518 #else
519       fputc ('?', stdout);
520 #endif
521       break;
522     case 'T':
523       out_string (pformat, prefix_len, human_fstype (statfsbuf));
524       break;
525     case 'b':
526       out_int (pformat, prefix_len, statfsbuf->f_blocks);
527       break;
528     case 'f':
529       out_int (pformat, prefix_len, statfsbuf->f_bfree);
530       break;
531     case 'a':
532       out_int (pformat, prefix_len, statfsbuf->f_bavail);
533       break;
534     case 's':
535       out_uint (pformat, prefix_len, statfsbuf->f_bsize);
536       break;
537     case 'S':
538       {
539         uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
540         if (! frsize)
541           frsize = statfsbuf->f_bsize;
542         out_uint (pformat, prefix_len, frsize);
543       }
544       break;
545     case 'c':
546       out_int (pformat, prefix_len, statfsbuf->f_files);
547       break;
548     case 'd':
549       out_int (pformat, prefix_len, statfsbuf->f_ffree);
550       break;
551     case 'C':
552       out_file_context (filename, pformat, prefix_len);
553       break;
554     default:
555       fputc ('?', stdout);
556       break;
557     }
558 }
559
560 /* print stat info */
561 static void
562 print_stat (char *pformat, size_t prefix_len, char m,
563             char const *filename, void const *data)
564 {
565   struct stat *statbuf = (struct stat *) data;
566   struct passwd *pw_ent;
567   struct group *gw_ent;
568
569   switch (m)
570     {
571     case 'n':
572       out_string (pformat, prefix_len, filename);
573       break;
574     case 'N':
575       out_string (pformat, prefix_len, quote (filename));
576       if (S_ISLNK (statbuf->st_mode))
577         {
578           char *linkname = areadlink_with_size (filename, statbuf->st_size);
579           if (linkname == NULL)
580             {
581               error (0, errno, _("cannot read symbolic link %s"),
582                      quote (filename));
583               return;
584             }
585           printf (" -> ");
586           out_string (pformat, prefix_len, quote (linkname));
587         }
588       break;
589     case 'd':
590       out_uint (pformat, prefix_len, statbuf->st_dev);
591       break;
592     case 'D':
593       out_uint_x (pformat, prefix_len, statbuf->st_dev);
594       break;
595     case 'i':
596       out_uint (pformat, prefix_len, statbuf->st_ino);
597       break;
598     case 'a':
599       out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
600       break;
601     case 'A':
602       out_string (pformat, prefix_len, human_access (statbuf));
603       break;
604     case 'f':
605       out_uint_x (pformat, prefix_len, statbuf->st_mode);
606       break;
607     case 'F':
608       out_string (pformat, prefix_len, file_type (statbuf));
609       break;
610     case 'h':
611       out_uint (pformat, prefix_len, statbuf->st_nlink);
612       break;
613     case 'u':
614       out_uint (pformat, prefix_len, statbuf->st_uid);
615       break;
616     case 'U':
617       setpwent ();
618       pw_ent = getpwuid (statbuf->st_uid);
619       out_string (pformat, prefix_len,
620                   pw_ent ? pw_ent->pw_name : "UNKNOWN");
621       break;
622     case 'g':
623       out_uint (pformat, prefix_len, statbuf->st_gid);
624       break;
625     case 'G':
626       setgrent ();
627       gw_ent = getgrgid (statbuf->st_gid);
628       out_string (pformat, prefix_len,
629                   gw_ent ? gw_ent->gr_name : "UNKNOWN");
630       break;
631     case 't':
632       out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
633       break;
634     case 'T':
635       out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
636       break;
637     case 's':
638       out_uint (pformat, prefix_len, statbuf->st_size);
639       break;
640     case 'B':
641       out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
642       break;
643     case 'b':
644       out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
645       break;
646     case 'o':
647       out_uint (pformat, prefix_len, statbuf->st_blksize);
648       break;
649     case 'x':
650       out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
651       break;
652     case 'X':
653       if (TYPE_SIGNED (time_t))
654         out_int (pformat, prefix_len, statbuf->st_atime);
655       else
656         out_uint (pformat, prefix_len, statbuf->st_atime);
657       break;
658     case 'y':
659       out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
660       break;
661     case 'Y':
662       if (TYPE_SIGNED (time_t))
663         out_int (pformat, prefix_len, statbuf->st_mtime);
664       else
665         out_uint (pformat, prefix_len, statbuf->st_mtime);
666       break;
667     case 'z':
668       out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
669       break;
670     case 'Z':
671       if (TYPE_SIGNED (time_t))
672         out_int (pformat, prefix_len, statbuf->st_ctime);
673       else
674         out_uint (pformat, prefix_len, statbuf->st_ctime);
675       break;
676     case 'C':
677       out_file_context (filename, pformat, prefix_len);
678       break;
679     default:
680       fputc ('?', stdout);
681       break;
682     }
683 }
684
685 /* Output a single-character \ escape.  */
686
687 static void
688 print_esc_char (char c)
689 {
690   switch (c)
691     {
692     case 'a':                   /* Alert. */
693       c ='\a';
694       break;
695     case 'b':                   /* Backspace. */
696       c ='\b';
697       break;
698     case 'f':                   /* Form feed. */
699       c ='\f';
700       break;
701     case 'n':                   /* New line. */
702       c ='\n';
703       break;
704     case 'r':                   /* Carriage return. */
705       c ='\r';
706       break;
707     case 't':                   /* Horizontal tab. */
708       c ='\t';
709       break;
710     case 'v':                   /* Vertical tab. */
711       c ='\v';
712       break;
713     case '"':
714     case '\\':
715       break;
716     default:
717       error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
718       break;
719     }
720   putchar (c);
721 }
722
723 static void
724 print_it (char const *format, char const *filename,
725           void (*print_func) (char *, size_t, char, char const *, void const *),
726           void const *data)
727 {
728   /* Add 2 to accommodate our conversion of the stat `%s' format string
729      to the longer printf `%llu' one.  */
730   enum
731     {
732       MAX_ADDITIONAL_BYTES =
733         (MAX (sizeof PRIdMAX,
734               MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
735          - 1)
736     };
737   size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
738   char *dest = xmalloc (n_alloc);
739   char const *b;
740   for (b = format; *b; b++)
741     {
742       switch (*b)
743         {
744         case '%':
745           {
746             size_t len = strspn (b + 1, "#-+.I 0123456789");
747             char const *fmt_char = b + len + 1;
748             memcpy (dest, b, len + 1);
749
750             b = fmt_char;
751             switch (*fmt_char)
752               {
753               case '\0':
754                 --b;
755                 /* fall through */
756               case '%':
757                 if (0 < len)
758                   {
759                     dest[len + 1] = *fmt_char;
760                     dest[len + 2] = '\0';
761                     error (EXIT_FAILURE, 0, _("%s: invalid directive"),
762                            quotearg_colon (dest));
763                   }
764                 putchar ('%');
765                 break;
766               default:
767                 print_func (dest, len + 1, *fmt_char, filename, data);
768                 break;
769               }
770             break;
771           }
772
773         case '\\':
774           if ( ! interpret_backslash_escapes)
775             {
776               putchar ('\\');
777               break;
778             }
779           ++b;
780           if (isodigit (*b))
781             {
782               int esc_value = octtobin (*b);
783               int esc_length = 1;       /* number of octal digits */
784               for (++b; esc_length < 3 && isodigit (*b);
785                    ++esc_length, ++b)
786                 {
787                   esc_value = esc_value * 8 + octtobin (*b);
788                 }
789               putchar (esc_value);
790               --b;
791             }
792           else if (*b == 'x' && isxdigit (to_uchar (b[1])))
793             {
794               int esc_value = hextobin (b[1]);  /* Value of \xhh escape. */
795               /* A hexadecimal \xhh escape sequence must have
796                  1 or 2 hex. digits.  */
797               ++b;
798               if (isxdigit (to_uchar (b[1])))
799                 {
800                   ++b;
801                   esc_value = esc_value * 16 + hextobin (*b);
802                 }
803               putchar (esc_value);
804             }
805           else if (*b == '\0')
806             {
807               error (0, 0, _("warning: backslash at end of format"));
808               putchar ('\\');
809               /* Arrange to exit the loop.  */
810               --b;
811             }
812           else
813             {
814               print_esc_char (*b);
815             }
816           break;
817
818         default:
819           putchar (*b);
820           break;
821         }
822     }
823   free (dest);
824
825   fputs (trailing_delim, stdout);
826 }
827
828 /* Stat the file system and print what we find.  */
829 static bool
830 do_statfs (char const *filename, bool terse, char const *format)
831 {
832   STRUCT_STATVFS statfsbuf;
833
834   if (STATFS (filename, &statfsbuf) != 0)
835     {
836       error (0, errno, _("cannot read file system information for %s"),
837              quote (filename));
838       return false;
839     }
840
841   if (format == NULL)
842     {
843       format = (terse
844                 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
845                 : "  File: \"%n\"\n"
846                 "    ID: %-8i Namelen: %-7l Type: %T\n"
847                 "Block size: %-10s Fundamental block size: %S\n"
848                 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
849                 "Inodes: Total: %-10c Free: %d\n");
850     }
851
852   print_it (format, filename, print_statfs, &statfsbuf);
853   return true;
854 }
855
856 /* stat the file and print what we find */
857 static bool
858 do_stat (char const *filename, bool terse, char const *format)
859 {
860   struct stat statbuf;
861
862   if ((follow_links ? stat : lstat) (filename, &statbuf) != 0)
863     {
864       error (0, errno, _("cannot stat %s"), quote (filename));
865       return false;
866     }
867
868   if (format == NULL)
869     {
870       if (terse)
871         {
872           format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
873         }
874       else
875         {
876           /* Temporary hack to match original output until conditional
877              implemented.  */
878           if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
879             {
880               format =
881                 "  File: %N\n"
882                 "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
883                 "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
884                 " Device type: %t,%T\n"
885                 "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
886                 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
887             }
888           else
889             {
890               format =
891                 "  File: %N\n"
892                 "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
893                 "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
894                 "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
895                 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
896             }
897         }
898     }
899   print_it (format, filename, print_stat, &statbuf);
900   return true;
901 }
902
903 void
904 usage (int status)
905 {
906   if (status != EXIT_SUCCESS)
907     fprintf (stderr, _("Try `%s --help' for more information.\n"),
908              program_name);
909   else
910     {
911       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
912       fputs (_("\
913 Display file or file system status.\n\
914 \n\
915   -L, --dereference     follow links\n\
916   -f, --file-system     display file system status instead of file status\n\
917 "), stdout);
918       fputs (_("\
919   -c  --format=FORMAT   use the specified FORMAT instead of the default;\n\
920                           output a newline after each use of FORMAT\n\
921       --printf=FORMAT   like --format, but interpret backslash escapes,\n\
922                           and do not output a mandatory trailing newline.\n\
923                           If you want a newline, include \\n in FORMAT.\n\
924   -t, --terse           print the information in terse form\n\
925 "), stdout);
926       fputs (HELP_OPTION_DESCRIPTION, stdout);
927       fputs (VERSION_OPTION_DESCRIPTION, stdout);
928
929       fputs (_("\n\
930 The valid format sequences for files (without --file-system):\n\
931 \n\
932   %a   Access rights in octal\n\
933   %A   Access rights in human readable form\n\
934   %b   Number of blocks allocated (see %B)\n\
935   %B   The size in bytes of each block reported by %b\n\
936   %C   SELinux security context string\n\
937 "), stdout);
938       fputs (_("\
939   %d   Device number in decimal\n\
940   %D   Device number in hex\n\
941   %f   Raw mode in hex\n\
942   %F   File type\n\
943   %g   Group ID of owner\n\
944   %G   Group name of owner\n\
945 "), stdout);
946       fputs (_("\
947   %h   Number of hard links\n\
948   %i   Inode number\n\
949   %n   File name\n\
950   %N   Quoted file name with dereference if symbolic link\n\
951   %o   I/O block size\n\
952   %s   Total size, in bytes\n\
953   %t   Major device type in hex\n\
954   %T   Minor device type in hex\n\
955 "), stdout);
956       fputs (_("\
957   %u   User ID of owner\n\
958   %U   User name of owner\n\
959   %x   Time of last access\n\
960   %X   Time of last access as seconds since Epoch\n\
961   %y   Time of last modification\n\
962   %Y   Time of last modification as seconds since Epoch\n\
963   %z   Time of last change\n\
964   %Z   Time of last change as seconds since Epoch\n\
965 \n\
966 "), stdout);
967
968       fputs (_("\
969 Valid format sequences for file systems:\n\
970 \n\
971   %a   Free blocks available to non-superuser\n\
972   %b   Total data blocks in file system\n\
973   %c   Total file nodes in file system\n\
974   %d   Free file nodes in file system\n\
975   %f   Free blocks in file system\n\
976   %C   SELinux security context string\n\
977 "), stdout);
978       fputs (_("\
979   %i   File System ID in hex\n\
980   %l   Maximum length of filenames\n\
981   %n   File name\n\
982   %s   Block size (for faster transfers)\n\
983   %S   Fundamental block size (for block counts)\n\
984   %t   Type in hex\n\
985   %T   Type in human readable form\n\
986 "), stdout);
987       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
988       emit_bug_reporting_address ();
989     }
990   exit (status);
991 }
992
993 int
994 main (int argc, char *argv[])
995 {
996   int c;
997   int i;
998   bool fs = false;
999   bool terse = false;
1000   char *format = NULL;
1001   bool ok = true;
1002
1003   initialize_main (&argc, &argv);
1004   set_program_name (argv[0]);
1005   setlocale (LC_ALL, "");
1006   bindtextdomain (PACKAGE, LOCALEDIR);
1007   textdomain (PACKAGE);
1008
1009   atexit (close_stdout);
1010
1011   while ((c = getopt_long (argc, argv, "c:fLtZ", long_options, NULL)) != -1)
1012     {
1013       switch (c)
1014         {
1015         case PRINTF_OPTION:
1016           format = optarg;
1017           interpret_backslash_escapes = true;
1018           trailing_delim = "";
1019           break;
1020
1021         case 'c':
1022           format = optarg;
1023           interpret_backslash_escapes = false;
1024           trailing_delim = "\n";
1025           break;
1026
1027         case 'L':
1028           follow_links = true;
1029           break;
1030
1031         case 'f':
1032           fs = true;
1033           break;
1034
1035         case 't':
1036           terse = true;
1037           break;
1038
1039         case 'Z':  /* FIXME: remove in 2010 */
1040           /* Ignore, for compatibility with distributions
1041              that implemented this before upstream.
1042              But warn of impending removal.  */
1043           error (0, 0,
1044                  _("the --context (-Z) option is obsolete and will be removed\n"
1045                    "in a future release"));
1046           break;
1047
1048         case_GETOPT_HELP_CHAR;
1049
1050         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1051
1052         default:
1053           usage (EXIT_FAILURE);
1054         }
1055     }
1056
1057   if (argc == optind)
1058     {
1059       error (0, 0, _("missing operand"));
1060       usage (EXIT_FAILURE);
1061     }
1062
1063   for (i = optind; i < argc; i++)
1064     ok &= (fs
1065            ? do_statfs (argv[i], terse, format)
1066            : do_stat (argv[i], terse, format));
1067
1068   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
1069 }