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