b2459e06d2e747719b51f33c66b54c01c06c0046
[platform/upstream/coreutils.git] / src / stat.c
1 /* stat.c -- display file or file system status
2    Copyright (C) 2001-2012 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 || STAT_STATVFS64)                                       \
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 <stdalign.h>
34 #include <sys/types.h>
35 #include <pwd.h>
36 #include <grp.h>
37 #if USE_STATVFS
38 # include <sys/statvfs.h>
39 #elif HAVE_SYS_VFS_H
40 # include <sys/vfs.h>
41 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
42 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
43    It does have statvfs.h, but shouldn't use it, since it doesn't
44    HAVE_STRUCT_STATVFS_F_BASETYPE.  So find a clean way to fix it.  */
45 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
46 # include <sys/param.h>
47 # include <sys/mount.h>
48 # if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
49 /* Ultrix 4.4 needs these for the declaration of struct statfs.  */
50 #  include <netinet/in.h>
51 #  include <nfs/nfs_clnt.h>
52 #  include <nfs/vfs.h>
53 # endif
54 #elif HAVE_OS_H /* BeOS */
55 # include <fs_info.h>
56 #endif
57 #include <selinux/selinux.h>
58
59 #include "system.h"
60
61 #include "areadlink.h"
62 #include "error.h"
63 #include "file-type.h"
64 #include "filemode.h"
65 #include "fs.h"
66 #include "getopt.h"
67 #include "mountlist.h"
68 #include "quote.h"
69 #include "quotearg.h"
70 #include "stat-size.h"
71 #include "stat-time.h"
72 #include "strftime.h"
73 #include "find-mount-point.h"
74 #include "xvasprintf.h"
75
76 #if USE_STATVFS
77 # define STRUCT_STATVFS struct statvfs
78 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
79 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
80 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
81 #  define SB_F_NAMEMAX(S) ((S)->f_namemax)
82 # endif
83 # if ! STAT_STATVFS && STAT_STATVFS64
84 #  define STATFS statvfs64
85 # else
86 #  define STATFS statvfs
87 # endif
88 # define STATFS_FRSIZE(S) ((S)->f_frsize)
89 #else
90 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
91 # if HAVE_STRUCT_STATFS_F_NAMELEN
92 #  define SB_F_NAMEMAX(S) ((S)->f_namelen)
93 # endif
94 # define STATFS statfs
95 # if HAVE_OS_H /* BeOS */
96 /* BeOS has a statvfs function, but it does not return sensible values
97    for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
98    f_fstypename.  Use 'struct fs_info' instead.  */
99 static int ATTRIBUTE_WARN_UNUSED_RESULT
100 statfs (char const *filename, struct fs_info *buf)
101 {
102   dev_t device = dev_for_path (filename);
103   if (device < 0)
104     {
105       errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
106                : device == B_BAD_VALUE ? EINVAL
107                : device == B_NAME_TOO_LONG ? ENAMETOOLONG
108                : device == B_NO_MEMORY ? ENOMEM
109                : device == B_FILE_ERROR ? EIO
110                : 0);
111       return -1;
112     }
113   /* If successful, buf->dev will be == device.  */
114   return fs_stat_dev (device, buf);
115 }
116 #  define f_fsid dev
117 #  define f_blocks total_blocks
118 #  define f_bfree free_blocks
119 #  define f_bavail free_blocks
120 #  define f_bsize io_size
121 #  define f_files total_nodes
122 #  define f_ffree free_nodes
123 #  define STRUCT_STATVFS struct fs_info
124 #  define STRUCT_STATXFS_F_FSID_IS_INTEGER true
125 #  define STATFS_FRSIZE(S) ((S)->block_size)
126 # else
127 #  define STRUCT_STATVFS struct statfs
128 #  define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
129 #  if HAVE_STRUCT_STATFS_F_FRSIZE
130 #   define STATFS_FRSIZE(S) ((S)->f_frsize)
131 #  else
132 #   define STATFS_FRSIZE(S) 0
133 #  endif
134 # endif
135 #endif
136
137 #ifdef SB_F_NAMEMAX
138 # define OUT_NAMEMAX out_uint
139 #else
140 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen.  */
141 # define SB_F_NAMEMAX(S) "*"
142 # define OUT_NAMEMAX out_string
143 #endif
144
145 #if HAVE_STRUCT_STATVFS_F_BASETYPE
146 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
147 #else
148 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
149 #  define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
150 # elif HAVE_OS_H /* BeOS */
151 #  define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
152 # endif
153 #endif
154
155 /* FIXME: these are used by printf.c, too */
156 #define isodigit(c) ('0' <= (c) && (c) <= '7')
157 #define octtobin(c) ((c) - '0')
158 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
159                      (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
160
161 static char const digits[] = "0123456789";
162
163 /* Flags that are portable for use in printf, for at least one
164    conversion specifier; make_format removes unportable flags as
165    needed for particular specifiers.  The glibc 2.2 extension "I" is
166    listed here; it is removed by make_format because it has undefined
167    behavior elsewhere and because it is incompatible with
168    out_epoch_sec.  */
169 static char const printf_flags[] = "'-+ #0I";
170
171 #define PROGRAM_NAME "stat"
172
173 #define AUTHORS proper_name ("Michael Meskes")
174
175 enum
176 {
177   PRINTF_OPTION = CHAR_MAX + 1
178 };
179
180 static struct option const long_options[] =
181 {
182   {"context", no_argument, 0, 'Z'},
183   {"dereference", no_argument, NULL, 'L'},
184   {"file-system", no_argument, NULL, 'f'},
185   {"format", required_argument, NULL, 'c'},
186   {"printf", required_argument, NULL, PRINTF_OPTION},
187   {"terse", no_argument, NULL, 't'},
188   {GETOPT_HELP_OPTION_DECL},
189   {GETOPT_VERSION_OPTION_DECL},
190   {NULL, 0, NULL, 0}
191 };
192
193 /* Whether to follow symbolic links;  True for --dereference (-L).  */
194 static bool follow_links;
195
196 /* Whether to interpret backslash-escape sequences.
197    True for --printf=FMT, not for --format=FMT (-c).  */
198 static bool interpret_backslash_escapes;
199
200 /* The trailing delimiter string:
201    "" for --printf=FMT, "\n" for --format=FMT (-c).  */
202 static char const *trailing_delim = "";
203
204 /* The representation of the decimal point in the current locale.  */
205 static char const *decimal_point;
206 static size_t decimal_point_len;
207
208 /* Return the type of the specified file system.
209    Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
210    Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
211    Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
212    Still others have neither and have to get by with f_type (GNU/Linux).
213    But f_type may only exist in statfs (Cygwin).  */
214 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
215 human_fstype (STRUCT_STATVFS const *statfsbuf)
216 {
217 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
218   return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
219 #else
220   switch (statfsbuf->f_type)
221     {
222 # if defined __linux__
223
224       /* Compare with what's in libc:
225          f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
226          sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
227            | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
228              -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
229            | sort > sym_libc
230          perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
231              -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
232            | sort > sym_stat
233          diff -u sym_stat sym_libc
234       */
235
236       /* Also compare with the list in "man 2 statfs" using the
237          fs-magic-compare make target.  */
238
239       /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
240          statements must be followed by a hexadecimal constant in
241          a comment.  The S_MAGIC_... name and constant are automatically
242          combined to produce the #define directives in fs.h.  */
243
244     case S_MAGIC_ADFS: /* 0xADF5 local */
245       return "adfs";
246     case S_MAGIC_AFFS: /* 0xADFF local */
247       return "affs";
248     case S_MAGIC_AFS: /* 0x5346414F remote */
249       return "afs";
250     case S_MAGIC_ANON_INODE_FS: /* 0x09041934 local */
251       return "anon-inode FS";
252     case S_MAGIC_AUFS: /* 0x61756673 remote */
253       /* FIXME: change syntax or add an optional attribute like "inotify:no".
254          The above is labeled as "remote" so that tail always uses polling,
255          but this isn't really a remote file system type.  */
256       return "aufs";
257     case S_MAGIC_AUTOFS: /* 0x0187 local */
258       return "autofs";
259     case S_MAGIC_BEFS: /* 0x42465331 local */
260       return "befs";
261     case S_MAGIC_BDEVFS: /* 0x62646576 local */
262       return "bdevfs";
263     case S_MAGIC_BFS: /* 0x1BADFACE local */
264       return "bfs";
265     case S_MAGIC_BINFMTFS: /* 0x42494E4D local */
266       return "binfmt_misc";
267     case S_MAGIC_BTRFS: /* 0x9123683E local */
268       return "btrfs";
269     case S_MAGIC_CEPH: /* 0x00C36400 remote */
270       return "ceph";
271     case S_MAGIC_CGROUP: /* 0x0027E0EB local */
272       return "cgroupfs";
273     case S_MAGIC_CIFS: /* 0xFF534D42 remote */
274       return "cifs";
275     case S_MAGIC_CODA: /* 0x73757245 remote */
276       return "coda";
277     case S_MAGIC_COH: /* 0x012FF7B7 local */
278       return "coh";
279     case S_MAGIC_CRAMFS: /* 0x28CD3D45 local */
280       return "cramfs";
281     case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 local */
282       return "cramfs-wend";
283     case S_MAGIC_DEBUGFS: /* 0x64626720 local */
284       return "debugfs";
285     case S_MAGIC_DEVFS: /* 0x1373 local */
286       return "devfs";
287     case S_MAGIC_DEVPTS: /* 0x1CD1 local */
288       return "devpts";
289     case S_MAGIC_ECRYPTFS: /* 0xF15F local */
290       return "ecryptfs";
291     case S_MAGIC_EFS: /* 0x00414A53 local */
292       return "efs";
293     case S_MAGIC_EXT: /* 0x137D local */
294       return "ext";
295     case S_MAGIC_EXT2: /* 0xEF53 local */
296       return "ext2/ext3";
297     case S_MAGIC_EXT2_OLD: /* 0xEF51 local */
298       return "ext2";
299     case S_MAGIC_FAT: /* 0x4006 local */
300       return "fat";
301     case S_MAGIC_FHGFS: /* 0x19830326 remote */
302       return "fhgfs";
303     case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
304       return "fuseblk";
305     case S_MAGIC_FUSECTL: /* 0x65735543 remote */
306       return "fusectl";
307     case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA local */
308       return "futexfs";
309     case S_MAGIC_GFS: /* 0x1161970 remote */
310       return "gfs/gfs2";
311     case S_MAGIC_GPFS: /* 0x47504653 remote */
312       return "gpfs";
313     case S_MAGIC_HFS: /* 0x4244 local */
314       return "hfs";
315     case S_MAGIC_HPFS: /* 0xF995E849 local */
316       return "hpfs";
317     case S_MAGIC_HUGETLBFS: /* 0x958458F6 local */
318       return "hugetlbfs";
319     case S_MAGIC_MTD_INODE_FS: /* 0x11307854 local */
320       return "inodefs";
321     case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA local */
322       return "inotifyfs";
323     case S_MAGIC_ISOFS: /* 0x9660 local */
324       return "isofs";
325     case S_MAGIC_ISOFS_R_WIN: /* 0x4004 local */
326       return "isofs";
327     case S_MAGIC_ISOFS_WIN: /* 0x4000 local */
328       return "isofs";
329     case S_MAGIC_JFFS: /* 0x07C0 local */
330       return "jffs";
331     case S_MAGIC_JFFS2: /* 0x72B6 local */
332       return "jffs2";
333     case S_MAGIC_JFS: /* 0x3153464A local */
334       return "jfs";
335     case S_MAGIC_KAFS: /* 0x6B414653 remote */
336       return "k-afs";
337     case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
338       return "lustre";
339     case S_MAGIC_MINIX: /* 0x137F local */
340       return "minix";
341     case S_MAGIC_MINIX_30: /* 0x138F local */
342       return "minix (30 char.)";
343     case S_MAGIC_MINIX_V2: /* 0x2468 local */
344       return "minix v2";
345     case S_MAGIC_MINIX_V2_30: /* 0x2478 local */
346       return "minix v2 (30 char.)";
347     case S_MAGIC_MINIX_V3: /* 0x4D5A local */
348       return "minix3";
349     case S_MAGIC_MQUEUE: /* 0x19800202 local */
350       return "mqueue";
351     case S_MAGIC_MSDOS: /* 0x4D44 local */
352       return "msdos";
353     case S_MAGIC_NCP: /* 0x564C remote */
354       return "novell";
355     case S_MAGIC_NFS: /* 0x6969 remote */
356       return "nfs";
357     case S_MAGIC_NFSD: /* 0x6E667364 remote */
358       return "nfsd";
359     case S_MAGIC_NILFS: /* 0x3434 local */
360       return "nilfs";
361     case S_MAGIC_NTFS: /* 0x5346544E local */
362       return "ntfs";
363     case S_MAGIC_OPENPROM: /* 0x9FA1 local */
364       return "openprom";
365     case S_MAGIC_OCFS2: /* 0x7461636f remote */
366       return "ocfs2";
367     case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
368       return "panfs";
369     case S_MAGIC_PIPEFS: /* 0x50495045 remote */
370       /* FIXME: change syntax or add an optional attribute like "inotify:no".
371          The above is labeled as "remote" so that tail always uses polling,
372          but this isn't really a remote file system type.  */
373       return "pipefs";
374     case S_MAGIC_PROC: /* 0x9FA0 local */
375       return "proc";
376     case S_MAGIC_PSTOREFS: /* 0x6165676C local */
377       return "pstorefs";
378     case S_MAGIC_QNX4: /* 0x002F local */
379       return "qnx4";
380     case S_MAGIC_QNX6: /* 0x68191122 local */
381       return "qnx6";
382     case S_MAGIC_RAMFS: /* 0x858458F6 local */
383       return "ramfs";
384     case S_MAGIC_REISERFS: /* 0x52654973 local */
385       return "reiserfs";
386     case S_MAGIC_ROMFS: /* 0x7275 local */
387       return "romfs";
388     case S_MAGIC_RPC_PIPEFS: /* 0x67596969 local */
389       return "rpc_pipefs";
390     case S_MAGIC_SECURITYFS: /* 0x73636673 local */
391       return "securityfs";
392     case S_MAGIC_SELINUX: /* 0xF97CFF8C local */
393       return "selinux";
394     case S_MAGIC_SMB: /* 0x517B remote */
395       return "smb";
396     case S_MAGIC_SOCKFS: /* 0x534F434B local */
397       return "sockfs";
398     case S_MAGIC_SQUASHFS: /* 0x73717368 local */
399       return "squashfs";
400     case S_MAGIC_SYSFS: /* 0x62656572 local */
401       return "sysfs";
402     case S_MAGIC_SYSV2: /* 0x012FF7B6 local */
403       return "sysv2";
404     case S_MAGIC_SYSV4: /* 0x012FF7B5 local */
405       return "sysv4";
406     case S_MAGIC_TMPFS: /* 0x01021994 local */
407       return "tmpfs";
408     case S_MAGIC_UDF: /* 0x15013346 local */
409       return "udf";
410     case S_MAGIC_UFS: /* 0x00011954 local */
411       return "ufs";
412     case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 local */
413       return "ufs";
414     case S_MAGIC_USBDEVFS: /* 0x9FA2 local */
415       return "usbdevfs";
416     case S_MAGIC_V9FS: /* 0x01021997 local */
417       return "v9fs";
418     case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
419       return "vmhgfs";
420     case S_MAGIC_VXFS: /* 0xA501FCF5 local */
421       return "vxfs";
422     case S_MAGIC_VZFS: /* 0x565A4653 local */
423       return "vzfs";
424     case S_MAGIC_XENFS: /* 0xABBA1974 local */
425       return "xenfs";
426     case S_MAGIC_XENIX: /* 0x012FF7B4 local */
427       return "xenix";
428     case S_MAGIC_XFS: /* 0x58465342 local */
429       return "xfs";
430     case S_MAGIC_XIAFS: /* 0x012FD16D local */
431       return "xia";
432     case S_MAGIC_ZFS: /* 0x2FC12FC1 local */
433       return "zfs";
434
435 # elif __GNU__
436     case FSTYPE_UFS:
437       return "ufs";
438     case FSTYPE_NFS:
439       return "nfs";
440     case FSTYPE_GFS:
441       return "gfs";
442     case FSTYPE_LFS:
443       return "lfs";
444     case FSTYPE_SYSV:
445       return "sysv";
446     case FSTYPE_FTP:
447       return "ftp";
448     case FSTYPE_TAR:
449       return "tar";
450     case FSTYPE_AR:
451       return "ar";
452     case FSTYPE_CPIO:
453       return "cpio";
454     case FSTYPE_MSLOSS:
455       return "msloss";
456     case FSTYPE_CPM:
457       return "cpm";
458     case FSTYPE_HFS:
459       return "hfs";
460     case FSTYPE_DTFS:
461       return "dtfs";
462     case FSTYPE_GRFS:
463       return "grfs";
464     case FSTYPE_TERM:
465       return "term";
466     case FSTYPE_DEV:
467       return "dev";
468     case FSTYPE_PROC:
469       return "proc";
470     case FSTYPE_IFSOCK:
471       return "ifsock";
472     case FSTYPE_AFS:
473       return "afs";
474     case FSTYPE_DFS:
475       return "dfs";
476     case FSTYPE_PROC9:
477       return "proc9";
478     case FSTYPE_SOCKET:
479       return "socket";
480     case FSTYPE_MISC:
481       return "misc";
482     case FSTYPE_EXT2FS:
483       return "ext2/ext3";
484     case FSTYPE_HTTP:
485       return "http";
486     case FSTYPE_MEMFS:
487       return "memfs";
488     case FSTYPE_ISO9660:
489       return "iso9660";
490 # endif
491     default:
492       {
493         unsigned long int type = statfsbuf->f_type;
494         static char buf[sizeof "UNKNOWN (0x%lx)" - 3
495                         + (sizeof type * CHAR_BIT + 3) / 4];
496         sprintf (buf, "UNKNOWN (0x%lx)", type);
497         return buf;
498       }
499     }
500 #endif
501 }
502
503 static char * ATTRIBUTE_WARN_UNUSED_RESULT
504 human_access (struct stat const *statbuf)
505 {
506   static char modebuf[12];
507   filemodestring (statbuf, modebuf);
508   modebuf[10] = 0;
509   return modebuf;
510 }
511
512 static char * ATTRIBUTE_WARN_UNUSED_RESULT
513 human_time (struct timespec t)
514 {
515   static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
516                        (INT_STRLEN_BOUND (int) /* YYYY */
517                         + 1 /* because YYYY might equal INT_MAX + 1900 */
518                         + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
519   struct tm const *tm = localtime (&t.tv_sec);
520   if (tm == NULL)
521     return timetostr (t.tv_sec, str);
522   nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
523   return str;
524 }
525
526 /* PFORMAT points to a '%' followed by a prefix of a format, all of
527    size PREFIX_LEN.  The flags allowed for this format are
528    ALLOWED_FLAGS; remove other printf flags from the prefix, then
529    append SUFFIX.  */
530 static void
531 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
532              char const *suffix)
533 {
534   char *dst = pformat + 1;
535   char const *src;
536   char const *srclim = pformat + prefix_len;
537   for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
538     if (strchr (allowed_flags, *src))
539       *dst++ = *src;
540   while (src < srclim)
541     *dst++ = *src++;
542   strcpy (dst, suffix);
543 }
544
545 static void
546 out_string (char *pformat, size_t prefix_len, char const *arg)
547 {
548   make_format (pformat, prefix_len, "-", "s");
549   printf (pformat, arg);
550 }
551 static int
552 out_int (char *pformat, size_t prefix_len, intmax_t arg)
553 {
554   make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
555   return printf (pformat, arg);
556 }
557 static int
558 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
559 {
560   make_format (pformat, prefix_len, "'-0", PRIuMAX);
561   return printf (pformat, arg);
562 }
563 static void
564 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
565 {
566   make_format (pformat, prefix_len, "-#0", PRIoMAX);
567   printf (pformat, arg);
568 }
569 static void
570 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
571 {
572   make_format (pformat, prefix_len, "-#0", PRIxMAX);
573   printf (pformat, arg);
574 }
575 static int
576 out_minus_zero (char *pformat, size_t prefix_len)
577 {
578   make_format (pformat, prefix_len, "'-+ 0", ".0f");
579   return printf (pformat, -0.25);
580 }
581
582 /* Output the number of seconds since the Epoch, using a format that
583    acts like printf's %f format.  */
584 static void
585 out_epoch_sec (char *pformat, size_t prefix_len,
586                struct stat const *statbuf ATTRIBUTE_UNUSED,
587                struct timespec arg)
588 {
589   char *dot = memchr (pformat, '.', prefix_len);
590   size_t sec_prefix_len = prefix_len;
591   int width = 0;
592   int precision = 0;
593   bool frac_left_adjust = false;
594
595   if (dot)
596     {
597       sec_prefix_len = dot - pformat;
598       pformat[prefix_len] = '\0';
599
600       if (ISDIGIT (dot[1]))
601         {
602           long int lprec = strtol (dot + 1, NULL, 10);
603           precision = (lprec <= INT_MAX ? lprec : INT_MAX);
604         }
605       else
606         {
607           precision = 9;
608         }
609
610       if (precision && ISDIGIT (dot[-1]))
611         {
612           /* If a nontrivial width is given, subtract the width of the
613              decimal point and PRECISION digits that will be output
614              later.  */
615           char *p = dot;
616           *dot = '\0';
617
618           do
619             --p;
620           while (ISDIGIT (p[-1]));
621
622           long int lwidth = strtol (p, NULL, 10);
623           width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
624           if (1 < width)
625             {
626               p += (*p == '0');
627               sec_prefix_len = p - pformat;
628               int w_d = (decimal_point_len < width
629                          ? width - decimal_point_len
630                          : 0);
631               if (1 < w_d)
632                 {
633                   int w = w_d - precision;
634                   if (1 < w)
635                     {
636                       char *dst = pformat;
637                       for (char const *src = dst; src < p; src++)
638                         {
639                           if (*src == '-')
640                             frac_left_adjust = true;
641                           else
642                             *dst++ = *src;
643                         }
644                       sec_prefix_len =
645                         (dst - pformat
646                          + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
647                     }
648                 }
649             }
650         }
651     }
652
653   int divisor = 1;
654   for (int i = precision; i < 9; i++)
655     divisor *= 10;
656   int frac_sec = arg.tv_nsec / divisor;
657   int int_len;
658
659   if (TYPE_SIGNED (time_t))
660     {
661       bool minus_zero = false;
662       if (arg.tv_sec < 0 && arg.tv_nsec != 0)
663         {
664           int frac_sec_modulus = 1000000000 / divisor;
665           frac_sec = (frac_sec_modulus - frac_sec
666                       - (arg.tv_nsec % divisor != 0));
667           arg.tv_sec += (frac_sec != 0);
668           minus_zero = (arg.tv_sec == 0);
669         }
670       int_len = (minus_zero
671                  ? out_minus_zero (pformat, sec_prefix_len)
672                  : out_int (pformat, sec_prefix_len, arg.tv_sec));
673     }
674   else
675     int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
676
677   if (precision)
678     {
679       int prec = (precision < 9 ? precision : 9);
680       int trailing_prec = precision - prec;
681       int ilen = (int_len < 0 ? 0 : int_len);
682       int trailing_width = (ilen < width && decimal_point_len < width - ilen
683                             ? width - ilen - decimal_point_len - prec
684                             : 0);
685       printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
686               trailing_width, trailing_prec, 0);
687     }
688 }
689
690 /* Print the context information of FILENAME, and return true iff the
691    context could not be obtained.  */
692 static bool ATTRIBUTE_WARN_UNUSED_RESULT
693 out_file_context (char *pformat, size_t prefix_len, char const *filename)
694 {
695   char *scontext;
696   bool fail = false;
697
698   if ((follow_links
699        ? getfilecon (filename, &scontext)
700        : lgetfilecon (filename, &scontext)) < 0)
701     {
702       error (0, errno, _("failed to get security context of %s"),
703              quote (filename));
704       scontext = NULL;
705       fail = true;
706     }
707   strcpy (pformat + prefix_len, "s");
708   printf (pformat, (scontext ? scontext : "?"));
709   if (scontext)
710     freecon (scontext);
711   return fail;
712 }
713
714 /* Print statfs info.  Return zero upon success, nonzero upon failure.  */
715 static bool ATTRIBUTE_WARN_UNUSED_RESULT
716 print_statfs (char *pformat, size_t prefix_len, unsigned int m,
717               char const *filename,
718               void const *data)
719 {
720   STRUCT_STATVFS const *statfsbuf = data;
721   bool fail = false;
722
723   switch (m)
724     {
725     case 'n':
726       out_string (pformat, prefix_len, filename);
727       break;
728
729     case 'i':
730       {
731 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
732         uintmax_t fsid = statfsbuf->f_fsid;
733 #else
734         typedef unsigned int fsid_word;
735         verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
736         verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
737         verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
738         fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
739
740         /* Assume a little-endian word order, as that is compatible
741            with glibc's statvfs implementation.  */
742         uintmax_t fsid = 0;
743         int words = sizeof statfsbuf->f_fsid / sizeof *p;
744         int i;
745         for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
746           {
747             uintmax_t u = p[words - 1 - i];
748             fsid |= u << (i * CHAR_BIT * sizeof *p);
749           }
750 #endif
751         out_uint_x (pformat, prefix_len, fsid);
752       }
753       break;
754
755     case 'l':
756       OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
757       break;
758     case 't':
759 #if HAVE_STRUCT_STATXFS_F_TYPE
760       out_uint_x (pformat, prefix_len, statfsbuf->f_type);
761 #else
762       fputc ('?', stdout);
763 #endif
764       break;
765     case 'T':
766       out_string (pformat, prefix_len, human_fstype (statfsbuf));
767       break;
768     case 'b':
769       out_int (pformat, prefix_len, statfsbuf->f_blocks);
770       break;
771     case 'f':
772       out_int (pformat, prefix_len, statfsbuf->f_bfree);
773       break;
774     case 'a':
775       out_int (pformat, prefix_len, statfsbuf->f_bavail);
776       break;
777     case 's':
778       out_uint (pformat, prefix_len, statfsbuf->f_bsize);
779       break;
780     case 'S':
781       {
782         uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
783         if (! frsize)
784           frsize = statfsbuf->f_bsize;
785         out_uint (pformat, prefix_len, frsize);
786       }
787       break;
788     case 'c':
789       out_uint (pformat, prefix_len, statfsbuf->f_files);
790       break;
791     case 'd':
792       out_int (pformat, prefix_len, statfsbuf->f_ffree);
793       break;
794     default:
795       fputc ('?', stdout);
796       break;
797     }
798   return fail;
799 }
800
801 /* Return any bind mounted source for a path.
802    The caller should not free the returned buffer.
803    Return NULL if no bind mount found.  */
804 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
805 find_bind_mount (char const * name)
806 {
807   char const * bind_mount = NULL;
808
809   static struct mount_entry *mount_list;
810   static bool tried_mount_list = false;
811   if (!tried_mount_list) /* attempt/warn once per process.  */
812     {
813       if (!(mount_list = read_file_system_list (false)))
814         error (0, errno, "%s", _("cannot read table of mounted file systems"));
815       tried_mount_list = true;
816     }
817
818   struct mount_entry *me;
819   for (me = mount_list; me; me = me->me_next)
820     {
821       if (me->me_dummy && me->me_devname[0] == '/'
822           && STREQ (me->me_mountdir, name))
823         {
824           struct stat name_stats;
825           struct stat dev_stats;
826
827           if (stat (name, &name_stats) == 0
828               && stat (me->me_devname, &dev_stats) == 0
829               && SAME_INODE (name_stats, dev_stats))
830             {
831               bind_mount = me->me_devname;
832               break;
833             }
834         }
835     }
836
837   return bind_mount;
838 }
839
840 /* Print mount point.  Return zero upon success, nonzero upon failure.  */
841 static bool ATTRIBUTE_WARN_UNUSED_RESULT
842 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
843                  const struct stat *statp)
844 {
845
846   char const *np = "?", *bp = NULL;
847   char *mp = NULL;
848   bool fail = true;
849
850   /* Look for bind mounts first.  Note we output the immediate alias,
851      rather than further resolving to a base device mount point.  */
852   if (follow_links || !S_ISLNK (statp->st_mode))
853     {
854       char *resolved = canonicalize_file_name (filename);
855       if (!resolved)
856         {
857           error (0, errno, _("failed to canonicalize %s"), quote (filename));
858           goto print_mount_point;
859         }
860       bp = find_bind_mount (resolved);
861       free (resolved);
862       if (bp)
863         {
864           fail = false;
865           goto print_mount_point;
866         }
867     }
868
869   /* If there is no direct bind mount, then navigate
870      back up the tree looking for a device change.
871      Note we don't detect if any of the directory components
872      are bind mounted to the same device, but that's OK
873      since we've not directly queried them.  */
874   if ((mp = find_mount_point (filename, statp)))
875     {
876       /* This dir might be bind mounted to another device,
877          so we resolve the bound source in that case also.  */
878       bp = find_bind_mount (mp);
879       fail = false;
880     }
881
882 print_mount_point:
883
884   out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
885   free (mp);
886   return fail;
887 }
888
889 /* Map a TS with negative TS.tv_nsec to {0,0}.  */
890 static inline struct timespec
891 neg_to_zero (struct timespec ts)
892 {
893   if (0 <= ts.tv_nsec)
894     return ts;
895   struct timespec z = {0, 0};
896   return z;
897 }
898
899 /* Print stat info.  Return zero upon success, nonzero upon failure.  */
900 static bool
901 print_stat (char *pformat, size_t prefix_len, unsigned int m,
902             char const *filename, void const *data)
903 {
904   struct stat *statbuf = (struct stat *) data;
905   struct passwd *pw_ent;
906   struct group *gw_ent;
907   bool fail = false;
908
909   switch (m)
910     {
911     case 'n':
912       out_string (pformat, prefix_len, filename);
913       break;
914     case 'N':
915       out_string (pformat, prefix_len, quote (filename));
916       if (S_ISLNK (statbuf->st_mode))
917         {
918           char *linkname = areadlink_with_size (filename, statbuf->st_size);
919           if (linkname == NULL)
920             {
921               error (0, errno, _("cannot read symbolic link %s"),
922                      quote (filename));
923               return true;
924             }
925           printf (" -> ");
926           out_string (pformat, prefix_len, quote (linkname));
927           free (linkname);
928         }
929       break;
930     case 'd':
931       out_uint (pformat, prefix_len, statbuf->st_dev);
932       break;
933     case 'D':
934       out_uint_x (pformat, prefix_len, statbuf->st_dev);
935       break;
936     case 'i':
937       out_uint (pformat, prefix_len, statbuf->st_ino);
938       break;
939     case 'a':
940       out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
941       break;
942     case 'A':
943       out_string (pformat, prefix_len, human_access (statbuf));
944       break;
945     case 'f':
946       out_uint_x (pformat, prefix_len, statbuf->st_mode);
947       break;
948     case 'F':
949       out_string (pformat, prefix_len, file_type (statbuf));
950       break;
951     case 'h':
952       out_uint (pformat, prefix_len, statbuf->st_nlink);
953       break;
954     case 'u':
955       out_uint (pformat, prefix_len, statbuf->st_uid);
956       break;
957     case 'U':
958       setpwent ();
959       pw_ent = getpwuid (statbuf->st_uid);
960       out_string (pformat, prefix_len,
961                   pw_ent ? pw_ent->pw_name : "UNKNOWN");
962       break;
963     case 'g':
964       out_uint (pformat, prefix_len, statbuf->st_gid);
965       break;
966     case 'G':
967       setgrent ();
968       gw_ent = getgrgid (statbuf->st_gid);
969       out_string (pformat, prefix_len,
970                   gw_ent ? gw_ent->gr_name : "UNKNOWN");
971       break;
972     case 't':
973       out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
974       break;
975     case 'm':
976       fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
977       break;
978     case 'T':
979       out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
980       break;
981     case 's':
982       out_int (pformat, prefix_len, statbuf->st_size);
983       break;
984     case 'B':
985       out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
986       break;
987     case 'b':
988       out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
989       break;
990     case 'o':
991       out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
992       break;
993     case 'w':
994       {
995         struct timespec t = get_stat_birthtime (statbuf);
996         if (t.tv_nsec < 0)
997           out_string (pformat, prefix_len, "-");
998         else
999           out_string (pformat, prefix_len, human_time (t));
1000       }
1001       break;
1002     case 'W':
1003       out_epoch_sec (pformat, prefix_len, statbuf,
1004                      neg_to_zero (get_stat_birthtime (statbuf)));
1005       break;
1006     case 'x':
1007       out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
1008       break;
1009     case 'X':
1010       out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
1011       break;
1012     case 'y':
1013       out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
1014       break;
1015     case 'Y':
1016       out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
1017       break;
1018     case 'z':
1019       out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
1020       break;
1021     case 'Z':
1022       out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
1023       break;
1024     case 'C':
1025       fail |= out_file_context (pformat, prefix_len, filename);
1026       break;
1027     default:
1028       fputc ('?', stdout);
1029       break;
1030     }
1031   return fail;
1032 }
1033
1034 /* Output a single-character \ escape.  */
1035
1036 static void
1037 print_esc_char (char c)
1038 {
1039   switch (c)
1040     {
1041     case 'a':                   /* Alert. */
1042       c ='\a';
1043       break;
1044     case 'b':                   /* Backspace. */
1045       c ='\b';
1046       break;
1047     case 'e':                   /* Escape. */
1048       c ='\x1B';
1049       break;
1050     case 'f':                   /* Form feed. */
1051       c ='\f';
1052       break;
1053     case 'n':                   /* New line. */
1054       c ='\n';
1055       break;
1056     case 'r':                   /* Carriage return. */
1057       c ='\r';
1058       break;
1059     case 't':                   /* Horizontal tab. */
1060       c ='\t';
1061       break;
1062     case 'v':                   /* Vertical tab. */
1063       c ='\v';
1064       break;
1065     case '"':
1066     case '\\':
1067       break;
1068     default:
1069       error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
1070       break;
1071     }
1072   putchar (c);
1073 }
1074
1075 /* Print the information specified by the format string, FORMAT,
1076    calling PRINT_FUNC for each %-directive encountered.
1077    Return zero upon success, nonzero upon failure.  */
1078 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1079 print_it (char const *format, char const *filename,
1080           bool (*print_func) (char *, size_t, unsigned int,
1081                               char const *, void const *),
1082           void const *data)
1083 {
1084   bool fail = false;
1085
1086   /* Add 2 to accommodate our conversion of the stat '%s' format string
1087      to the longer printf '%llu' one.  */
1088   enum
1089     {
1090       MAX_ADDITIONAL_BYTES =
1091         (MAX (sizeof PRIdMAX,
1092               MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
1093          - 1)
1094     };
1095   size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1096   char *dest = xmalloc (n_alloc);
1097   char const *b;
1098   for (b = format; *b; b++)
1099     {
1100       switch (*b)
1101         {
1102         case '%':
1103           {
1104             size_t len = strspn (b + 1, printf_flags);
1105             char const *fmt_char = b + len + 1;
1106             fmt_char += strspn (fmt_char, digits);
1107             if (*fmt_char == '.')
1108               fmt_char += 1 + strspn (fmt_char + 1, digits);
1109             len = fmt_char - (b + 1);
1110             unsigned int fmt_code = *fmt_char;
1111             memcpy (dest, b, len + 1);
1112
1113             b = fmt_char;
1114             switch (fmt_code)
1115               {
1116               case '\0':
1117                 --b;
1118                 /* fall through */
1119               case '%':
1120                 if (0 < len)
1121                   {
1122                     dest[len + 1] = *fmt_char;
1123                     dest[len + 2] = '\0';
1124                     error (EXIT_FAILURE, 0, _("%s: invalid directive"),
1125                            quotearg_colon (dest));
1126                   }
1127                 putchar ('%');
1128                 break;
1129               default:
1130                 fail |= print_func (dest, len + 1, fmt_code, filename, data);
1131                 break;
1132               }
1133             break;
1134           }
1135
1136         case '\\':
1137           if ( ! interpret_backslash_escapes)
1138             {
1139               putchar ('\\');
1140               break;
1141             }
1142           ++b;
1143           if (isodigit (*b))
1144             {
1145               int esc_value = octtobin (*b);
1146               int esc_length = 1;       /* number of octal digits */
1147               for (++b; esc_length < 3 && isodigit (*b);
1148                    ++esc_length, ++b)
1149                 {
1150                   esc_value = esc_value * 8 + octtobin (*b);
1151                 }
1152               putchar (esc_value);
1153               --b;
1154             }
1155           else if (*b == 'x' && isxdigit (to_uchar (b[1])))
1156             {
1157               int esc_value = hextobin (b[1]);  /* Value of \xhh escape. */
1158               /* A hexadecimal \xhh escape sequence must have
1159                  1 or 2 hex. digits.  */
1160               ++b;
1161               if (isxdigit (to_uchar (b[1])))
1162                 {
1163                   ++b;
1164                   esc_value = esc_value * 16 + hextobin (*b);
1165                 }
1166               putchar (esc_value);
1167             }
1168           else if (*b == '\0')
1169             {
1170               error (0, 0, _("warning: backslash at end of format"));
1171               putchar ('\\');
1172               /* Arrange to exit the loop.  */
1173               --b;
1174             }
1175           else
1176             {
1177               print_esc_char (*b);
1178             }
1179           break;
1180
1181         default:
1182           putchar (*b);
1183           break;
1184         }
1185     }
1186   free (dest);
1187
1188   fputs (trailing_delim, stdout);
1189
1190   return fail;
1191 }
1192
1193 /* Stat the file system and print what we find.  */
1194 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1195 do_statfs (char const *filename, char const *format)
1196 {
1197   STRUCT_STATVFS statfsbuf;
1198
1199   if (STREQ (filename, "-"))
1200     {
1201       error (0, 0, _("using %s to denote standard input does not work"
1202                      " in file system mode"), quote (filename));
1203       return false;
1204     }
1205
1206   if (STATFS (filename, &statfsbuf) != 0)
1207     {
1208       error (0, errno, _("cannot read file system information for %s"),
1209              quote (filename));
1210       return false;
1211     }
1212
1213   bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1214   return ! fail;
1215 }
1216
1217 /* stat the file and print what we find */
1218 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1219 do_stat (char const *filename, char const *format,
1220          char const *format2)
1221 {
1222   struct stat statbuf;
1223
1224   if (STREQ (filename, "-"))
1225     {
1226       if (fstat (STDIN_FILENO, &statbuf) != 0)
1227         {
1228           error (0, errno, _("cannot stat standard input"));
1229           return false;
1230         }
1231     }
1232   /* We can't use the shorter
1233      (follow_links?stat:lstat) (filename, &statbug)
1234      since stat might be a function-like macro.  */
1235   else if ((follow_links
1236             ? stat (filename, &statbuf)
1237             : lstat (filename, &statbuf)) != 0)
1238     {
1239       error (0, errno, _("cannot stat %s"), quote (filename));
1240       return false;
1241     }
1242
1243   if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1244     format = format2;
1245
1246   bool fail = print_it (format, filename, print_stat, &statbuf);
1247   return ! fail;
1248 }
1249
1250 /* Return an allocated format string in static storage that
1251    corresponds to whether FS and TERSE options were declared.  */
1252 static char *
1253 default_format (bool fs, bool terse, bool device)
1254 {
1255   char *format;
1256   if (fs)
1257     {
1258       if (terse)
1259         format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1260       else
1261         {
1262           /* TRANSLATORS: This string uses format specifiers from
1263              'stat --help' with --file-system, and NOT from printf.  */
1264           format = xstrdup (_("  File: \"%n\"\n"
1265                               "    ID: %-8i Namelen: %-7l Type: %T\n"
1266                               "Block size: %-10s Fundamental block size: %S\n"
1267                               "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1268                               "Inodes: Total: %-10c Free: %d\n"));
1269         }
1270     }
1271   else /* ! fs */
1272     {
1273       if (terse)
1274         {
1275           if (0 < is_selinux_enabled ())
1276             format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1277                               " %X %Y %Z %W %o %C\n");
1278           else
1279             format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1280                               " %X %Y %Z %W %o\n");
1281         }
1282       else
1283         {
1284           char *temp;
1285           /* TRANSLATORS: This string uses format specifiers from
1286              'stat --help' without --file-system, and NOT from printf.  */
1287           format = xstrdup (_("\
1288   File: %N\n\
1289   Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1290 "));
1291
1292           temp = format;
1293           if (device)
1294             {
1295               /* TRANSLATORS: This string uses format specifiers from
1296                  'stat --help' without --file-system, and NOT from printf.  */
1297               format = xasprintf ("%s%s", format, _("\
1298 " "Device: %Dh/%dd\tInode: %-10i  Links: %-5h Device type: %t,%T\n\
1299 "));
1300             }
1301           else
1302             {
1303               /* TRANSLATORS: This string uses format specifiers from
1304                  'stat --help' without --file-system, and NOT from printf.  */
1305               format = xasprintf ("%s%s", format, _("\
1306 " "Device: %Dh/%dd\tInode: %-10i  Links: %h\n\
1307 "));
1308             }
1309           free (temp);
1310
1311           temp = format;
1312           /* TRANSLATORS: This string uses format specifiers from
1313              'stat --help' without --file-system, and NOT from printf.  */
1314           format = xasprintf ("%s%s", format, _("\
1315 " "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n\
1316 "));
1317           free (temp);
1318
1319           if (0 < is_selinux_enabled ())
1320             {
1321               temp = format;
1322               /* TRANSLATORS: This string uses format specifiers from
1323                  'stat --help' without --file-system, and NOT from printf.  */
1324               format = xasprintf ("%s%s", format, _("Context: %C\n"));
1325               free (temp);
1326             }
1327
1328           temp = format;
1329           /* TRANSLATORS: This string uses format specifiers from
1330              'stat --help' without --file-system, and NOT from printf.  */
1331           format = xasprintf ("%s%s", format,
1332                               _("Access: %x\n"
1333                                 "Modify: %y\n"
1334                                 "Change: %z\n"
1335                                 " Birth: %w\n"));
1336           free (temp);
1337         }
1338     }
1339   return format;
1340 }
1341
1342 void
1343 usage (int status)
1344 {
1345   if (status != EXIT_SUCCESS)
1346     emit_try_help ();
1347   else
1348     {
1349       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1350       fputs (_("\
1351 Display file or file system status.\n\
1352 \n\
1353   -L, --dereference     follow links\n\
1354   -f, --file-system     display file system status instead of file status\n\
1355 "), stdout);
1356       fputs (_("\
1357   -c  --format=FORMAT   use the specified FORMAT instead of the default;\n\
1358                           output a newline after each use of FORMAT\n\
1359       --printf=FORMAT   like --format, but interpret backslash escapes,\n\
1360                           and do not output a mandatory trailing newline.\n\
1361                           If you want a newline, include \\n in FORMAT\n\
1362   -t, --terse           print the information in terse form\n\
1363 "), stdout);
1364       fputs (HELP_OPTION_DESCRIPTION, stdout);
1365       fputs (VERSION_OPTION_DESCRIPTION, stdout);
1366
1367       fputs (_("\n\
1368 The valid format sequences for files (without --file-system):\n\
1369 \n\
1370   %a   access rights in octal\n\
1371   %A   access rights in human readable form\n\
1372   %b   number of blocks allocated (see %B)\n\
1373   %B   the size in bytes of each block reported by %b\n\
1374   %C   SELinux security context string\n\
1375 "), stdout);
1376       fputs (_("\
1377   %d   device number in decimal\n\
1378   %D   device number in hex\n\
1379   %f   raw mode in hex\n\
1380   %F   file type\n\
1381   %g   group ID of owner\n\
1382   %G   group name of owner\n\
1383 "), stdout);
1384       fputs (_("\
1385   %h   number of hard links\n\
1386   %i   inode number\n\
1387   %m   mount point\n\
1388   %n   file name\n\
1389   %N   quoted file name with dereference if symbolic link\n\
1390   %o   optimal I/O transfer size hint\n\
1391   %s   total size, in bytes\n\
1392   %t   major device type in hex\n\
1393   %T   minor device type in hex\n\
1394 "), stdout);
1395       fputs (_("\
1396   %u   user ID of owner\n\
1397   %U   user name of owner\n\
1398   %w   time of file birth, human-readable; - if unknown\n\
1399   %W   time of file birth, seconds since Epoch; 0 if unknown\n\
1400   %x   time of last access, human-readable\n\
1401   %X   time of last access, seconds since Epoch\n\
1402   %y   time of last modification, human-readable\n\
1403   %Y   time of last modification, seconds since Epoch\n\
1404   %z   time of last change, human-readable\n\
1405   %Z   time of last change, seconds since Epoch\n\
1406 \n\
1407 "), stdout);
1408
1409       fputs (_("\
1410 Valid format sequences for file systems:\n\
1411 \n\
1412   %a   free blocks available to non-superuser\n\
1413   %b   total data blocks in file system\n\
1414   %c   total file nodes in file system\n\
1415   %d   free file nodes in file system\n\
1416   %f   free blocks in file system\n\
1417 "), stdout);
1418       fputs (_("\
1419   %i   file system ID in hex\n\
1420   %l   maximum length of filenames\n\
1421   %n   file name\n\
1422   %s   block size (for faster transfers)\n\
1423   %S   fundamental block size (for block counts)\n\
1424   %t   file system type in hex\n\
1425   %T   file system type in human readable form\n\
1426 "), stdout);
1427       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1428       emit_ancillary_info ();
1429     }
1430   exit (status);
1431 }
1432
1433 int
1434 main (int argc, char *argv[])
1435 {
1436   int c;
1437   int i;
1438   bool fs = false;
1439   bool terse = false;
1440   char *format = NULL;
1441   char *format2;
1442   bool ok = true;
1443
1444   initialize_main (&argc, &argv);
1445   set_program_name (argv[0]);
1446   setlocale (LC_ALL, "");
1447   bindtextdomain (PACKAGE, LOCALEDIR);
1448   textdomain (PACKAGE);
1449
1450   struct lconv const *locale = localeconv ();
1451   decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1452   decimal_point_len = strlen (decimal_point);
1453
1454   atexit (close_stdout);
1455
1456   while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1457     {
1458       switch (c)
1459         {
1460         case PRINTF_OPTION:
1461           format = optarg;
1462           interpret_backslash_escapes = true;
1463           trailing_delim = "";
1464           break;
1465
1466         case 'c':
1467           format = optarg;
1468           interpret_backslash_escapes = false;
1469           trailing_delim = "\n";
1470           break;
1471
1472         case 'L':
1473           follow_links = true;
1474           break;
1475
1476         case 'f':
1477           fs = true;
1478           break;
1479
1480         case 't':
1481           terse = true;
1482           break;
1483
1484         case_GETOPT_HELP_CHAR;
1485
1486         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1487
1488         default:
1489           usage (EXIT_FAILURE);
1490         }
1491     }
1492
1493   if (argc == optind)
1494     {
1495       error (0, 0, _("missing operand"));
1496       usage (EXIT_FAILURE);
1497     }
1498
1499   if (format)
1500     format2 = format;
1501   else
1502     {
1503       format = default_format (fs, terse, false);
1504       format2 = default_format (fs, terse, true);
1505     }
1506
1507   for (i = optind; i < argc; i++)
1508     ok &= (fs
1509            ? do_statfs (argv[i], format)
1510            : do_stat (argv[i], format, format2));
1511
1512   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
1513 }