maint: update all copyright year number ranges
[coreutils.git] / src / stat.c
blob8614a4d11f6f5448b3b6ac89e6599547dced12e4
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2018 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 <https://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 "argmatch.h"
63 #include "die.h"
64 #include "error.h"
65 #include "file-type.h"
66 #include "filemode.h"
67 #include "fs.h"
68 #include "getopt.h"
69 #include "mountlist.h"
70 #include "quote.h"
71 #include "stat-size.h"
72 #include "stat-time.h"
73 #include "strftime.h"
74 #include "find-mount-point.h"
75 #include "xvasprintf.h"
77 #if USE_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 STRUCT_STATVFS struct statvfs64
85 # define STATFS statvfs64
86 # else
87 # define STRUCT_STATVFS struct statvfs
88 # define STATFS statvfs
89 # endif
90 # define STATFS_FRSIZE(S) ((S)->f_frsize)
91 #else
92 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
93 # if HAVE_STRUCT_STATFS_F_NAMELEN
94 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
95 # elif HAVE_STRUCT_STATFS_F_NAMEMAX
96 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
97 # endif
98 # define STATFS statfs
99 # if HAVE_OS_H /* BeOS */
100 /* BeOS has a statvfs function, but it does not return sensible values
101 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
102 f_fstypename. Use 'struct fs_info' instead. */
103 static int ATTRIBUTE_WARN_UNUSED_RESULT
104 statfs (char const *filename, struct fs_info *buf)
106 dev_t device = dev_for_path (filename);
107 if (device < 0)
109 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
110 : device == B_BAD_VALUE ? EINVAL
111 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
112 : device == B_NO_MEMORY ? ENOMEM
113 : device == B_FILE_ERROR ? EIO
114 : 0);
115 return -1;
117 /* If successful, buf->dev will be == device. */
118 return fs_stat_dev (device, buf);
120 # define f_fsid dev
121 # define f_blocks total_blocks
122 # define f_bfree free_blocks
123 # define f_bavail free_blocks
124 # define f_bsize io_size
125 # define f_files total_nodes
126 # define f_ffree free_nodes
127 # define STRUCT_STATVFS struct fs_info
128 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
129 # define STATFS_FRSIZE(S) ((S)->block_size)
130 # else
131 # define STRUCT_STATVFS struct statfs
132 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
133 # if HAVE_STRUCT_STATFS_F_FRSIZE
134 # define STATFS_FRSIZE(S) ((S)->f_frsize)
135 # else
136 # define STATFS_FRSIZE(S) 0
137 # endif
138 # endif
139 #endif
141 #ifdef SB_F_NAMEMAX
142 # define OUT_NAMEMAX out_uint
143 #else
144 /* Depending on whether statvfs or statfs is used,
145 neither f_namemax or f_namelen may be available. */
146 # define SB_F_NAMEMAX(S) "?"
147 # define OUT_NAMEMAX out_string
148 #endif
150 #if HAVE_STRUCT_STATVFS_F_BASETYPE
151 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
152 #else
153 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
154 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
155 # elif HAVE_OS_H /* BeOS */
156 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
157 # endif
158 #endif
160 #if HAVE_GETATTRAT
161 # include <attr.h>
162 # include <sys/nvpair.h>
163 #endif
165 /* FIXME: these are used by printf.c, too */
166 #define isodigit(c) ('0' <= (c) && (c) <= '7')
167 #define octtobin(c) ((c) - '0')
168 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
169 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
171 static char const digits[] = "0123456789";
173 /* Flags that are portable for use in printf, for at least one
174 conversion specifier; make_format removes unportable flags as
175 needed for particular specifiers. The glibc 2.2 extension "I" is
176 listed here; it is removed by make_format because it has undefined
177 behavior elsewhere and because it is incompatible with
178 out_epoch_sec. */
179 static char const printf_flags[] = "'-+ #0I";
181 /* Formats for the --terse option. */
182 static char const fmt_terse_fs[] = "%n %i %l %t %s %S %b %f %a %c %d\n";
183 static char const fmt_terse_regular[] = "%n %s %b %f %u %g %D %i %h %t %T"
184 " %X %Y %Z %W %o\n";
185 static char const fmt_terse_selinux[] = "%n %s %b %f %u %g %D %i %h %t %T"
186 " %X %Y %Z %W %o %C\n";
188 #define PROGRAM_NAME "stat"
190 #define AUTHORS proper_name ("Michael Meskes")
192 enum
194 PRINTF_OPTION = CHAR_MAX + 1
197 static struct option const long_options[] =
199 {"dereference", no_argument, NULL, 'L'},
200 {"file-system", no_argument, NULL, 'f'},
201 {"format", required_argument, NULL, 'c'},
202 {"printf", required_argument, NULL, PRINTF_OPTION},
203 {"terse", no_argument, NULL, 't'},
204 {GETOPT_HELP_OPTION_DECL},
205 {GETOPT_VERSION_OPTION_DECL},
206 {NULL, 0, NULL, 0}
209 /* Whether to follow symbolic links; True for --dereference (-L). */
210 static bool follow_links;
212 /* Whether to interpret backslash-escape sequences.
213 True for --printf=FMT, not for --format=FMT (-c). */
214 static bool interpret_backslash_escapes;
216 /* The trailing delimiter string:
217 "" for --printf=FMT, "\n" for --format=FMT (-c). */
218 static char const *trailing_delim = "";
220 /* The representation of the decimal point in the current locale. */
221 static char const *decimal_point;
222 static size_t decimal_point_len;
224 /* Return the type of the specified file system.
225 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
226 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
227 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
228 Still others have neither and have to get by with f_type (GNU/Linux).
229 But f_type may only exist in statfs (Cygwin). */
230 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
231 human_fstype (STRUCT_STATVFS const *statfsbuf)
233 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
234 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
235 #else
236 switch (statfsbuf->f_type)
238 # if defined __linux__
240 /* Compare with what's in libc:
241 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
242 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
243 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
244 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
245 | sort > sym_libc
246 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
247 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
248 | sort > sym_stat
249 diff -u sym_stat sym_libc
252 /* Also compare with the list in "man 2 statfs" using the
253 fs-magic-compare make target. */
255 /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
256 statements must be followed by a hexadecimal constant in
257 a comment. The S_MAGIC_... name and constant are automatically
258 combined to produce the #define directives in fs.h. */
260 case S_MAGIC_AAFS: /* 0x5A3C69F0 local */
261 return "aafs";
262 case S_MAGIC_ACFS: /* 0x61636673 remote */
263 return "acfs";
264 case S_MAGIC_ADFS: /* 0xADF5 local */
265 return "adfs";
266 case S_MAGIC_AFFS: /* 0xADFF local */
267 return "affs";
268 case S_MAGIC_AFS: /* 0x5346414F remote */
269 return "afs";
270 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 local */
271 return "anon-inode FS";
272 case S_MAGIC_AUFS: /* 0x61756673 remote */
273 /* FIXME: change syntax or add an optional attribute like "inotify:no".
274 The above is labeled as "remote" so that tail always uses polling,
275 but this isn't really a remote file system type. */
276 return "aufs";
277 case S_MAGIC_AUTOFS: /* 0x0187 local */
278 return "autofs";
279 case S_MAGIC_BALLOON_KVM: /* 0x13661366 local */
280 return "balloon-kvm-fs";
281 case S_MAGIC_BEFS: /* 0x42465331 local */
282 return "befs";
283 case S_MAGIC_BDEVFS: /* 0x62646576 local */
284 return "bdevfs";
285 case S_MAGIC_BFS: /* 0x1BADFACE local */
286 return "bfs";
287 case S_MAGIC_BPF_FS: /* 0xCAFE4A11 local */
288 return "bpf_fs";
289 case S_MAGIC_BINFMTFS: /* 0x42494E4D local */
290 return "binfmt_misc";
291 case S_MAGIC_BTRFS: /* 0x9123683E local */
292 return "btrfs";
293 case S_MAGIC_BTRFS_TEST: /* 0x73727279 local */
294 return "btrfs_test";
295 case S_MAGIC_CEPH: /* 0x00C36400 remote */
296 return "ceph";
297 case S_MAGIC_CGROUP: /* 0x0027E0EB local */
298 return "cgroupfs";
299 case S_MAGIC_CGROUP2: /* 0x63677270 local */
300 return "cgroup2fs";
301 case S_MAGIC_CIFS: /* 0xFF534D42 remote */
302 return "cifs";
303 case S_MAGIC_CODA: /* 0x73757245 remote */
304 return "coda";
305 case S_MAGIC_COH: /* 0x012FF7B7 local */
306 return "coh";
307 case S_MAGIC_CONFIGFS: /* 0x62656570 local */
308 return "configfs";
309 case S_MAGIC_CRAMFS: /* 0x28CD3D45 local */
310 return "cramfs";
311 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 local */
312 return "cramfs-wend";
313 case S_MAGIC_DAXFS: /* 0x64646178 local */
314 return "daxfs";
315 case S_MAGIC_DEBUGFS: /* 0x64626720 local */
316 return "debugfs";
317 case S_MAGIC_DEVFS: /* 0x1373 local */
318 return "devfs";
319 case S_MAGIC_DEVPTS: /* 0x1CD1 local */
320 return "devpts";
321 case S_MAGIC_ECRYPTFS: /* 0xF15F local */
322 return "ecryptfs";
323 case S_MAGIC_EFIVARFS: /* 0xDE5E81E4 local */
324 return "efivarfs";
325 case S_MAGIC_EFS: /* 0x00414A53 local */
326 return "efs";
327 case S_MAGIC_EXOFS: /* 0x5DF5 local */
328 return "exofs";
329 case S_MAGIC_EXT: /* 0x137D local */
330 return "ext";
331 case S_MAGIC_EXT2: /* 0xEF53 local */
332 return "ext2/ext3";
333 case S_MAGIC_EXT2_OLD: /* 0xEF51 local */
334 return "ext2";
335 case S_MAGIC_F2FS: /* 0xF2F52010 local */
336 return "f2fs";
337 case S_MAGIC_FAT: /* 0x4006 local */
338 return "fat";
339 case S_MAGIC_FHGFS: /* 0x19830326 remote */
340 return "fhgfs";
341 case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
342 return "fuseblk";
343 case S_MAGIC_FUSECTL: /* 0x65735543 remote */
344 return "fusectl";
345 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA local */
346 return "futexfs";
347 case S_MAGIC_GFS: /* 0x01161970 remote */
348 return "gfs/gfs2";
349 case S_MAGIC_GPFS: /* 0x47504653 remote */
350 return "gpfs";
351 case S_MAGIC_HFS: /* 0x4244 local */
352 return "hfs";
353 case S_MAGIC_HFS_PLUS: /* 0x482B local */
354 return "hfs+";
355 case S_MAGIC_HFS_X: /* 0x4858 local */
356 return "hfsx";
357 case S_MAGIC_HOSTFS: /* 0x00C0FFEE local */
358 return "hostfs";
359 case S_MAGIC_HPFS: /* 0xF995E849 local */
360 return "hpfs";
361 case S_MAGIC_HUGETLBFS: /* 0x958458F6 local */
362 return "hugetlbfs";
363 case S_MAGIC_MTD_INODE_FS: /* 0x11307854 local */
364 return "inodefs";
365 case S_MAGIC_IBRIX: /* 0x013111A8 remote */
366 return "ibrix";
367 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA local */
368 return "inotifyfs";
369 case S_MAGIC_ISOFS: /* 0x9660 local */
370 return "isofs";
371 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 local */
372 return "isofs";
373 case S_MAGIC_ISOFS_WIN: /* 0x4000 local */
374 return "isofs";
375 case S_MAGIC_JFFS: /* 0x07C0 local */
376 return "jffs";
377 case S_MAGIC_JFFS2: /* 0x72B6 local */
378 return "jffs2";
379 case S_MAGIC_JFS: /* 0x3153464A local */
380 return "jfs";
381 case S_MAGIC_KAFS: /* 0x6B414653 remote */
382 return "k-afs";
383 case S_MAGIC_LOGFS: /* 0xC97E8168 local */
384 return "logfs";
385 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
386 return "lustre";
387 case S_MAGIC_M1FS: /* 0x5346314D local */
388 return "m1fs";
389 case S_MAGIC_MINIX: /* 0x137F local */
390 return "minix";
391 case S_MAGIC_MINIX_30: /* 0x138F local */
392 return "minix (30 char.)";
393 case S_MAGIC_MINIX_V2: /* 0x2468 local */
394 return "minix v2";
395 case S_MAGIC_MINIX_V2_30: /* 0x2478 local */
396 return "minix v2 (30 char.)";
397 case S_MAGIC_MINIX_V3: /* 0x4D5A local */
398 return "minix3";
399 case S_MAGIC_MQUEUE: /* 0x19800202 local */
400 return "mqueue";
401 case S_MAGIC_MSDOS: /* 0x4D44 local */
402 return "msdos";
403 case S_MAGIC_NCP: /* 0x564C remote */
404 return "novell";
405 case S_MAGIC_NFS: /* 0x6969 remote */
406 return "nfs";
407 case S_MAGIC_NFSD: /* 0x6E667364 remote */
408 return "nfsd";
409 case S_MAGIC_NILFS: /* 0x3434 local */
410 return "nilfs";
411 case S_MAGIC_NSFS: /* 0x6E736673 local */
412 return "nsfs";
413 case S_MAGIC_NTFS: /* 0x5346544E local */
414 return "ntfs";
415 case S_MAGIC_OPENPROM: /* 0x9FA1 local */
416 return "openprom";
417 case S_MAGIC_OCFS2: /* 0x7461636F remote */
418 return "ocfs2";
419 case S_MAGIC_OVERLAYFS: /* 0x794C7630 remote */
420 /* This may overlay remote file systems.
421 Also there have been issues reported with inotify and overlayfs,
422 so mark as "remote" so that polling is used. */
423 return "overlayfs";
424 case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
425 return "panfs";
426 case S_MAGIC_PIPEFS: /* 0x50495045 remote */
427 /* FIXME: change syntax or add an optional attribute like "inotify:no".
428 pipefs and prlfs are labeled as "remote" so that tail always polls,
429 but these aren't really remote file system types. */
430 return "pipefs";
431 case S_MAGIC_PRL_FS: /* 0x7C7C6673 remote */
432 return "prl_fs";
433 case S_MAGIC_PROC: /* 0x9FA0 local */
434 return "proc";
435 case S_MAGIC_PSTOREFS: /* 0x6165676C local */
436 return "pstorefs";
437 case S_MAGIC_QNX4: /* 0x002F local */
438 return "qnx4";
439 case S_MAGIC_QNX6: /* 0x68191122 local */
440 return "qnx6";
441 case S_MAGIC_RAMFS: /* 0x858458F6 local */
442 return "ramfs";
443 case S_MAGIC_RDTGROUP: /* 0x07655821 local */
444 return "rdt";
445 case S_MAGIC_REISERFS: /* 0x52654973 local */
446 return "reiserfs";
447 case S_MAGIC_ROMFS: /* 0x7275 local */
448 return "romfs";
449 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 local */
450 return "rpc_pipefs";
451 case S_MAGIC_SECURITYFS: /* 0x73636673 local */
452 return "securityfs";
453 case S_MAGIC_SELINUX: /* 0xF97CFF8C local */
454 return "selinux";
455 case S_MAGIC_SMACK: /* 0x43415D53 local */
456 return "smackfs";
457 case S_MAGIC_SMB: /* 0x517B remote */
458 return "smb";
459 case S_MAGIC_SMB2: /* 0xFE534D42 remote */
460 return "smb2";
461 case S_MAGIC_SNFS: /* 0xBEEFDEAD remote */
462 return "snfs";
463 case S_MAGIC_SOCKFS: /* 0x534F434B local */
464 return "sockfs";
465 case S_MAGIC_SQUASHFS: /* 0x73717368 local */
466 return "squashfs";
467 case S_MAGIC_SYSFS: /* 0x62656572 local */
468 return "sysfs";
469 case S_MAGIC_SYSV2: /* 0x012FF7B6 local */
470 return "sysv2";
471 case S_MAGIC_SYSV4: /* 0x012FF7B5 local */
472 return "sysv4";
473 case S_MAGIC_TMPFS: /* 0x01021994 local */
474 return "tmpfs";
475 case S_MAGIC_TRACEFS: /* 0x74726163 local */
476 return "tracefs";
477 case S_MAGIC_UBIFS: /* 0x24051905 local */
478 return "ubifs";
479 case S_MAGIC_UDF: /* 0x15013346 local */
480 return "udf";
481 case S_MAGIC_UFS: /* 0x00011954 local */
482 return "ufs";
483 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 local */
484 return "ufs";
485 case S_MAGIC_USBDEVFS: /* 0x9FA2 local */
486 return "usbdevfs";
487 case S_MAGIC_V9FS: /* 0x01021997 local */
488 return "v9fs";
489 case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
490 return "vmhgfs";
491 case S_MAGIC_VXFS: /* 0xA501FCF5 remote */
492 /* Veritas File System can run in single instance or clustered mode,
493 so mark as remote to cater for the latter case. */
494 return "vxfs";
495 case S_MAGIC_VZFS: /* 0x565A4653 local */
496 return "vzfs";
497 case S_MAGIC_WSLFS: /* 0x53464846 local */
498 return "wslfs";
499 case S_MAGIC_XENFS: /* 0xABBA1974 local */
500 return "xenfs";
501 case S_MAGIC_XENIX: /* 0x012FF7B4 local */
502 return "xenix";
503 case S_MAGIC_XFS: /* 0x58465342 local */
504 return "xfs";
505 case S_MAGIC_XIAFS: /* 0x012FD16D local */
506 return "xia";
507 case S_MAGIC_ZFS: /* 0x2FC12FC1 local */
508 return "zfs";
509 case S_MAGIC_ZSMALLOC: /* 0x58295829 local */
510 return "zsmallocfs";
513 # elif __GNU__
514 case FSTYPE_UFS:
515 return "ufs";
516 case FSTYPE_NFS:
517 return "nfs";
518 case FSTYPE_GFS:
519 return "gfs";
520 case FSTYPE_LFS:
521 return "lfs";
522 case FSTYPE_SYSV:
523 return "sysv";
524 case FSTYPE_FTP:
525 return "ftp";
526 case FSTYPE_TAR:
527 return "tar";
528 case FSTYPE_AR:
529 return "ar";
530 case FSTYPE_CPIO:
531 return "cpio";
532 case FSTYPE_MSLOSS:
533 return "msloss";
534 case FSTYPE_CPM:
535 return "cpm";
536 case FSTYPE_HFS:
537 return "hfs";
538 case FSTYPE_DTFS:
539 return "dtfs";
540 case FSTYPE_GRFS:
541 return "grfs";
542 case FSTYPE_TERM:
543 return "term";
544 case FSTYPE_DEV:
545 return "dev";
546 case FSTYPE_PROC:
547 return "proc";
548 case FSTYPE_IFSOCK:
549 return "ifsock";
550 case FSTYPE_AFS:
551 return "afs";
552 case FSTYPE_DFS:
553 return "dfs";
554 case FSTYPE_PROC9:
555 return "proc9";
556 case FSTYPE_SOCKET:
557 return "socket";
558 case FSTYPE_MISC:
559 return "misc";
560 case FSTYPE_EXT2FS:
561 return "ext2/ext3";
562 case FSTYPE_HTTP:
563 return "http";
564 case FSTYPE_MEMFS:
565 return "memfs";
566 case FSTYPE_ISO9660:
567 return "iso9660";
568 # endif
569 default:
571 unsigned long int type = statfsbuf->f_type;
572 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
573 + (sizeof type * CHAR_BIT + 3) / 4];
574 sprintf (buf, "UNKNOWN (0x%lx)", type);
575 return buf;
578 #endif
581 static char * ATTRIBUTE_WARN_UNUSED_RESULT
582 human_access (struct stat const *statbuf)
584 static char modebuf[12];
585 filemodestring (statbuf, modebuf);
586 modebuf[10] = 0;
587 return modebuf;
590 static char * ATTRIBUTE_WARN_UNUSED_RESULT
591 human_time (struct timespec t)
593 /* STR must be at least this big, either because localtime_rz fails,
594 or because the time zone is truly outlandish so that %z expands
595 to a long string. */
596 enum { intmax_bufsize = INT_BUFSIZE_BOUND (intmax_t) };
598 static char str[intmax_bufsize
599 + INT_STRLEN_BOUND (int) /* YYYY */
600 + 1 /* because YYYY might equal INT_MAX + 1900 */
601 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +"];
602 static timezone_t tz;
603 if (!tz)
604 tz = tzalloc (getenv ("TZ"));
605 struct tm tm;
606 int ns = t.tv_nsec;
607 if (localtime_rz (tz, &t.tv_sec, &tm))
608 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", &tm, tz, ns);
609 else
611 char secbuf[INT_BUFSIZE_BOUND (intmax_t)];
612 sprintf (str, "%s.%09d", timetostr (t.tv_sec, secbuf), ns);
614 return str;
617 /* PFORMAT points to a '%' followed by a prefix of a format, all of
618 size PREFIX_LEN. The flags allowed for this format are
619 ALLOWED_FLAGS; remove other printf flags from the prefix, then
620 append SUFFIX. */
621 static void
622 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
623 char const *suffix)
625 char *dst = pformat + 1;
626 char const *src;
627 char const *srclim = pformat + prefix_len;
628 for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
629 if (strchr (allowed_flags, *src))
630 *dst++ = *src;
631 while (src < srclim)
632 *dst++ = *src++;
633 strcpy (dst, suffix);
636 static void
637 out_string (char *pformat, size_t prefix_len, char const *arg)
639 make_format (pformat, prefix_len, "-", "s");
640 printf (pformat, arg);
642 static int
643 out_int (char *pformat, size_t prefix_len, intmax_t arg)
645 make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
646 return printf (pformat, arg);
648 static int
649 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
651 make_format (pformat, prefix_len, "'-0", PRIuMAX);
652 return printf (pformat, arg);
654 static void
655 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
657 make_format (pformat, prefix_len, "-#0", PRIoMAX);
658 printf (pformat, arg);
660 static void
661 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
663 make_format (pformat, prefix_len, "-#0", PRIxMAX);
664 printf (pformat, arg);
666 static int
667 out_minus_zero (char *pformat, size_t prefix_len)
669 make_format (pformat, prefix_len, "'-+ 0", ".0f");
670 return printf (pformat, -0.25);
673 /* Output the number of seconds since the Epoch, using a format that
674 acts like printf's %f format. */
675 static void
676 out_epoch_sec (char *pformat, size_t prefix_len,
677 struct stat const *statbuf _GL_UNUSED,
678 struct timespec arg)
680 char *dot = memchr (pformat, '.', prefix_len);
681 size_t sec_prefix_len = prefix_len;
682 int width = 0;
683 int precision = 0;
684 bool frac_left_adjust = false;
686 if (dot)
688 sec_prefix_len = dot - pformat;
689 pformat[prefix_len] = '\0';
691 if (ISDIGIT (dot[1]))
693 long int lprec = strtol (dot + 1, NULL, 10);
694 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
696 else
698 precision = 9;
701 if (precision && ISDIGIT (dot[-1]))
703 /* If a nontrivial width is given, subtract the width of the
704 decimal point and PRECISION digits that will be output
705 later. */
706 char *p = dot;
707 *dot = '\0';
710 --p;
711 while (ISDIGIT (p[-1]));
713 long int lwidth = strtol (p, NULL, 10);
714 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
715 if (1 < width)
717 p += (*p == '0');
718 sec_prefix_len = p - pformat;
719 int w_d = (decimal_point_len < width
720 ? width - decimal_point_len
721 : 0);
722 if (1 < w_d)
724 int w = w_d - precision;
725 if (1 < w)
727 char *dst = pformat;
728 for (char const *src = dst; src < p; src++)
730 if (*src == '-')
731 frac_left_adjust = true;
732 else
733 *dst++ = *src;
735 sec_prefix_len =
736 (dst - pformat
737 + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
744 int divisor = 1;
745 for (int i = precision; i < 9; i++)
746 divisor *= 10;
747 int frac_sec = arg.tv_nsec / divisor;
748 int int_len;
750 if (TYPE_SIGNED (time_t))
752 bool minus_zero = false;
753 if (arg.tv_sec < 0 && arg.tv_nsec != 0)
755 int frac_sec_modulus = 1000000000 / divisor;
756 frac_sec = (frac_sec_modulus - frac_sec
757 - (arg.tv_nsec % divisor != 0));
758 arg.tv_sec += (frac_sec != 0);
759 minus_zero = (arg.tv_sec == 0);
761 int_len = (minus_zero
762 ? out_minus_zero (pformat, sec_prefix_len)
763 : out_int (pformat, sec_prefix_len, arg.tv_sec));
765 else
766 int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
768 if (precision)
770 int prec = (precision < 9 ? precision : 9);
771 int trailing_prec = precision - prec;
772 int ilen = (int_len < 0 ? 0 : int_len);
773 int trailing_width = (ilen < width && decimal_point_len < width - ilen
774 ? width - ilen - decimal_point_len - prec
775 : 0);
776 printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
777 trailing_width, trailing_prec, 0);
781 /* Print the context information of FILENAME, and return true iff the
782 context could not be obtained. */
783 static bool ATTRIBUTE_WARN_UNUSED_RESULT
784 out_file_context (char *pformat, size_t prefix_len, char const *filename)
786 char *scontext;
787 bool fail = false;
789 if ((follow_links
790 ? getfilecon (filename, &scontext)
791 : lgetfilecon (filename, &scontext)) < 0)
793 error (0, errno, _("failed to get security context of %s"),
794 quoteaf (filename));
795 scontext = NULL;
796 fail = true;
798 strcpy (pformat + prefix_len, "s");
799 printf (pformat, (scontext ? scontext : "?"));
800 if (scontext)
801 freecon (scontext);
802 return fail;
805 /* Print statfs info. Return zero upon success, nonzero upon failure. */
806 static bool ATTRIBUTE_WARN_UNUSED_RESULT
807 print_statfs (char *pformat, size_t prefix_len, unsigned int m,
808 int fd, char const *filename,
809 void const *data)
811 STRUCT_STATVFS const *statfsbuf = data;
812 bool fail = false;
814 switch (m)
816 case 'n':
817 out_string (pformat, prefix_len, filename);
818 break;
820 case 'i':
822 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
823 uintmax_t fsid = statfsbuf->f_fsid;
824 #else
825 typedef unsigned int fsid_word;
826 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
827 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
828 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
829 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
831 /* Assume a little-endian word order, as that is compatible
832 with glibc's statvfs implementation. */
833 uintmax_t fsid = 0;
834 int words = sizeof statfsbuf->f_fsid / sizeof *p;
835 for (int i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
837 uintmax_t u = p[words - 1 - i];
838 fsid |= u << (i * CHAR_BIT * sizeof *p);
840 #endif
841 out_uint_x (pformat, prefix_len, fsid);
843 break;
845 case 'l':
846 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
847 break;
848 case 't':
849 #if HAVE_STRUCT_STATXFS_F_TYPE
850 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
851 #else
852 fputc ('?', stdout);
853 #endif
854 break;
855 case 'T':
856 out_string (pformat, prefix_len, human_fstype (statfsbuf));
857 break;
858 case 'b':
859 out_int (pformat, prefix_len, statfsbuf->f_blocks);
860 break;
861 case 'f':
862 out_int (pformat, prefix_len, statfsbuf->f_bfree);
863 break;
864 case 'a':
865 out_int (pformat, prefix_len, statfsbuf->f_bavail);
866 break;
867 case 's':
868 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
869 break;
870 case 'S':
872 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
873 if (! frsize)
874 frsize = statfsbuf->f_bsize;
875 out_uint (pformat, prefix_len, frsize);
877 break;
878 case 'c':
879 out_uint (pformat, prefix_len, statfsbuf->f_files);
880 break;
881 case 'd':
882 out_int (pformat, prefix_len, statfsbuf->f_ffree);
883 break;
884 default:
885 fputc ('?', stdout);
886 break;
888 return fail;
891 /* Return any bind mounted source for a path.
892 The caller should not free the returned buffer.
893 Return NULL if no bind mount found. */
894 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
895 find_bind_mount (char const * name)
897 char const * bind_mount = NULL;
899 static struct mount_entry *mount_list;
900 static bool tried_mount_list = false;
901 if (!tried_mount_list) /* attempt/warn once per process. */
903 if (!(mount_list = read_file_system_list (false)))
904 error (0, errno, "%s", _("cannot read table of mounted file systems"));
905 tried_mount_list = true;
908 struct stat name_stats;
909 if (stat (name, &name_stats) != 0)
910 return NULL;
912 struct mount_entry *me;
913 for (me = mount_list; me; me = me->me_next)
915 if (me->me_dummy && me->me_devname[0] == '/'
916 && STREQ (me->me_mountdir, name))
918 struct stat dev_stats;
920 if (stat (me->me_devname, &dev_stats) == 0
921 && SAME_INODE (name_stats, dev_stats))
923 bind_mount = me->me_devname;
924 break;
929 return bind_mount;
932 /* Print mount point. Return zero upon success, nonzero upon failure. */
933 static bool ATTRIBUTE_WARN_UNUSED_RESULT
934 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
935 const struct stat *statp)
938 char const *np = "?", *bp = NULL;
939 char *mp = NULL;
940 bool fail = true;
942 /* Look for bind mounts first. Note we output the immediate alias,
943 rather than further resolving to a base device mount point. */
944 if (follow_links || !S_ISLNK (statp->st_mode))
946 char *resolved = canonicalize_file_name (filename);
947 if (!resolved)
949 error (0, errno, _("failed to canonicalize %s"), quoteaf (filename));
950 goto print_mount_point;
952 bp = find_bind_mount (resolved);
953 free (resolved);
954 if (bp)
956 fail = false;
957 goto print_mount_point;
961 /* If there is no direct bind mount, then navigate
962 back up the tree looking for a device change.
963 Note we don't detect if any of the directory components
964 are bind mounted to the same device, but that's OK
965 since we've not directly queried them. */
966 if ((mp = find_mount_point (filename, statp)))
968 /* This dir might be bind mounted to another device,
969 so we resolve the bound source in that case also. */
970 bp = find_bind_mount (mp);
971 fail = false;
974 print_mount_point:
976 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
977 free (mp);
978 return fail;
981 static struct timespec
982 get_birthtime (int fd, char const *filename, struct stat const *st)
984 struct timespec ts = get_stat_birthtime (st);
986 #if HAVE_GETATTRAT
987 if (ts.tv_nsec < 0)
989 nvlist_t *response;
990 if ((fd < 0
991 ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
992 : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
993 == 0)
995 uint64_t *val;
996 uint_t n;
997 if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
998 && 2 <= n
999 && val[0] <= TYPE_MAXIMUM (time_t)
1000 && val[1] < 1000000000 * 2 /* for leap seconds */)
1002 ts.tv_sec = val[0];
1003 ts.tv_nsec = val[1];
1005 nvlist_free (response);
1008 #endif
1010 return ts;
1013 /* Map a TS with negative TS.tv_nsec to {0,0}. */
1014 static inline struct timespec
1015 neg_to_zero (struct timespec ts)
1017 if (0 <= ts.tv_nsec)
1018 return ts;
1019 struct timespec z = {0, 0};
1020 return z;
1023 /* Set the quoting style default if the environment variable
1024 QUOTING_STYLE is set. */
1026 static void
1027 getenv_quoting_style (void)
1029 char const *q_style = getenv ("QUOTING_STYLE");
1030 if (q_style)
1032 int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
1033 if (0 <= i)
1034 set_quoting_style (NULL, quoting_style_vals[i]);
1035 else
1037 set_quoting_style (NULL, shell_escape_always_quoting_style);
1038 error (0, 0, _("ignoring invalid value of environment "
1039 "variable QUOTING_STYLE: %s"), quote (q_style));
1042 else
1043 set_quoting_style (NULL, shell_escape_always_quoting_style);
1046 /* Equivalent to quotearg(), but explicit to avoid syntax checks. */
1047 #define quoteN(x) quotearg_style (get_quoting_style (NULL), x)
1049 /* Print stat info. Return zero upon success, nonzero upon failure. */
1050 static bool
1051 print_stat (char *pformat, size_t prefix_len, unsigned int m,
1052 int fd, char const *filename, void const *data)
1054 struct stat *statbuf = (struct stat *) data;
1055 struct passwd *pw_ent;
1056 struct group *gw_ent;
1057 bool fail = false;
1059 switch (m)
1061 case 'n':
1062 out_string (pformat, prefix_len, filename);
1063 break;
1064 case 'N':
1065 out_string (pformat, prefix_len, quoteN (filename));
1066 if (S_ISLNK (statbuf->st_mode))
1068 char *linkname = areadlink_with_size (filename, statbuf->st_size);
1069 if (linkname == NULL)
1071 error (0, errno, _("cannot read symbolic link %s"),
1072 quoteaf (filename));
1073 return true;
1075 printf (" -> ");
1076 out_string (pformat, prefix_len, quoteN (linkname));
1077 free (linkname);
1079 break;
1080 case 'd':
1081 out_uint (pformat, prefix_len, statbuf->st_dev);
1082 break;
1083 case 'D':
1084 out_uint_x (pformat, prefix_len, statbuf->st_dev);
1085 break;
1086 case 'i':
1087 out_uint (pformat, prefix_len, statbuf->st_ino);
1088 break;
1089 case 'a':
1090 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
1091 break;
1092 case 'A':
1093 out_string (pformat, prefix_len, human_access (statbuf));
1094 break;
1095 case 'f':
1096 out_uint_x (pformat, prefix_len, statbuf->st_mode);
1097 break;
1098 case 'F':
1099 out_string (pformat, prefix_len, file_type (statbuf));
1100 break;
1101 case 'h':
1102 out_uint (pformat, prefix_len, statbuf->st_nlink);
1103 break;
1104 case 'u':
1105 out_uint (pformat, prefix_len, statbuf->st_uid);
1106 break;
1107 case 'U':
1108 pw_ent = getpwuid (statbuf->st_uid);
1109 out_string (pformat, prefix_len,
1110 pw_ent ? pw_ent->pw_name : "UNKNOWN");
1111 break;
1112 case 'g':
1113 out_uint (pformat, prefix_len, statbuf->st_gid);
1114 break;
1115 case 'G':
1116 gw_ent = getgrgid (statbuf->st_gid);
1117 out_string (pformat, prefix_len,
1118 gw_ent ? gw_ent->gr_name : "UNKNOWN");
1119 break;
1120 case 't':
1121 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
1122 break;
1123 case 'm':
1124 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
1125 break;
1126 case 'T':
1127 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
1128 break;
1129 case 's':
1130 out_int (pformat, prefix_len, statbuf->st_size);
1131 break;
1132 case 'B':
1133 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
1134 break;
1135 case 'b':
1136 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
1137 break;
1138 case 'o':
1139 out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
1140 break;
1141 case 'w':
1143 struct timespec t = get_birthtime (fd, filename, statbuf);
1144 if (t.tv_nsec < 0)
1145 out_string (pformat, prefix_len, "-");
1146 else
1147 out_string (pformat, prefix_len, human_time (t));
1149 break;
1150 case 'W':
1151 out_epoch_sec (pformat, prefix_len, statbuf,
1152 neg_to_zero (get_birthtime (fd, filename, statbuf)));
1153 break;
1154 case 'x':
1155 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
1156 break;
1157 case 'X':
1158 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
1159 break;
1160 case 'y':
1161 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
1162 break;
1163 case 'Y':
1164 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
1165 break;
1166 case 'z':
1167 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
1168 break;
1169 case 'Z':
1170 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
1171 break;
1172 case 'C':
1173 fail |= out_file_context (pformat, prefix_len, filename);
1174 break;
1175 default:
1176 fputc ('?', stdout);
1177 break;
1179 return fail;
1182 /* Output a single-character \ escape. */
1184 static void
1185 print_esc_char (char c)
1187 switch (c)
1189 case 'a': /* Alert. */
1190 c ='\a';
1191 break;
1192 case 'b': /* Backspace. */
1193 c ='\b';
1194 break;
1195 case 'e': /* Escape. */
1196 c ='\x1B';
1197 break;
1198 case 'f': /* Form feed. */
1199 c ='\f';
1200 break;
1201 case 'n': /* New line. */
1202 c ='\n';
1203 break;
1204 case 'r': /* Carriage return. */
1205 c ='\r';
1206 break;
1207 case 't': /* Horizontal tab. */
1208 c ='\t';
1209 break;
1210 case 'v': /* Vertical tab. */
1211 c ='\v';
1212 break;
1213 case '"':
1214 case '\\':
1215 break;
1216 default:
1217 error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
1218 break;
1220 putchar (c);
1223 /* Print the information specified by the format string, FORMAT,
1224 calling PRINT_FUNC for each %-directive encountered.
1225 Return zero upon success, nonzero upon failure. */
1226 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1227 print_it (char const *format, int fd, char const *filename,
1228 bool (*print_func) (char *, size_t, unsigned int,
1229 int, char const *, void const *),
1230 void const *data)
1232 bool fail = false;
1234 /* Add 2 to accommodate our conversion of the stat '%s' format string
1235 to the longer printf '%llu' one. */
1236 enum
1238 MAX_ADDITIONAL_BYTES =
1239 (MAX (sizeof PRIdMAX,
1240 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
1241 - 1)
1243 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1244 char *dest = xmalloc (n_alloc);
1245 char const *b;
1246 for (b = format; *b; b++)
1248 switch (*b)
1250 case '%':
1252 size_t len = strspn (b + 1, printf_flags);
1253 char const *fmt_char = b + len + 1;
1254 fmt_char += strspn (fmt_char, digits);
1255 if (*fmt_char == '.')
1256 fmt_char += 1 + strspn (fmt_char + 1, digits);
1257 len = fmt_char - (b + 1);
1258 unsigned int fmt_code = *fmt_char;
1259 memcpy (dest, b, len + 1);
1261 b = fmt_char;
1262 switch (fmt_code)
1264 case '\0':
1265 --b;
1266 FALLTHROUGH;
1267 case '%':
1268 if (0 < len)
1270 dest[len + 1] = *fmt_char;
1271 dest[len + 2] = '\0';
1272 die (EXIT_FAILURE, 0, _("%s: invalid directive"),
1273 quote (dest));
1275 putchar ('%');
1276 break;
1277 default:
1278 fail |= print_func (dest, len + 1, fmt_code,
1279 fd, filename, data);
1280 break;
1282 break;
1285 case '\\':
1286 if ( ! interpret_backslash_escapes)
1288 putchar ('\\');
1289 break;
1291 ++b;
1292 if (isodigit (*b))
1294 int esc_value = octtobin (*b);
1295 int esc_length = 1; /* number of octal digits */
1296 for (++b; esc_length < 3 && isodigit (*b);
1297 ++esc_length, ++b)
1299 esc_value = esc_value * 8 + octtobin (*b);
1301 putchar (esc_value);
1302 --b;
1304 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
1306 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
1307 /* A hexadecimal \xhh escape sequence must have
1308 1 or 2 hex. digits. */
1309 ++b;
1310 if (isxdigit (to_uchar (b[1])))
1312 ++b;
1313 esc_value = esc_value * 16 + hextobin (*b);
1315 putchar (esc_value);
1317 else if (*b == '\0')
1319 error (0, 0, _("warning: backslash at end of format"));
1320 putchar ('\\');
1321 /* Arrange to exit the loop. */
1322 --b;
1324 else
1326 print_esc_char (*b);
1328 break;
1330 default:
1331 putchar (*b);
1332 break;
1335 free (dest);
1337 fputs (trailing_delim, stdout);
1339 return fail;
1342 /* Stat the file system and print what we find. */
1343 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1344 do_statfs (char const *filename, char const *format)
1346 STRUCT_STATVFS statfsbuf;
1348 if (STREQ (filename, "-"))
1350 error (0, 0, _("using %s to denote standard input does not work"
1351 " in file system mode"), quoteaf (filename));
1352 return false;
1355 if (STATFS (filename, &statfsbuf) != 0)
1357 error (0, errno, _("cannot read file system information for %s"),
1358 quoteaf (filename));
1359 return false;
1362 bool fail = print_it (format, -1, filename, print_statfs, &statfsbuf);
1363 return ! fail;
1366 /* stat the file and print what we find */
1367 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1368 do_stat (char const *filename, char const *format,
1369 char const *format2)
1371 int fd = STREQ (filename, "-") ? 0 : -1;
1372 struct stat statbuf;
1374 if (0 <= fd)
1376 if (fstat (fd, &statbuf) != 0)
1378 error (0, errno, _("cannot stat standard input"));
1379 return false;
1382 /* We can't use the shorter
1383 (follow_links?stat:lstat) (filename, &statbug)
1384 since stat might be a function-like macro. */
1385 else if ((follow_links
1386 ? stat (filename, &statbuf)
1387 : lstat (filename, &statbuf)) != 0)
1389 error (0, errno, _("cannot stat %s"), quoteaf (filename));
1390 return false;
1393 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1394 format = format2;
1396 bool fail = print_it (format, fd, filename, print_stat, &statbuf);
1397 return ! fail;
1400 /* Return an allocated format string in static storage that
1401 corresponds to whether FS and TERSE options were declared. */
1402 static char *
1403 default_format (bool fs, bool terse, bool device)
1405 char *format;
1406 if (fs)
1408 if (terse)
1409 format = xstrdup (fmt_terse_fs);
1410 else
1412 /* TRANSLATORS: This string uses format specifiers from
1413 'stat --help' with --file-system, and NOT from printf. */
1414 format = xstrdup (_(" File: \"%n\"\n"
1415 " ID: %-8i Namelen: %-7l Type: %T\n"
1416 "Block size: %-10s Fundamental block size: %S\n"
1417 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1418 "Inodes: Total: %-10c Free: %d\n"));
1421 else /* ! fs */
1423 if (terse)
1425 if (0 < is_selinux_enabled ())
1426 format = xstrdup (fmt_terse_selinux);
1427 else
1428 format = xstrdup (fmt_terse_regular);
1430 else
1432 char *temp;
1433 /* TRANSLATORS: This string uses format specifiers from
1434 'stat --help' without --file-system, and NOT from printf. */
1435 format = xstrdup (_("\
1436 File: %N\n\
1437 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1438 "));
1440 temp = format;
1441 if (device)
1443 /* TRANSLATORS: This string uses format specifiers from
1444 'stat --help' without --file-system, and NOT from printf. */
1445 format = xasprintf ("%s%s", format, _("\
1446 " "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1447 "));
1449 else
1451 /* TRANSLATORS: This string uses format specifiers from
1452 'stat --help' without --file-system, and NOT from printf. */
1453 format = xasprintf ("%s%s", format, _("\
1454 " "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1455 "));
1457 free (temp);
1459 temp = format;
1460 /* TRANSLATORS: This string uses format specifiers from
1461 'stat --help' without --file-system, and NOT from printf. */
1462 format = xasprintf ("%s%s", format, _("\
1463 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1464 "));
1465 free (temp);
1467 if (0 < is_selinux_enabled ())
1469 temp = format;
1470 /* TRANSLATORS: This string uses format specifiers from
1471 'stat --help' without --file-system, and NOT from printf. */
1472 format = xasprintf ("%s%s", format, _("Context: %C\n"));
1473 free (temp);
1476 temp = format;
1477 /* TRANSLATORS: This string uses format specifiers from
1478 'stat --help' without --file-system, and NOT from printf. */
1479 format = xasprintf ("%s%s", format,
1480 _("Access: %x\n"
1481 "Modify: %y\n"
1482 "Change: %z\n"
1483 " Birth: %w\n"));
1484 free (temp);
1487 return format;
1490 void
1491 usage (int status)
1493 if (status != EXIT_SUCCESS)
1494 emit_try_help ();
1495 else
1497 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1498 fputs (_("\
1499 Display file or file system status.\n\
1500 "), stdout);
1502 emit_mandatory_arg_note ();
1504 fputs (_("\
1505 -L, --dereference follow links\n\
1506 -f, --file-system display file system status instead of file status\n\
1507 "), stdout);
1508 fputs (_("\
1509 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1510 output a newline after each use of FORMAT\n\
1511 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1512 and do not output a mandatory trailing newline;\n\
1513 if you want a newline, include \\n in FORMAT\n\
1514 -t, --terse print the information in terse form\n\
1515 "), stdout);
1516 fputs (HELP_OPTION_DESCRIPTION, stdout);
1517 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1519 fputs (_("\n\
1520 The valid format sequences for files (without --file-system):\n\
1522 %a access rights in octal (note '#' and '0' printf flags)\n\
1523 %A access rights in human readable form\n\
1524 %b number of blocks allocated (see %B)\n\
1525 %B the size in bytes of each block reported by %b\n\
1526 %C SELinux security context string\n\
1527 "), stdout);
1528 fputs (_("\
1529 %d device number in decimal\n\
1530 %D device number in hex\n\
1531 %f raw mode in hex\n\
1532 %F file type\n\
1533 %g group ID of owner\n\
1534 %G group name of owner\n\
1535 "), stdout);
1536 fputs (_("\
1537 %h number of hard links\n\
1538 %i inode number\n\
1539 %m mount point\n\
1540 %n file name\n\
1541 %N quoted file name with dereference if symbolic link\n\
1542 %o optimal I/O transfer size hint\n\
1543 %s total size, in bytes\n\
1544 %t major device type in hex, for character/block device special files\n\
1545 %T minor device type in hex, for character/block device special files\n\
1546 "), stdout);
1547 fputs (_("\
1548 %u user ID of owner\n\
1549 %U user name of owner\n\
1550 %w time of file birth, human-readable; - if unknown\n\
1551 %W time of file birth, seconds since Epoch; 0 if unknown\n\
1552 %x time of last access, human-readable\n\
1553 %X time of last access, seconds since Epoch\n\
1554 %y time of last data modification, human-readable\n\
1555 %Y time of last data modification, seconds since Epoch\n\
1556 %z time of last status change, human-readable\n\
1557 %Z time of last status change, seconds since Epoch\n\
1559 "), stdout);
1561 fputs (_("\
1562 Valid format sequences for file systems:\n\
1564 %a free blocks available to non-superuser\n\
1565 %b total data blocks in file system\n\
1566 %c total file nodes in file system\n\
1567 %d free file nodes in file system\n\
1568 %f free blocks in file system\n\
1569 "), stdout);
1570 fputs (_("\
1571 %i file system ID in hex\n\
1572 %l maximum length of filenames\n\
1573 %n file name\n\
1574 %s block size (for faster transfers)\n\
1575 %S fundamental block size (for block counts)\n\
1576 %t file system type in hex\n\
1577 %T file system type in human readable form\n\
1578 "), stdout);
1580 printf (_("\n\
1581 --terse is equivalent to the following FORMAT:\n\
1584 #if HAVE_SELINUX_SELINUX_H
1585 fmt_terse_selinux
1586 #else
1587 fmt_terse_regular
1588 #endif
1591 printf (_("\
1592 --terse --file-system is equivalent to the following FORMAT:\n\
1594 "), fmt_terse_fs);
1596 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1597 emit_ancillary_info (PROGRAM_NAME);
1599 exit (status);
1603 main (int argc, char *argv[])
1605 int c;
1606 bool fs = false;
1607 bool terse = false;
1608 char *format = NULL;
1609 char *format2;
1610 bool ok = true;
1612 initialize_main (&argc, &argv);
1613 set_program_name (argv[0]);
1614 setlocale (LC_ALL, "");
1615 bindtextdomain (PACKAGE, LOCALEDIR);
1616 textdomain (PACKAGE);
1618 struct lconv const *locale = localeconv ();
1619 decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1620 decimal_point_len = strlen (decimal_point);
1622 atexit (close_stdout);
1624 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1626 switch (c)
1628 case PRINTF_OPTION:
1629 format = optarg;
1630 interpret_backslash_escapes = true;
1631 trailing_delim = "";
1632 break;
1634 case 'c':
1635 format = optarg;
1636 interpret_backslash_escapes = false;
1637 trailing_delim = "\n";
1638 break;
1640 case 'L':
1641 follow_links = true;
1642 break;
1644 case 'f':
1645 fs = true;
1646 break;
1648 case 't':
1649 terse = true;
1650 break;
1652 case_GETOPT_HELP_CHAR;
1654 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1656 default:
1657 usage (EXIT_FAILURE);
1661 if (argc == optind)
1663 error (0, 0, _("missing operand"));
1664 usage (EXIT_FAILURE);
1667 if (format)
1669 if (strstr (format, "%N"))
1670 getenv_quoting_style ();
1671 format2 = format;
1673 else
1675 format = default_format (fs, terse, /* device= */ false);
1676 format2 = default_format (fs, terse, /* device= */ true);
1679 for (int i = optind; i < argc; i++)
1680 ok &= (fs
1681 ? do_statfs (argv[i], format)
1682 : do_stat (argv[i], format, format2));
1684 return ok ? EXIT_SUCCESS : EXIT_FAILURE;