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