doc: improve ls --help grammar
[coreutils/ericb.git] / src / stat.c
blob75e0e391cd39993dd332fd6ca785e5f3f36e0cd3
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_HFS: /* 0x4244 */
293 return "hfs";
294 case S_MAGIC_HPFS: /* 0xF995E849 */
295 return "hpfs";
296 case S_MAGIC_HUGETLBFS: /* 0x958458F6 */
297 return "hugetlbfs";
298 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA */
299 return "inotifyfs";
300 case S_MAGIC_ISOFS: /* 0x9660 */
301 return "isofs";
302 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
303 return "isofs";
304 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
305 return "isofs";
306 case S_MAGIC_JFFS: /* 0x07C0 */
307 return "jffs";
308 case S_MAGIC_JFFS2: /* 0x72B6 */
309 return "jffs2";
310 case S_MAGIC_JFS: /* 0x3153464A */
311 return "jfs";
312 case S_MAGIC_KAFS: /* 0x6B414653 */
313 return "k-afs";
314 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 */
315 return "lustre";
316 case S_MAGIC_MINIX: /* 0x137F */
317 return "minix";
318 case S_MAGIC_MINIX_30: /* 0x138F */
319 return "minix (30 char.)";
320 case S_MAGIC_MINIX_V2: /* 0x2468 */
321 return "minix v2";
322 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
323 return "minix v2 (30 char.)";
324 case S_MAGIC_MINIX_V3: /* 0x4D5A */
325 return "minix3";
326 case S_MAGIC_MQUEUE: /* 0x19800202 */
327 return "mqueue";
328 case S_MAGIC_MSDOS: /* 0x4D44 */
329 return "msdos";
330 case S_MAGIC_NCP: /* 0x564C */
331 return "novell";
332 case S_MAGIC_NFS: /* 0x6969 */
333 return "nfs";
334 case S_MAGIC_NFSD: /* 0x6E667364 */
335 return "nfsd";
336 case S_MAGIC_NILFS: /* 0x3434 */
337 return "nilfs";
338 case S_MAGIC_NTFS: /* 0x5346544E */
339 return "ntfs";
340 case S_MAGIC_OPENPROM: /* 0x9FA1 */
341 return "openprom";
342 case S_MAGIC_OCFS2: /* 0x7461636f */
343 return "ocfs2";
344 case S_MAGIC_PROC: /* 0x9FA0 */
345 return "proc";
346 case S_MAGIC_PSTOREFS: /* 0x6165676C */
347 return "pstorefs";
348 case S_MAGIC_QNX4: /* 0x002F */
349 return "qnx4";
350 case S_MAGIC_RAMFS: /* 0x858458F6 */
351 return "ramfs";
352 case S_MAGIC_REISERFS: /* 0x52654973 */
353 return "reiserfs";
354 case S_MAGIC_ROMFS: /* 0x7275 */
355 return "romfs";
356 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 */
357 return "rpc_pipefs";
358 case S_MAGIC_SECURITYFS: /* 0x73636673 */
359 return "securityfs";
360 case S_MAGIC_SELINUX: /* 0xF97CFF8C */
361 return "selinux";
362 case S_MAGIC_SMB: /* 0x517B */
363 return "smb";
364 case S_MAGIC_SOCKFS: /* 0x534F434B */
365 return "sockfs";
366 case S_MAGIC_SQUASHFS: /* 0x73717368 */
367 return "squashfs";
368 case S_MAGIC_SYSFS: /* 0x62656572 */
369 return "sysfs";
370 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
371 return "sysv2";
372 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
373 return "sysv4";
374 case S_MAGIC_TMPFS: /* 0x01021994 */
375 return "tmpfs";
376 case S_MAGIC_UDF: /* 0x15013346 */
377 return "udf";
378 case S_MAGIC_UFS: /* 0x00011954 */
379 return "ufs";
380 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
381 return "ufs";
382 case S_MAGIC_USBDEVFS: /* 0x9FA2 */
383 return "usbdevfs";
384 case S_MAGIC_V9FS: /* 0x01021997 */
385 return "v9fs";
386 case S_MAGIC_VXFS: /* 0xA501FCF5 */
387 return "vxfs";
388 case S_MAGIC_XENFS: /* 0xABBA1974 */
389 return "xenfs";
390 case S_MAGIC_XENIX: /* 0x012FF7B4 */
391 return "xenix";
392 case S_MAGIC_XFS: /* 0x58465342 */
393 return "xfs";
394 case S_MAGIC_XIAFS: /* 0x012FD16D */
395 return "xia";
397 # elif __GNU__
398 case FSTYPE_UFS:
399 return "ufs";
400 case FSTYPE_NFS:
401 return "nfs";
402 case FSTYPE_GFS:
403 return "gfs";
404 case FSTYPE_LFS:
405 return "lfs";
406 case FSTYPE_SYSV:
407 return "sysv";
408 case FSTYPE_FTP:
409 return "ftp";
410 case FSTYPE_TAR:
411 return "tar";
412 case FSTYPE_AR:
413 return "ar";
414 case FSTYPE_CPIO:
415 return "cpio";
416 case FSTYPE_MSLOSS:
417 return "msloss";
418 case FSTYPE_CPM:
419 return "cpm";
420 case FSTYPE_HFS:
421 return "hfs";
422 case FSTYPE_DTFS:
423 return "dtfs";
424 case FSTYPE_GRFS:
425 return "grfs";
426 case FSTYPE_TERM:
427 return "term";
428 case FSTYPE_DEV:
429 return "dev";
430 case FSTYPE_PROC:
431 return "proc";
432 case FSTYPE_IFSOCK:
433 return "ifsock";
434 case FSTYPE_AFS:
435 return "afs";
436 case FSTYPE_DFS:
437 return "dfs";
438 case FSTYPE_PROC9:
439 return "proc9";
440 case FSTYPE_SOCKET:
441 return "socket";
442 case FSTYPE_MISC:
443 return "misc";
444 case FSTYPE_EXT2FS:
445 return "ext2/ext3";
446 case FSTYPE_HTTP:
447 return "http";
448 case FSTYPE_MEMFS:
449 return "memfs";
450 case FSTYPE_ISO9660:
451 return "iso9660";
452 # endif
453 default:
455 unsigned long int type = statfsbuf->f_type;
456 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
457 + (sizeof type * CHAR_BIT + 3) / 4];
458 sprintf (buf, "UNKNOWN (0x%lx)", type);
459 return buf;
462 #endif
465 static char * ATTRIBUTE_WARN_UNUSED_RESULT
466 human_access (struct stat const *statbuf)
468 static char modebuf[12];
469 filemodestring (statbuf, modebuf);
470 modebuf[10] = 0;
471 return modebuf;
474 static char * ATTRIBUTE_WARN_UNUSED_RESULT
475 human_time (struct timespec t)
477 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
478 (INT_STRLEN_BOUND (int) /* YYYY */
479 + 1 /* because YYYY might equal INT_MAX + 1900 */
480 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
481 struct tm const *tm = localtime (&t.tv_sec);
482 if (tm == NULL)
483 return timetostr (t.tv_sec, str);
484 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
485 return str;
488 /* PFORMAT points to a '%' followed by a prefix of a format, all of
489 size PREFIX_LEN. The flags allowed for this format are
490 ALLOWED_FLAGS; remove other printf flags from the prefix, then
491 append SUFFIX. */
492 static void
493 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
494 char const *suffix)
496 char *dst = pformat + 1;
497 char const *src;
498 char const *srclim = pformat + prefix_len;
499 for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
500 if (strchr (allowed_flags, *src))
501 *dst++ = *src;
502 while (src < srclim)
503 *dst++ = *src++;
504 strcpy (dst, suffix);
507 static void
508 out_string (char *pformat, size_t prefix_len, char const *arg)
510 make_format (pformat, prefix_len, "-", "s");
511 printf (pformat, arg);
513 static int
514 out_int (char *pformat, size_t prefix_len, intmax_t arg)
516 make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
517 return printf (pformat, arg);
519 static int
520 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
522 make_format (pformat, prefix_len, "'-0", PRIuMAX);
523 return printf (pformat, arg);
525 static void
526 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
528 make_format (pformat, prefix_len, "-#0", PRIoMAX);
529 printf (pformat, arg);
531 static void
532 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
534 make_format (pformat, prefix_len, "-#0", PRIxMAX);
535 printf (pformat, arg);
537 static int
538 out_minus_zero (char *pformat, size_t prefix_len)
540 make_format (pformat, prefix_len, "'-+ 0", ".0f");
541 return printf (pformat, -0.25);
544 /* Output the number of seconds since the Epoch, using a format that
545 acts like printf's %f format. */
546 static void
547 out_epoch_sec (char *pformat, size_t prefix_len, struct stat const *statbuf,
548 struct timespec arg)
550 char *dot = memchr (pformat, '.', prefix_len);
551 size_t sec_prefix_len = prefix_len;
552 int width = 0;
553 int precision = 0;
554 bool frac_left_adjust = false;
556 if (dot)
558 sec_prefix_len = dot - pformat;
559 pformat[prefix_len] = '\0';
561 if (ISDIGIT (dot[1]))
563 long int lprec = strtol (dot + 1, NULL, 10);
564 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
566 else
568 precision = 9;
571 if (precision && ISDIGIT (dot[-1]))
573 /* If a nontrivial width is given, subtract the width of the
574 decimal point and PRECISION digits that will be output
575 later. */
576 char *p = dot;
577 *dot = '\0';
580 --p;
581 while (ISDIGIT (p[-1]));
583 long int lwidth = strtol (p, NULL, 10);
584 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
585 if (1 < width)
587 p += (*p == '0');
588 sec_prefix_len = p - pformat;
589 int w_d = (decimal_point_len < width
590 ? width - decimal_point_len
591 : 0);
592 if (1 < w_d)
594 int w = w_d - precision;
595 if (1 < w)
597 char *dst = pformat;
598 for (char const *src = dst; src < p; src++)
600 if (*src == '-')
601 frac_left_adjust = true;
602 else
603 *dst++ = *src;
605 sec_prefix_len =
606 (dst - pformat
607 + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
614 int divisor = 1;
615 for (int i = precision; i < 9; i++)
616 divisor *= 10;
617 int frac_sec = arg.tv_nsec / divisor;
618 int int_len;
620 if (TYPE_SIGNED (time_t))
622 bool minus_zero = false;
623 if (arg.tv_sec < 0 && arg.tv_nsec != 0)
625 int frac_sec_modulus = 1000000000 / divisor;
626 frac_sec = (frac_sec_modulus - frac_sec
627 - (arg.tv_nsec % divisor != 0));
628 arg.tv_sec += (frac_sec != 0);
629 minus_zero = (arg.tv_sec == 0);
631 int_len = (minus_zero
632 ? out_minus_zero (pformat, sec_prefix_len)
633 : out_int (pformat, sec_prefix_len, arg.tv_sec));
635 else
636 int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
638 if (precision)
640 int prec = (precision < 9 ? precision : 9);
641 int trailing_prec = precision - prec;
642 int ilen = (int_len < 0 ? 0 : int_len);
643 int trailing_width = (ilen < width && decimal_point_len < width - ilen
644 ? width - ilen - decimal_point_len - prec
645 : 0);
646 printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
647 trailing_width, trailing_prec, 0);
651 /* Print the context information of FILENAME, and return true iff the
652 context could not be obtained. */
653 static bool ATTRIBUTE_WARN_UNUSED_RESULT
654 out_file_context (char *pformat, size_t prefix_len, char const *filename)
656 char *scontext;
657 bool fail = false;
659 if ((follow_links
660 ? getfilecon (filename, &scontext)
661 : lgetfilecon (filename, &scontext)) < 0)
663 error (0, errno, _("failed to get security context of %s"),
664 quote (filename));
665 scontext = NULL;
666 fail = true;
668 strcpy (pformat + prefix_len, "s");
669 printf (pformat, (scontext ? scontext : "?"));
670 if (scontext)
671 freecon (scontext);
672 return fail;
675 /* Print statfs info. Return zero upon success, nonzero upon failure. */
676 static bool ATTRIBUTE_WARN_UNUSED_RESULT
677 print_statfs (char *pformat, size_t prefix_len, unsigned int m,
678 char const *filename,
679 void const *data)
681 STRUCT_STATVFS const *statfsbuf = data;
682 bool fail = false;
684 switch (m)
686 case 'n':
687 out_string (pformat, prefix_len, filename);
688 break;
690 case 'i':
692 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
693 uintmax_t fsid = statfsbuf->f_fsid;
694 #else
695 typedef unsigned int fsid_word;
696 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
697 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
698 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
699 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
701 /* Assume a little-endian word order, as that is compatible
702 with glibc's statvfs implementation. */
703 uintmax_t fsid = 0;
704 int words = sizeof statfsbuf->f_fsid / sizeof *p;
705 int i;
706 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
708 uintmax_t u = p[words - 1 - i];
709 fsid |= u << (i * CHAR_BIT * sizeof *p);
711 #endif
712 out_uint_x (pformat, prefix_len, fsid);
714 break;
716 case 'l':
717 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
718 break;
719 case 't':
720 #if HAVE_STRUCT_STATXFS_F_TYPE
721 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
722 #else
723 fputc ('?', stdout);
724 #endif
725 break;
726 case 'T':
727 out_string (pformat, prefix_len, human_fstype (statfsbuf));
728 break;
729 case 'b':
730 out_int (pformat, prefix_len, statfsbuf->f_blocks);
731 break;
732 case 'f':
733 out_int (pformat, prefix_len, statfsbuf->f_bfree);
734 break;
735 case 'a':
736 out_int (pformat, prefix_len, statfsbuf->f_bavail);
737 break;
738 case 's':
739 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
740 break;
741 case 'S':
743 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
744 if (! frsize)
745 frsize = statfsbuf->f_bsize;
746 out_uint (pformat, prefix_len, frsize);
748 break;
749 case 'c':
750 out_uint (pformat, prefix_len, statfsbuf->f_files);
751 break;
752 case 'd':
753 out_int (pformat, prefix_len, statfsbuf->f_ffree);
754 break;
755 default:
756 fputc ('?', stdout);
757 break;
759 return fail;
762 /* Return any bind mounted source for a path.
763 The caller should not free the returned buffer.
764 Return NULL if no bind mount found. */
765 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
766 find_bind_mount (char const * name)
768 char const * bind_mount = NULL;
770 static struct mount_entry *mount_list;
771 static bool tried_mount_list = false;
772 if (!tried_mount_list) /* attempt/warn once per process. */
774 if (!(mount_list = read_file_system_list (false)))
775 error (0, errno, "%s", _("cannot read table of mounted file systems"));
776 tried_mount_list = true;
779 struct mount_entry *me;
780 for (me = mount_list; me; me = me->me_next)
782 if (me->me_dummy && me->me_devname[0] == '/'
783 && STREQ (me->me_mountdir, name))
785 struct stat name_stats;
786 struct stat dev_stats;
788 if (stat (name, &name_stats) == 0
789 && stat (me->me_devname, &dev_stats) == 0
790 && SAME_INODE (name_stats, dev_stats))
792 bind_mount = me->me_devname;
793 break;
798 return bind_mount;
801 /* Print mount point. Return zero upon success, nonzero upon failure. */
802 static bool ATTRIBUTE_WARN_UNUSED_RESULT
803 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
804 const struct stat *statp)
807 char const *np = "?", *bp = NULL;
808 char *mp = NULL;
809 bool fail = true;
811 /* Look for bind mounts first. Note we output the immediate alias,
812 rather than further resolving to a base device mount point. */
813 if (follow_links || !S_ISLNK (statp->st_mode))
815 char *resolved = canonicalize_file_name (filename);
816 if (!resolved)
818 error (0, errno, _("failed to canonicalize %s"), quote (filename));
819 goto print_mount_point;
821 bp = find_bind_mount (resolved);
822 free (resolved);
823 if (bp)
825 fail = false;
826 goto print_mount_point;
830 /* If there is no direct bind mount, then navigate
831 back up the tree looking for a device change.
832 Note we don't detect if any of the directory components
833 are bind mounted to the same device, but that's OK
834 since we've not directly queried them. */
835 if ((mp = find_mount_point (filename, statp)))
837 /* This dir might be bind mounted to another device,
838 so we resolve the bound source in that case also. */
839 bp = find_bind_mount (mp);
840 fail = false;
843 print_mount_point:
845 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
846 free (mp);
847 return fail;
850 /* Map a TS with negative TS.tv_nsec to {0,0}. */
851 static inline struct timespec
852 neg_to_zero (struct timespec ts)
854 if (0 <= ts.tv_nsec)
855 return ts;
856 struct timespec z = {0, 0};
857 return z;
860 /* Print stat info. Return zero upon success, nonzero upon failure. */
861 static bool
862 print_stat (char *pformat, size_t prefix_len, unsigned int m,
863 char const *filename, void const *data)
865 struct stat *statbuf = (struct stat *) data;
866 struct passwd *pw_ent;
867 struct group *gw_ent;
868 bool fail = false;
870 switch (m)
872 case 'n':
873 out_string (pformat, prefix_len, filename);
874 break;
875 case 'N':
876 out_string (pformat, prefix_len, quote (filename));
877 if (S_ISLNK (statbuf->st_mode))
879 char *linkname = areadlink_with_size (filename, statbuf->st_size);
880 if (linkname == NULL)
882 error (0, errno, _("cannot read symbolic link %s"),
883 quote (filename));
884 return true;
886 printf (" -> ");
887 out_string (pformat, prefix_len, quote (linkname));
888 free (linkname);
890 break;
891 case 'd':
892 out_uint (pformat, prefix_len, statbuf->st_dev);
893 break;
894 case 'D':
895 out_uint_x (pformat, prefix_len, statbuf->st_dev);
896 break;
897 case 'i':
898 out_uint (pformat, prefix_len, statbuf->st_ino);
899 break;
900 case 'a':
901 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
902 break;
903 case 'A':
904 out_string (pformat, prefix_len, human_access (statbuf));
905 break;
906 case 'f':
907 out_uint_x (pformat, prefix_len, statbuf->st_mode);
908 break;
909 case 'F':
910 out_string (pformat, prefix_len, file_type (statbuf));
911 break;
912 case 'h':
913 out_uint (pformat, prefix_len, statbuf->st_nlink);
914 break;
915 case 'u':
916 out_uint (pformat, prefix_len, statbuf->st_uid);
917 break;
918 case 'U':
919 setpwent ();
920 pw_ent = getpwuid (statbuf->st_uid);
921 out_string (pformat, prefix_len,
922 pw_ent ? pw_ent->pw_name : "UNKNOWN");
923 break;
924 case 'g':
925 out_uint (pformat, prefix_len, statbuf->st_gid);
926 break;
927 case 'G':
928 setgrent ();
929 gw_ent = getgrgid (statbuf->st_gid);
930 out_string (pformat, prefix_len,
931 gw_ent ? gw_ent->gr_name : "UNKNOWN");
932 break;
933 case 't':
934 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
935 break;
936 case 'm':
937 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
938 break;
939 case 'T':
940 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
941 break;
942 case 's':
943 out_uint (pformat, prefix_len, statbuf->st_size);
944 break;
945 case 'B':
946 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
947 break;
948 case 'b':
949 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
950 break;
951 case 'o':
952 out_uint (pformat, prefix_len, statbuf->st_blksize);
953 break;
954 case 'w':
956 struct timespec t = get_stat_birthtime (statbuf);
957 if (t.tv_nsec < 0)
958 out_string (pformat, prefix_len, "-");
959 else
960 out_string (pformat, prefix_len, human_time (t));
962 break;
963 case 'W':
964 out_epoch_sec (pformat, prefix_len, statbuf,
965 neg_to_zero (get_stat_birthtime (statbuf)));
966 break;
967 case 'x':
968 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
969 break;
970 case 'X':
971 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
972 break;
973 case 'y':
974 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
975 break;
976 case 'Y':
977 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
978 break;
979 case 'z':
980 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
981 break;
982 case 'Z':
983 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
984 break;
985 case 'C':
986 fail |= out_file_context (pformat, prefix_len, filename);
987 break;
988 default:
989 fputc ('?', stdout);
990 break;
992 return fail;
995 /* Output a single-character \ escape. */
997 static void
998 print_esc_char (char c)
1000 switch (c)
1002 case 'a': /* Alert. */
1003 c ='\a';
1004 break;
1005 case 'b': /* Backspace. */
1006 c ='\b';
1007 break;
1008 case 'e': /* Escape. */
1009 c ='\x1B';
1010 break;
1011 case 'f': /* Form feed. */
1012 c ='\f';
1013 break;
1014 case 'n': /* New line. */
1015 c ='\n';
1016 break;
1017 case 'r': /* Carriage return. */
1018 c ='\r';
1019 break;
1020 case 't': /* Horizontal tab. */
1021 c ='\t';
1022 break;
1023 case 'v': /* Vertical tab. */
1024 c ='\v';
1025 break;
1026 case '"':
1027 case '\\':
1028 break;
1029 default:
1030 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
1031 break;
1033 putchar (c);
1036 /* Print the information specified by the format string, FORMAT,
1037 calling PRINT_FUNC for each %-directive encountered.
1038 Return zero upon success, nonzero upon failure. */
1039 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1040 print_it (char const *format, char const *filename,
1041 bool (*print_func) (char *, size_t, unsigned int,
1042 char const *, void const *),
1043 void const *data)
1045 bool fail = false;
1047 /* Add 2 to accommodate our conversion of the stat `%s' format string
1048 to the longer printf `%llu' one. */
1049 enum
1051 MAX_ADDITIONAL_BYTES =
1052 (MAX (sizeof PRIdMAX,
1053 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
1054 - 1)
1056 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1057 char *dest = xmalloc (n_alloc);
1058 char const *b;
1059 for (b = format; *b; b++)
1061 switch (*b)
1063 case '%':
1065 size_t len = strspn (b + 1, printf_flags);
1066 char const *fmt_char = b + len + 1;
1067 fmt_char += strspn (fmt_char, digits);
1068 if (*fmt_char == '.')
1069 fmt_char += 1 + strspn (fmt_char + 1, digits);
1070 len = fmt_char - (b + 1);
1071 unsigned int fmt_code = *fmt_char;
1072 memcpy (dest, b, len + 1);
1074 b = fmt_char;
1075 switch (fmt_code)
1077 case '\0':
1078 --b;
1079 /* fall through */
1080 case '%':
1081 if (0 < len)
1083 dest[len + 1] = *fmt_char;
1084 dest[len + 2] = '\0';
1085 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
1086 quotearg_colon (dest));
1088 putchar ('%');
1089 break;
1090 default:
1091 fail |= print_func (dest, len + 1, fmt_code, filename, data);
1092 break;
1094 break;
1097 case '\\':
1098 if ( ! interpret_backslash_escapes)
1100 putchar ('\\');
1101 break;
1103 ++b;
1104 if (isodigit (*b))
1106 int esc_value = octtobin (*b);
1107 int esc_length = 1; /* number of octal digits */
1108 for (++b; esc_length < 3 && isodigit (*b);
1109 ++esc_length, ++b)
1111 esc_value = esc_value * 8 + octtobin (*b);
1113 putchar (esc_value);
1114 --b;
1116 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
1118 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
1119 /* A hexadecimal \xhh escape sequence must have
1120 1 or 2 hex. digits. */
1121 ++b;
1122 if (isxdigit (to_uchar (b[1])))
1124 ++b;
1125 esc_value = esc_value * 16 + hextobin (*b);
1127 putchar (esc_value);
1129 else if (*b == '\0')
1131 error (0, 0, _("warning: backslash at end of format"));
1132 putchar ('\\');
1133 /* Arrange to exit the loop. */
1134 --b;
1136 else
1138 print_esc_char (*b);
1140 break;
1142 default:
1143 putchar (*b);
1144 break;
1147 free (dest);
1149 fputs (trailing_delim, stdout);
1151 return fail;
1154 /* Stat the file system and print what we find. */
1155 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1156 do_statfs (char const *filename, bool terse, char const *format)
1158 STRUCT_STATVFS statfsbuf;
1160 if (STREQ (filename, "-"))
1162 error (0, 0, _("using %s to denote standard input does not work"
1163 " in file system mode"), quote (filename));
1164 return false;
1167 if (STATFS (filename, &statfsbuf) != 0)
1169 error (0, errno, _("cannot read file system information for %s"),
1170 quote (filename));
1171 return false;
1174 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1175 return ! fail;
1178 /* stat the file and print what we find */
1179 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1180 do_stat (char const *filename, bool terse, char const *format,
1181 char const *format2)
1183 struct stat statbuf;
1185 if (STREQ (filename, "-"))
1187 if (fstat (STDIN_FILENO, &statbuf) != 0)
1189 error (0, errno, _("cannot stat standard input"));
1190 return false;
1193 /* We can't use the shorter
1194 (follow_links?stat:lstat) (filename, &statbug)
1195 since stat might be a function-like macro. */
1196 else if ((follow_links
1197 ? stat (filename, &statbuf)
1198 : lstat (filename, &statbuf)) != 0)
1200 error (0, errno, _("cannot stat %s"), quote (filename));
1201 return false;
1204 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1205 format = format2;
1207 bool fail = print_it (format, filename, print_stat, &statbuf);
1208 return ! fail;
1211 /* Return an allocated format string in static storage that
1212 corresponds to whether FS and TERSE options were declared. */
1213 static char *
1214 default_format (bool fs, bool terse, bool device)
1216 char *format;
1217 if (fs)
1219 if (terse)
1220 format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1221 else
1223 /* TRANSLATORS: This string uses format specifiers from
1224 'stat --help' with --file-system, and NOT from printf. */
1225 format = xstrdup (_("\
1226 File: \"%n\"\n\
1227 ID: %-8i Namelen: %-7l Type: %T\n\
1228 Block size: %-10s Fundamental block size: %S\n\
1229 Blocks: Total: %-10b Free: %-10f Available: %a\n\
1230 Inodes: Total: %-10c Free: %d\n\
1231 "));
1234 else /* ! fs */
1236 if (terse)
1238 if (0 < is_selinux_enabled ())
1239 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1240 " %X %Y %Z %W %o %C\n");
1241 else
1242 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1243 " %X %Y %Z %W %o\n");
1245 else
1247 char *temp;
1248 /* TRANSLATORS: This string uses format specifiers from
1249 'stat --help' without --file-system, and NOT from printf. */
1250 format = xstrdup (_("\
1251 File: %N\n\
1252 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1253 "));
1255 temp = format;
1256 if (device)
1258 /* TRANSLATORS: This string uses format specifiers from
1259 'stat --help' without --file-system, and NOT from printf. */
1260 format = xasprintf ("%s%s", format, _("\
1261 Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1262 "));
1264 else
1266 /* TRANSLATORS: This string uses format specifiers from
1267 'stat --help' without --file-system, and NOT from printf. */
1268 format = xasprintf ("%s%s", format, _("\
1269 Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1270 "));
1272 free (temp);
1274 temp = format;
1275 /* TRANSLATORS: This string uses format specifiers from
1276 'stat --help' without --file-system, and NOT from printf. */
1277 format = xasprintf ("%s%s", format, _("\
1278 Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1279 "));
1280 free (temp);
1282 if (0 < is_selinux_enabled ())
1284 temp = format;
1285 /* TRANSLATORS: This string uses format specifiers from
1286 'stat --help' without --file-system, and NOT from printf. */
1287 format = xasprintf ("%s%s", format, _("\
1288 Context: %C\n\
1289 "));
1290 free (temp);
1293 temp = format;
1294 /* TRANSLATORS: This string uses format specifiers from
1295 'stat --help' without --file-system, and NOT from printf. */
1296 format = xasprintf ("%s%s", format, _("\
1297 Access: %x\n\
1298 Modify: %y\n\
1299 Change: %z\n\
1300 Birth: %w\n\
1301 "));
1302 free (temp);
1305 return format;
1308 void
1309 usage (int status)
1311 if (status != EXIT_SUCCESS)
1312 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1313 program_name);
1314 else
1316 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1317 fputs (_("\
1318 Display file or file system status.\n\
1320 -L, --dereference follow links\n\
1321 -f, --file-system display file system status instead of file status\n\
1322 "), stdout);
1323 fputs (_("\
1324 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1325 output a newline after each use of FORMAT\n\
1326 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1327 and do not output a mandatory trailing newline.\n\
1328 If you want a newline, include \\n in FORMAT\n\
1329 -t, --terse print the information in terse form\n\
1330 "), stdout);
1331 fputs (HELP_OPTION_DESCRIPTION, stdout);
1332 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1334 fputs (_("\n\
1335 The valid format sequences for files (without --file-system):\n\
1337 %a Access rights in octal\n\
1338 %A Access rights in human readable form\n\
1339 %b Number of blocks allocated (see %B)\n\
1340 %B The size in bytes of each block reported by %b\n\
1341 %C SELinux security context string\n\
1342 "), stdout);
1343 fputs (_("\
1344 %d Device number in decimal\n\
1345 %D Device number in hex\n\
1346 %f Raw mode in hex\n\
1347 %F File type\n\
1348 %g Group ID of owner\n\
1349 %G Group name of owner\n\
1350 "), stdout);
1351 fputs (_("\
1352 %h Number of hard links\n\
1353 %i Inode number\n\
1354 %m Mount point\n\
1355 %n File name\n\
1356 %N Quoted file name with dereference if symbolic link\n\
1357 %o I/O block size\n\
1358 %s Total size, in bytes\n\
1359 %t Major device type in hex\n\
1360 %T Minor device type in hex\n\
1361 "), stdout);
1362 fputs (_("\
1363 %u User ID of owner\n\
1364 %U User name of owner\n\
1365 %w Time of file birth, human-readable; - if unknown\n\
1366 %W Time of file birth, seconds since Epoch; 0 if unknown\n\
1367 %x Time of last access, human-readable\n\
1368 %X Time of last access, seconds since Epoch\n\
1369 %y Time of last modification, human-readable\n\
1370 %Y Time of last modification, seconds since Epoch\n\
1371 %z Time of last change, human-readable\n\
1372 %Z Time of last change, seconds since Epoch\n\
1374 "), stdout);
1376 fputs (_("\
1377 Valid format sequences for file systems:\n\
1379 %a Free blocks available to non-superuser\n\
1380 %b Total data blocks in file system\n\
1381 %c Total file nodes in file system\n\
1382 %d Free file nodes in file system\n\
1383 %f Free blocks in file system\n\
1384 "), stdout);
1385 fputs (_("\
1386 %i File System ID in hex\n\
1387 %l Maximum length of filenames\n\
1388 %n File name\n\
1389 %s Block size (for faster transfers)\n\
1390 %S Fundamental block size (for block counts)\n\
1391 %t Type in hex\n\
1392 %T Type in human readable form\n\
1393 "), stdout);
1394 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1395 emit_ancillary_info ();
1397 exit (status);
1401 main (int argc, char *argv[])
1403 int c;
1404 int i;
1405 bool fs = false;
1406 bool terse = false;
1407 char *format = NULL;
1408 char *format2;
1409 bool ok = true;
1411 initialize_main (&argc, &argv);
1412 set_program_name (argv[0]);
1413 setlocale (LC_ALL, "");
1414 bindtextdomain (PACKAGE, LOCALEDIR);
1415 textdomain (PACKAGE);
1417 struct lconv const *locale = localeconv ();
1418 decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1419 decimal_point_len = strlen (decimal_point);
1421 atexit (close_stdout);
1423 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1425 switch (c)
1427 case PRINTF_OPTION:
1428 format = optarg;
1429 interpret_backslash_escapes = true;
1430 trailing_delim = "";
1431 break;
1433 case 'c':
1434 format = optarg;
1435 interpret_backslash_escapes = false;
1436 trailing_delim = "\n";
1437 break;
1439 case 'L':
1440 follow_links = true;
1441 break;
1443 case 'f':
1444 fs = true;
1445 break;
1447 case 't':
1448 terse = true;
1449 break;
1451 case_GETOPT_HELP_CHAR;
1453 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1455 default:
1456 usage (EXIT_FAILURE);
1460 if (argc == optind)
1462 error (0, 0, _("missing operand"));
1463 usage (EXIT_FAILURE);
1466 if (format)
1467 format2 = format;
1468 else
1470 format = default_format (fs, terse, false);
1471 format2 = default_format (fs, terse, true);
1474 for (i = optind; i < argc; i++)
1475 ok &= (fs
1476 ? do_statfs (argv[i], terse, format)
1477 : do_stat (argv[i], terse, format, format2));
1479 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);