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