id: adjust/restrict smack support to newer versions of libsmack
[coreutils.git] / src / stat.c
blob947bc31e0a847b633673ab3c4e0ea68fdf0c97cd
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 Written by Michael Meskes. */
19 #include <config.h>
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
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>
59 #include "system.h"
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"
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)
102 dev_t device = dev_for_path (filename);
103 if (device < 0)
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;
113 /* If successful, buf->dev will be == device. */
114 return fs_stat_dev (device, buf);
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
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
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
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')
161 static char const digits[] = "0123456789";
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";
171 #define PROGRAM_NAME "stat"
173 #define AUTHORS proper_name ("Michael Meskes")
175 enum
177 PRINTF_OPTION = CHAR_MAX + 1
180 static struct option const long_options[] =
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}
193 /* Whether to follow symbolic links; True for --dereference (-L). */
194 static bool follow_links;
196 /* Whether to interpret backslash-escape sequences.
197 True for --printf=FMT, not for --format=FMT (-c). */
198 static bool interpret_backslash_escapes;
200 /* The trailing delimiter string:
201 "" for --printf=FMT, "\n" for --format=FMT (-c). */
202 static char const *trailing_delim = "";
204 /* The representation of the decimal point in the current locale. */
205 static char const *decimal_point;
206 static size_t decimal_point_len;
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)
217 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
218 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
219 #else
220 switch (statfsbuf->f_type)
222 # if defined __linux__
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
236 /* Also compare with the list in "man 2 statfs" using the
237 fs-magic-compare make target. */
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. */
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_EFIVARFS: /* 0xDE5E81E4 local */
292 return "efivarfs";
293 case S_MAGIC_EFS: /* 0x00414A53 local */
294 return "efs";
295 case S_MAGIC_EXOFS: /* 0x5DF5 local */
296 return "exofs";
297 case S_MAGIC_EXT: /* 0x137D local */
298 return "ext";
299 case S_MAGIC_EXT2: /* 0xEF53 local */
300 return "ext2/ext3";
301 case S_MAGIC_EXT2_OLD: /* 0xEF51 local */
302 return "ext2";
303 case S_MAGIC_F2FS: /* 0xF2F52010 local */
304 return "f2fs";
305 case S_MAGIC_FAT: /* 0x4006 local */
306 return "fat";
307 case S_MAGIC_FHGFS: /* 0x19830326 remote */
308 return "fhgfs";
309 case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
310 return "fuseblk";
311 case S_MAGIC_FUSECTL: /* 0x65735543 remote */
312 return "fusectl";
313 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA local */
314 return "futexfs";
315 case S_MAGIC_GFS: /* 0x1161970 remote */
316 return "gfs/gfs2";
317 case S_MAGIC_GPFS: /* 0x47504653 remote */
318 return "gpfs";
319 case S_MAGIC_HFS: /* 0x4244 local */
320 return "hfs";
321 case S_MAGIC_HPFS: /* 0xF995E849 local */
322 return "hpfs";
323 case S_MAGIC_HUGETLBFS: /* 0x958458F6 local */
324 return "hugetlbfs";
325 case S_MAGIC_MTD_INODE_FS: /* 0x11307854 local */
326 return "inodefs";
327 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA local */
328 return "inotifyfs";
329 case S_MAGIC_ISOFS: /* 0x9660 local */
330 return "isofs";
331 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 local */
332 return "isofs";
333 case S_MAGIC_ISOFS_WIN: /* 0x4000 local */
334 return "isofs";
335 case S_MAGIC_JFFS: /* 0x07C0 local */
336 return "jffs";
337 case S_MAGIC_JFFS2: /* 0x72B6 local */
338 return "jffs2";
339 case S_MAGIC_JFS: /* 0x3153464A local */
340 return "jfs";
341 case S_MAGIC_KAFS: /* 0x6B414653 remote */
342 return "k-afs";
343 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
344 return "lustre";
345 case S_MAGIC_MINIX: /* 0x137F local */
346 return "minix";
347 case S_MAGIC_MINIX_30: /* 0x138F local */
348 return "minix (30 char.)";
349 case S_MAGIC_MINIX_V2: /* 0x2468 local */
350 return "minix v2";
351 case S_MAGIC_MINIX_V2_30: /* 0x2478 local */
352 return "minix v2 (30 char.)";
353 case S_MAGIC_MINIX_V3: /* 0x4D5A local */
354 return "minix3";
355 case S_MAGIC_MQUEUE: /* 0x19800202 local */
356 return "mqueue";
357 case S_MAGIC_MSDOS: /* 0x4D44 local */
358 return "msdos";
359 case S_MAGIC_NCP: /* 0x564C remote */
360 return "novell";
361 case S_MAGIC_NFS: /* 0x6969 remote */
362 return "nfs";
363 case S_MAGIC_NFSD: /* 0x6E667364 remote */
364 return "nfsd";
365 case S_MAGIC_NILFS: /* 0x3434 local */
366 return "nilfs";
367 case S_MAGIC_NTFS: /* 0x5346544E local */
368 return "ntfs";
369 case S_MAGIC_OPENPROM: /* 0x9FA1 local */
370 return "openprom";
371 case S_MAGIC_OCFS2: /* 0x7461636f remote */
372 return "ocfs2";
373 case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
374 return "panfs";
375 case S_MAGIC_PIPEFS: /* 0x50495045 remote */
376 /* FIXME: change syntax or add an optional attribute like "inotify:no".
377 The above is labeled as "remote" so that tail always uses polling,
378 but this isn't really a remote file system type. */
379 return "pipefs";
380 case S_MAGIC_PROC: /* 0x9FA0 local */
381 return "proc";
382 case S_MAGIC_PSTOREFS: /* 0x6165676C local */
383 return "pstorefs";
384 case S_MAGIC_QNX4: /* 0x002F local */
385 return "qnx4";
386 case S_MAGIC_QNX6: /* 0x68191122 local */
387 return "qnx6";
388 case S_MAGIC_RAMFS: /* 0x858458F6 local */
389 return "ramfs";
390 case S_MAGIC_REISERFS: /* 0x52654973 local */
391 return "reiserfs";
392 case S_MAGIC_ROMFS: /* 0x7275 local */
393 return "romfs";
394 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 local */
395 return "rpc_pipefs";
396 case S_MAGIC_SECURITYFS: /* 0x73636673 local */
397 return "securityfs";
398 case S_MAGIC_SELINUX: /* 0xF97CFF8C local */
399 return "selinux";
400 case S_MAGIC_SMB: /* 0x517B remote */
401 return "smb";
402 case S_MAGIC_SNFS: /* 0xBEEFDEAD remote */
403 return "snfs";
404 case S_MAGIC_SOCKFS: /* 0x534F434B local */
405 return "sockfs";
406 case S_MAGIC_SQUASHFS: /* 0x73717368 local */
407 return "squashfs";
408 case S_MAGIC_SYSFS: /* 0x62656572 local */
409 return "sysfs";
410 case S_MAGIC_SYSV2: /* 0x012FF7B6 local */
411 return "sysv2";
412 case S_MAGIC_SYSV4: /* 0x012FF7B5 local */
413 return "sysv4";
414 case S_MAGIC_TMPFS: /* 0x01021994 local */
415 return "tmpfs";
416 case S_MAGIC_UBIFS: /* 0x24051905 local */
417 return "ubifs";
418 case S_MAGIC_UDF: /* 0x15013346 local */
419 return "udf";
420 case S_MAGIC_UFS: /* 0x00011954 local */
421 return "ufs";
422 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 local */
423 return "ufs";
424 case S_MAGIC_USBDEVFS: /* 0x9FA2 local */
425 return "usbdevfs";
426 case S_MAGIC_V9FS: /* 0x01021997 local */
427 return "v9fs";
428 case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
429 return "vmhgfs";
430 case S_MAGIC_VXFS: /* 0xA501FCF5 local */
431 return "vxfs";
432 case S_MAGIC_VZFS: /* 0x565A4653 local */
433 return "vzfs";
434 case S_MAGIC_XENFS: /* 0xABBA1974 local */
435 return "xenfs";
436 case S_MAGIC_XENIX: /* 0x012FF7B4 local */
437 return "xenix";
438 case S_MAGIC_XFS: /* 0x58465342 local */
439 return "xfs";
440 case S_MAGIC_XIAFS: /* 0x012FD16D local */
441 return "xia";
442 case S_MAGIC_ZFS: /* 0x2FC12FC1 local */
443 return "zfs";
445 # elif __GNU__
446 case FSTYPE_UFS:
447 return "ufs";
448 case FSTYPE_NFS:
449 return "nfs";
450 case FSTYPE_GFS:
451 return "gfs";
452 case FSTYPE_LFS:
453 return "lfs";
454 case FSTYPE_SYSV:
455 return "sysv";
456 case FSTYPE_FTP:
457 return "ftp";
458 case FSTYPE_TAR:
459 return "tar";
460 case FSTYPE_AR:
461 return "ar";
462 case FSTYPE_CPIO:
463 return "cpio";
464 case FSTYPE_MSLOSS:
465 return "msloss";
466 case FSTYPE_CPM:
467 return "cpm";
468 case FSTYPE_HFS:
469 return "hfs";
470 case FSTYPE_DTFS:
471 return "dtfs";
472 case FSTYPE_GRFS:
473 return "grfs";
474 case FSTYPE_TERM:
475 return "term";
476 case FSTYPE_DEV:
477 return "dev";
478 case FSTYPE_PROC:
479 return "proc";
480 case FSTYPE_IFSOCK:
481 return "ifsock";
482 case FSTYPE_AFS:
483 return "afs";
484 case FSTYPE_DFS:
485 return "dfs";
486 case FSTYPE_PROC9:
487 return "proc9";
488 case FSTYPE_SOCKET:
489 return "socket";
490 case FSTYPE_MISC:
491 return "misc";
492 case FSTYPE_EXT2FS:
493 return "ext2/ext3";
494 case FSTYPE_HTTP:
495 return "http";
496 case FSTYPE_MEMFS:
497 return "memfs";
498 case FSTYPE_ISO9660:
499 return "iso9660";
500 # endif
501 default:
503 unsigned long int type = statfsbuf->f_type;
504 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
505 + (sizeof type * CHAR_BIT + 3) / 4];
506 sprintf (buf, "UNKNOWN (0x%lx)", type);
507 return buf;
510 #endif
513 static char * ATTRIBUTE_WARN_UNUSED_RESULT
514 human_access (struct stat const *statbuf)
516 static char modebuf[12];
517 filemodestring (statbuf, modebuf);
518 modebuf[10] = 0;
519 return modebuf;
522 static char * ATTRIBUTE_WARN_UNUSED_RESULT
523 human_time (struct timespec t)
525 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
526 (INT_STRLEN_BOUND (int) /* YYYY */
527 + 1 /* because YYYY might equal INT_MAX + 1900 */
528 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
529 struct tm const *tm = localtime (&t.tv_sec);
530 if (tm == NULL)
531 return timetostr (t.tv_sec, str);
532 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
533 return str;
536 /* PFORMAT points to a '%' followed by a prefix of a format, all of
537 size PREFIX_LEN. The flags allowed for this format are
538 ALLOWED_FLAGS; remove other printf flags from the prefix, then
539 append SUFFIX. */
540 static void
541 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
542 char const *suffix)
544 char *dst = pformat + 1;
545 char const *src;
546 char const *srclim = pformat + prefix_len;
547 for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
548 if (strchr (allowed_flags, *src))
549 *dst++ = *src;
550 while (src < srclim)
551 *dst++ = *src++;
552 strcpy (dst, suffix);
555 static void
556 out_string (char *pformat, size_t prefix_len, char const *arg)
558 make_format (pformat, prefix_len, "-", "s");
559 printf (pformat, arg);
561 static int
562 out_int (char *pformat, size_t prefix_len, intmax_t arg)
564 make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
565 return printf (pformat, arg);
567 static int
568 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
570 make_format (pformat, prefix_len, "'-0", PRIuMAX);
571 return printf (pformat, arg);
573 static void
574 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
576 make_format (pformat, prefix_len, "-#0", PRIoMAX);
577 printf (pformat, arg);
579 static void
580 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
582 make_format (pformat, prefix_len, "-#0", PRIxMAX);
583 printf (pformat, arg);
585 static int
586 out_minus_zero (char *pformat, size_t prefix_len)
588 make_format (pformat, prefix_len, "'-+ 0", ".0f");
589 return printf (pformat, -0.25);
592 /* Output the number of seconds since the Epoch, using a format that
593 acts like printf's %f format. */
594 static void
595 out_epoch_sec (char *pformat, size_t prefix_len,
596 struct stat const *statbuf _GL_UNUSED,
597 struct timespec arg)
599 char *dot = memchr (pformat, '.', prefix_len);
600 size_t sec_prefix_len = prefix_len;
601 int width = 0;
602 int precision = 0;
603 bool frac_left_adjust = false;
605 if (dot)
607 sec_prefix_len = dot - pformat;
608 pformat[prefix_len] = '\0';
610 if (ISDIGIT (dot[1]))
612 long int lprec = strtol (dot + 1, NULL, 10);
613 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
615 else
617 precision = 9;
620 if (precision && ISDIGIT (dot[-1]))
622 /* If a nontrivial width is given, subtract the width of the
623 decimal point and PRECISION digits that will be output
624 later. */
625 char *p = dot;
626 *dot = '\0';
629 --p;
630 while (ISDIGIT (p[-1]));
632 long int lwidth = strtol (p, NULL, 10);
633 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
634 if (1 < width)
636 p += (*p == '0');
637 sec_prefix_len = p - pformat;
638 int w_d = (decimal_point_len < width
639 ? width - decimal_point_len
640 : 0);
641 if (1 < w_d)
643 int w = w_d - precision;
644 if (1 < w)
646 char *dst = pformat;
647 for (char const *src = dst; src < p; src++)
649 if (*src == '-')
650 frac_left_adjust = true;
651 else
652 *dst++ = *src;
654 sec_prefix_len =
655 (dst - pformat
656 + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
663 int divisor = 1;
664 for (int i = precision; i < 9; i++)
665 divisor *= 10;
666 int frac_sec = arg.tv_nsec / divisor;
667 int int_len;
669 if (TYPE_SIGNED (time_t))
671 bool minus_zero = false;
672 if (arg.tv_sec < 0 && arg.tv_nsec != 0)
674 int frac_sec_modulus = 1000000000 / divisor;
675 frac_sec = (frac_sec_modulus - frac_sec
676 - (arg.tv_nsec % divisor != 0));
677 arg.tv_sec += (frac_sec != 0);
678 minus_zero = (arg.tv_sec == 0);
680 int_len = (minus_zero
681 ? out_minus_zero (pformat, sec_prefix_len)
682 : out_int (pformat, sec_prefix_len, arg.tv_sec));
684 else
685 int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
687 if (precision)
689 int prec = (precision < 9 ? precision : 9);
690 int trailing_prec = precision - prec;
691 int ilen = (int_len < 0 ? 0 : int_len);
692 int trailing_width = (ilen < width && decimal_point_len < width - ilen
693 ? width - ilen - decimal_point_len - prec
694 : 0);
695 printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
696 trailing_width, trailing_prec, 0);
700 /* Print the context information of FILENAME, and return true iff the
701 context could not be obtained. */
702 static bool ATTRIBUTE_WARN_UNUSED_RESULT
703 out_file_context (char *pformat, size_t prefix_len, char const *filename)
705 char *scontext;
706 bool fail = false;
708 if ((follow_links
709 ? getfilecon (filename, &scontext)
710 : lgetfilecon (filename, &scontext)) < 0)
712 error (0, errno, _("failed to get security context of %s"),
713 quote (filename));
714 scontext = NULL;
715 fail = true;
717 strcpy (pformat + prefix_len, "s");
718 printf (pformat, (scontext ? scontext : "?"));
719 if (scontext)
720 freecon (scontext);
721 return fail;
724 /* Print statfs info. Return zero upon success, nonzero upon failure. */
725 static bool ATTRIBUTE_WARN_UNUSED_RESULT
726 print_statfs (char *pformat, size_t prefix_len, unsigned int m,
727 char const *filename,
728 void const *data)
730 STRUCT_STATVFS const *statfsbuf = data;
731 bool fail = false;
733 switch (m)
735 case 'n':
736 out_string (pformat, prefix_len, filename);
737 break;
739 case 'i':
741 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
742 uintmax_t fsid = statfsbuf->f_fsid;
743 #else
744 typedef unsigned int fsid_word;
745 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
746 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
747 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
748 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
750 /* Assume a little-endian word order, as that is compatible
751 with glibc's statvfs implementation. */
752 uintmax_t fsid = 0;
753 int words = sizeof statfsbuf->f_fsid / sizeof *p;
754 int i;
755 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
757 uintmax_t u = p[words - 1 - i];
758 fsid |= u << (i * CHAR_BIT * sizeof *p);
760 #endif
761 out_uint_x (pformat, prefix_len, fsid);
763 break;
765 case 'l':
766 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
767 break;
768 case 't':
769 #if HAVE_STRUCT_STATXFS_F_TYPE
770 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
771 #else
772 fputc ('?', stdout);
773 #endif
774 break;
775 case 'T':
776 out_string (pformat, prefix_len, human_fstype (statfsbuf));
777 break;
778 case 'b':
779 out_int (pformat, prefix_len, statfsbuf->f_blocks);
780 break;
781 case 'f':
782 out_int (pformat, prefix_len, statfsbuf->f_bfree);
783 break;
784 case 'a':
785 out_int (pformat, prefix_len, statfsbuf->f_bavail);
786 break;
787 case 's':
788 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
789 break;
790 case 'S':
792 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
793 if (! frsize)
794 frsize = statfsbuf->f_bsize;
795 out_uint (pformat, prefix_len, frsize);
797 break;
798 case 'c':
799 out_uint (pformat, prefix_len, statfsbuf->f_files);
800 break;
801 case 'd':
802 out_int (pformat, prefix_len, statfsbuf->f_ffree);
803 break;
804 default:
805 fputc ('?', stdout);
806 break;
808 return fail;
811 /* Return any bind mounted source for a path.
812 The caller should not free the returned buffer.
813 Return NULL if no bind mount found. */
814 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
815 find_bind_mount (char const * name)
817 char const * bind_mount = NULL;
819 static struct mount_entry *mount_list;
820 static bool tried_mount_list = false;
821 if (!tried_mount_list) /* attempt/warn once per process. */
823 if (!(mount_list = read_file_system_list (false)))
824 error (0, errno, "%s", _("cannot read table of mounted file systems"));
825 tried_mount_list = true;
828 struct mount_entry *me;
829 for (me = mount_list; me; me = me->me_next)
831 if (me->me_dummy && me->me_devname[0] == '/'
832 && STREQ (me->me_mountdir, name))
834 struct stat name_stats;
835 struct stat dev_stats;
837 if (stat (name, &name_stats) == 0
838 && stat (me->me_devname, &dev_stats) == 0
839 && SAME_INODE (name_stats, dev_stats))
841 bind_mount = me->me_devname;
842 break;
847 return bind_mount;
850 /* Print mount point. Return zero upon success, nonzero upon failure. */
851 static bool ATTRIBUTE_WARN_UNUSED_RESULT
852 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
853 const struct stat *statp)
856 char const *np = "?", *bp = NULL;
857 char *mp = NULL;
858 bool fail = true;
860 /* Look for bind mounts first. Note we output the immediate alias,
861 rather than further resolving to a base device mount point. */
862 if (follow_links || !S_ISLNK (statp->st_mode))
864 char *resolved = canonicalize_file_name (filename);
865 if (!resolved)
867 error (0, errno, _("failed to canonicalize %s"), quote (filename));
868 goto print_mount_point;
870 bp = find_bind_mount (resolved);
871 free (resolved);
872 if (bp)
874 fail = false;
875 goto print_mount_point;
879 /* If there is no direct bind mount, then navigate
880 back up the tree looking for a device change.
881 Note we don't detect if any of the directory components
882 are bind mounted to the same device, but that's OK
883 since we've not directly queried them. */
884 if ((mp = find_mount_point (filename, statp)))
886 /* This dir might be bind mounted to another device,
887 so we resolve the bound source in that case also. */
888 bp = find_bind_mount (mp);
889 fail = false;
892 print_mount_point:
894 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
895 free (mp);
896 return fail;
899 /* Map a TS with negative TS.tv_nsec to {0,0}. */
900 static inline struct timespec
901 neg_to_zero (struct timespec ts)
903 if (0 <= ts.tv_nsec)
904 return ts;
905 struct timespec z = {0, 0};
906 return z;
909 /* Print stat info. Return zero upon success, nonzero upon failure. */
910 static bool
911 print_stat (char *pformat, size_t prefix_len, unsigned int m,
912 char const *filename, void const *data)
914 struct stat *statbuf = (struct stat *) data;
915 struct passwd *pw_ent;
916 struct group *gw_ent;
917 bool fail = false;
919 switch (m)
921 case 'n':
922 out_string (pformat, prefix_len, filename);
923 break;
924 case 'N':
925 out_string (pformat, prefix_len, quote (filename));
926 if (S_ISLNK (statbuf->st_mode))
928 char *linkname = areadlink_with_size (filename, statbuf->st_size);
929 if (linkname == NULL)
931 error (0, errno, _("cannot read symbolic link %s"),
932 quote (filename));
933 return true;
935 printf (" -> ");
936 out_string (pformat, prefix_len, quote (linkname));
937 free (linkname);
939 break;
940 case 'd':
941 out_uint (pformat, prefix_len, statbuf->st_dev);
942 break;
943 case 'D':
944 out_uint_x (pformat, prefix_len, statbuf->st_dev);
945 break;
946 case 'i':
947 out_uint (pformat, prefix_len, statbuf->st_ino);
948 break;
949 case 'a':
950 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
951 break;
952 case 'A':
953 out_string (pformat, prefix_len, human_access (statbuf));
954 break;
955 case 'f':
956 out_uint_x (pformat, prefix_len, statbuf->st_mode);
957 break;
958 case 'F':
959 out_string (pformat, prefix_len, file_type (statbuf));
960 break;
961 case 'h':
962 out_uint (pformat, prefix_len, statbuf->st_nlink);
963 break;
964 case 'u':
965 out_uint (pformat, prefix_len, statbuf->st_uid);
966 break;
967 case 'U':
968 pw_ent = getpwuid (statbuf->st_uid);
969 out_string (pformat, prefix_len,
970 pw_ent ? pw_ent->pw_name : "UNKNOWN");
971 break;
972 case 'g':
973 out_uint (pformat, prefix_len, statbuf->st_gid);
974 break;
975 case 'G':
976 gw_ent = getgrgid (statbuf->st_gid);
977 out_string (pformat, prefix_len,
978 gw_ent ? gw_ent->gr_name : "UNKNOWN");
979 break;
980 case 't':
981 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
982 break;
983 case 'm':
984 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
985 break;
986 case 'T':
987 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
988 break;
989 case 's':
990 out_int (pformat, prefix_len, statbuf->st_size);
991 break;
992 case 'B':
993 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
994 break;
995 case 'b':
996 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
997 break;
998 case 'o':
999 out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
1000 break;
1001 case 'w':
1003 struct timespec t = get_stat_birthtime (statbuf);
1004 if (t.tv_nsec < 0)
1005 out_string (pformat, prefix_len, "-");
1006 else
1007 out_string (pformat, prefix_len, human_time (t));
1009 break;
1010 case 'W':
1011 out_epoch_sec (pformat, prefix_len, statbuf,
1012 neg_to_zero (get_stat_birthtime (statbuf)));
1013 break;
1014 case 'x':
1015 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
1016 break;
1017 case 'X':
1018 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
1019 break;
1020 case 'y':
1021 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
1022 break;
1023 case 'Y':
1024 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
1025 break;
1026 case 'z':
1027 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
1028 break;
1029 case 'Z':
1030 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
1031 break;
1032 case 'C':
1033 fail |= out_file_context (pformat, prefix_len, filename);
1034 break;
1035 default:
1036 fputc ('?', stdout);
1037 break;
1039 return fail;
1042 /* Output a single-character \ escape. */
1044 static void
1045 print_esc_char (char c)
1047 switch (c)
1049 case 'a': /* Alert. */
1050 c ='\a';
1051 break;
1052 case 'b': /* Backspace. */
1053 c ='\b';
1054 break;
1055 case 'e': /* Escape. */
1056 c ='\x1B';
1057 break;
1058 case 'f': /* Form feed. */
1059 c ='\f';
1060 break;
1061 case 'n': /* New line. */
1062 c ='\n';
1063 break;
1064 case 'r': /* Carriage return. */
1065 c ='\r';
1066 break;
1067 case 't': /* Horizontal tab. */
1068 c ='\t';
1069 break;
1070 case 'v': /* Vertical tab. */
1071 c ='\v';
1072 break;
1073 case '"':
1074 case '\\':
1075 break;
1076 default:
1077 error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
1078 break;
1080 putchar (c);
1083 /* Print the information specified by the format string, FORMAT,
1084 calling PRINT_FUNC for each %-directive encountered.
1085 Return zero upon success, nonzero upon failure. */
1086 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1087 print_it (char const *format, char const *filename,
1088 bool (*print_func) (char *, size_t, unsigned int,
1089 char const *, void const *),
1090 void const *data)
1092 bool fail = false;
1094 /* Add 2 to accommodate our conversion of the stat '%s' format string
1095 to the longer printf '%llu' one. */
1096 enum
1098 MAX_ADDITIONAL_BYTES =
1099 (MAX (sizeof PRIdMAX,
1100 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
1101 - 1)
1103 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1104 char *dest = xmalloc (n_alloc);
1105 char const *b;
1106 for (b = format; *b; b++)
1108 switch (*b)
1110 case '%':
1112 size_t len = strspn (b + 1, printf_flags);
1113 char const *fmt_char = b + len + 1;
1114 fmt_char += strspn (fmt_char, digits);
1115 if (*fmt_char == '.')
1116 fmt_char += 1 + strspn (fmt_char + 1, digits);
1117 len = fmt_char - (b + 1);
1118 unsigned int fmt_code = *fmt_char;
1119 memcpy (dest, b, len + 1);
1121 b = fmt_char;
1122 switch (fmt_code)
1124 case '\0':
1125 --b;
1126 /* fall through */
1127 case '%':
1128 if (0 < len)
1130 dest[len + 1] = *fmt_char;
1131 dest[len + 2] = '\0';
1132 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
1133 quotearg_colon (dest));
1135 putchar ('%');
1136 break;
1137 default:
1138 fail |= print_func (dest, len + 1, fmt_code, filename, data);
1139 break;
1141 break;
1144 case '\\':
1145 if ( ! interpret_backslash_escapes)
1147 putchar ('\\');
1148 break;
1150 ++b;
1151 if (isodigit (*b))
1153 int esc_value = octtobin (*b);
1154 int esc_length = 1; /* number of octal digits */
1155 for (++b; esc_length < 3 && isodigit (*b);
1156 ++esc_length, ++b)
1158 esc_value = esc_value * 8 + octtobin (*b);
1160 putchar (esc_value);
1161 --b;
1163 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
1165 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
1166 /* A hexadecimal \xhh escape sequence must have
1167 1 or 2 hex. digits. */
1168 ++b;
1169 if (isxdigit (to_uchar (b[1])))
1171 ++b;
1172 esc_value = esc_value * 16 + hextobin (*b);
1174 putchar (esc_value);
1176 else if (*b == '\0')
1178 error (0, 0, _("warning: backslash at end of format"));
1179 putchar ('\\');
1180 /* Arrange to exit the loop. */
1181 --b;
1183 else
1185 print_esc_char (*b);
1187 break;
1189 default:
1190 putchar (*b);
1191 break;
1194 free (dest);
1196 fputs (trailing_delim, stdout);
1198 return fail;
1201 /* Stat the file system and print what we find. */
1202 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1203 do_statfs (char const *filename, char const *format)
1205 STRUCT_STATVFS statfsbuf;
1207 if (STREQ (filename, "-"))
1209 error (0, 0, _("using %s to denote standard input does not work"
1210 " in file system mode"), quote (filename));
1211 return false;
1214 if (STATFS (filename, &statfsbuf) != 0)
1216 error (0, errno, _("cannot read file system information for %s"),
1217 quote (filename));
1218 return false;
1221 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1222 return ! fail;
1225 /* stat the file and print what we find */
1226 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1227 do_stat (char const *filename, char const *format,
1228 char const *format2)
1230 struct stat statbuf;
1232 if (STREQ (filename, "-"))
1234 if (fstat (STDIN_FILENO, &statbuf) != 0)
1236 error (0, errno, _("cannot stat standard input"));
1237 return false;
1240 /* We can't use the shorter
1241 (follow_links?stat:lstat) (filename, &statbug)
1242 since stat might be a function-like macro. */
1243 else if ((follow_links
1244 ? stat (filename, &statbuf)
1245 : lstat (filename, &statbuf)) != 0)
1247 error (0, errno, _("cannot stat %s"), quote (filename));
1248 return false;
1251 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1252 format = format2;
1254 bool fail = print_it (format, filename, print_stat, &statbuf);
1255 return ! fail;
1258 /* Return an allocated format string in static storage that
1259 corresponds to whether FS and TERSE options were declared. */
1260 static char *
1261 default_format (bool fs, bool terse, bool device)
1263 char *format;
1264 if (fs)
1266 if (terse)
1267 format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1268 else
1270 /* TRANSLATORS: This string uses format specifiers from
1271 'stat --help' with --file-system, and NOT from printf. */
1272 format = xstrdup (_(" File: \"%n\"\n"
1273 " ID: %-8i Namelen: %-7l Type: %T\n"
1274 "Block size: %-10s Fundamental block size: %S\n"
1275 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1276 "Inodes: Total: %-10c Free: %d\n"));
1279 else /* ! fs */
1281 if (terse)
1283 if (0 < is_selinux_enabled ())
1284 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1285 " %X %Y %Z %W %o %C\n");
1286 else
1287 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1288 " %X %Y %Z %W %o\n");
1290 else
1292 char *temp;
1293 /* TRANSLATORS: This string uses format specifiers from
1294 'stat --help' without --file-system, and NOT from printf. */
1295 format = xstrdup (_("\
1296 File: %N\n\
1297 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1298 "));
1300 temp = format;
1301 if (device)
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: %-5h Device type: %t,%T\n\
1307 "));
1309 else
1311 /* TRANSLATORS: This string uses format specifiers from
1312 'stat --help' without --file-system, and NOT from printf. */
1313 format = xasprintf ("%s%s", format, _("\
1314 " "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1315 "));
1317 free (temp);
1319 temp = format;
1320 /* TRANSLATORS: This string uses format specifiers from
1321 'stat --help' without --file-system, and NOT from printf. */
1322 format = xasprintf ("%s%s", format, _("\
1323 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1324 "));
1325 free (temp);
1327 if (0 < is_selinux_enabled ())
1329 temp = format;
1330 /* TRANSLATORS: This string uses format specifiers from
1331 'stat --help' without --file-system, and NOT from printf. */
1332 format = xasprintf ("%s%s", format, _("Context: %C\n"));
1333 free (temp);
1336 temp = format;
1337 /* TRANSLATORS: This string uses format specifiers from
1338 'stat --help' without --file-system, and NOT from printf. */
1339 format = xasprintf ("%s%s", format,
1340 _("Access: %x\n"
1341 "Modify: %y\n"
1342 "Change: %z\n"
1343 " Birth: %w\n"));
1344 free (temp);
1347 return format;
1350 void
1351 usage (int status)
1353 if (status != EXIT_SUCCESS)
1354 emit_try_help ();
1355 else
1357 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1358 fputs (_("\
1359 Display file or file system status.\n\
1360 "), stdout);
1362 emit_mandatory_arg_note ();
1364 fputs (_("\
1365 -L, --dereference follow links\n\
1366 -f, --file-system display file system status instead of file status\n\
1367 "), stdout);
1368 fputs (_("\
1369 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1370 output a newline after each use of FORMAT\n\
1371 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1372 and do not output a mandatory trailing newline.\n\
1373 If you want a newline, include \\n in FORMAT\n\
1374 -t, --terse print the information in terse form\n\
1375 "), stdout);
1376 fputs (HELP_OPTION_DESCRIPTION, stdout);
1377 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1379 fputs (_("\n\
1380 The valid format sequences for files (without --file-system):\n\
1382 %a access rights in octal\n\
1383 %A access rights in human readable form\n\
1384 %b number of blocks allocated (see %B)\n\
1385 %B the size in bytes of each block reported by %b\n\
1386 %C SELinux security context string\n\
1387 "), stdout);
1388 fputs (_("\
1389 %d device number in decimal\n\
1390 %D device number in hex\n\
1391 %f raw mode in hex\n\
1392 %F file type\n\
1393 %g group ID of owner\n\
1394 %G group name of owner\n\
1395 "), stdout);
1396 fputs (_("\
1397 %h number of hard links\n\
1398 %i inode number\n\
1399 %m mount point\n\
1400 %n file name\n\
1401 %N quoted file name with dereference if symbolic link\n\
1402 %o optimal I/O transfer size hint\n\
1403 %s total size, in bytes\n\
1404 %t major device type in hex, for character/block device special files\n\
1405 %T minor device type in hex, for character/block device special files\n\
1406 "), stdout);
1407 fputs (_("\
1408 %u user ID of owner\n\
1409 %U user name of owner\n\
1410 %w time of file birth, human-readable; - if unknown\n\
1411 %W time of file birth, seconds since Epoch; 0 if unknown\n\
1412 %x time of last access, human-readable\n\
1413 %X time of last access, seconds since Epoch\n\
1414 %y time of last modification, human-readable\n\
1415 %Y time of last modification, seconds since Epoch\n\
1416 %z time of last change, human-readable\n\
1417 %Z time of last change, seconds since Epoch\n\
1419 "), stdout);
1421 fputs (_("\
1422 Valid format sequences for file systems:\n\
1424 %a free blocks available to non-superuser\n\
1425 %b total data blocks in file system\n\
1426 %c total file nodes in file system\n\
1427 %d free file nodes in file system\n\
1428 %f free blocks in file system\n\
1429 "), stdout);
1430 fputs (_("\
1431 %i file system ID in hex\n\
1432 %l maximum length of filenames\n\
1433 %n file name\n\
1434 %s block size (for faster transfers)\n\
1435 %S fundamental block size (for block counts)\n\
1436 %t file system type in hex\n\
1437 %T file system type in human readable form\n\
1438 "), stdout);
1439 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1440 emit_ancillary_info ();
1442 exit (status);
1446 main (int argc, char *argv[])
1448 int c;
1449 int i;
1450 bool fs = false;
1451 bool terse = false;
1452 char *format = NULL;
1453 char *format2;
1454 bool ok = true;
1456 initialize_main (&argc, &argv);
1457 set_program_name (argv[0]);
1458 setlocale (LC_ALL, "");
1459 bindtextdomain (PACKAGE, LOCALEDIR);
1460 textdomain (PACKAGE);
1462 struct lconv const *locale = localeconv ();
1463 decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1464 decimal_point_len = strlen (decimal_point);
1466 atexit (close_stdout);
1468 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1470 switch (c)
1472 case PRINTF_OPTION:
1473 format = optarg;
1474 interpret_backslash_escapes = true;
1475 trailing_delim = "";
1476 break;
1478 case 'c':
1479 format = optarg;
1480 interpret_backslash_escapes = false;
1481 trailing_delim = "\n";
1482 break;
1484 case 'L':
1485 follow_links = true;
1486 break;
1488 case 'f':
1489 fs = true;
1490 break;
1492 case 't':
1493 terse = true;
1494 break;
1496 case_GETOPT_HELP_CHAR;
1498 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1500 default:
1501 usage (EXIT_FAILURE);
1505 if (argc == optind)
1507 error (0, 0, _("missing operand"));
1508 usage (EXIT_FAILURE);
1511 if (format)
1512 format2 = format;
1513 else
1515 format = default_format (fs, terse, false);
1516 format2 = default_format (fs, terse, true);
1519 for (i = optind; i < argc; i++)
1520 ok &= (fs
1521 ? do_statfs (argv[i], format)
1522 : do_stat (argv[i], format, format2));
1524 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);