maint: revert "build: update gnulib submodule to latest"
[coreutils/ericb.git] / src / stat.c
blob4e5dbcef7c5893c23cc9ca763e9bacdc2cd99065
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 || 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 # define STATFS_FRSIZE(S) 0
130 # endif
131 #endif
133 #ifdef SB_F_NAMEMAX
134 # define OUT_NAMEMAX out_uint
135 #else
136 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
137 # define SB_F_NAMEMAX(S) "*"
138 # define OUT_NAMEMAX out_string
139 #endif
141 #if HAVE_STRUCT_STATVFS_F_BASETYPE
142 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
143 #else
144 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
145 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
146 # elif HAVE_OS_H /* BeOS */
147 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
148 # endif
149 #endif
151 /* FIXME: these are used by printf.c, too */
152 #define isodigit(c) ('0' <= (c) && (c) <= '7')
153 #define octtobin(c) ((c) - '0')
154 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
155 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
157 static char const digits[] = "0123456789";
159 /* Flags that are portable for use in printf, for at least one
160 conversion specifier; make_format removes unportable flags as
161 needed for particular specifiers. The glibc 2.2 extension "I" is
162 listed here; it is removed by make_format because it has undefined
163 behavior elsewhere and because it is incompatible with
164 out_epoch_sec. */
165 static char const printf_flags[] = "'-+ #0I";
167 #define PROGRAM_NAME "stat"
169 #define AUTHORS proper_name ("Michael Meskes")
171 enum
173 PRINTF_OPTION = CHAR_MAX + 1
176 static struct option const long_options[] =
178 {"context", no_argument, 0, 'Z'},
179 {"dereference", no_argument, NULL, 'L'},
180 {"file-system", no_argument, NULL, 'f'},
181 {"format", required_argument, NULL, 'c'},
182 {"printf", required_argument, NULL, PRINTF_OPTION},
183 {"terse", no_argument, NULL, 't'},
184 {GETOPT_HELP_OPTION_DECL},
185 {GETOPT_VERSION_OPTION_DECL},
186 {NULL, 0, NULL, 0}
189 /* Whether to follow symbolic links; True for --dereference (-L). */
190 static bool follow_links;
192 /* Whether to interpret backslash-escape sequences.
193 True for --printf=FMT, not for --format=FMT (-c). */
194 static bool interpret_backslash_escapes;
196 /* The trailing delimiter string:
197 "" for --printf=FMT, "\n" for --format=FMT (-c). */
198 static char const *trailing_delim = "";
200 /* The representation of the decimal point in the current locale. */
201 static char const *decimal_point;
202 static size_t decimal_point_len;
204 /* Return the type of the specified file system.
205 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
206 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
207 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
208 Still others have neither and have to get by with f_type (GNU/Linux).
209 But f_type may only exist in statfs (Cygwin). */
210 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
211 human_fstype (STRUCT_STATVFS const *statfsbuf)
213 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
214 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
215 #else
216 switch (statfsbuf->f_type)
218 # if defined __linux__
220 /* Compare with what's in libc:
221 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
222 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
223 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
224 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
225 | sort > sym_libc
226 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
227 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
228 | sort > sym_stat
229 diff -u sym_stat sym_libc
232 /* Also compare with the list in "man 2 statfs" using the
233 fs-magic-compare make target. */
235 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
236 statements must be followed by a hexadecimal constant in
237 a comment. The S_MAGIC_... name and constant are automatically
238 combined to produce the #define directives in fs.h. */
240 case S_MAGIC_ADFS: /* 0xADF5 */
241 return "adfs";
242 case S_MAGIC_AFFS: /* 0xADFF */
243 return "affs";
244 case S_MAGIC_AFS: /* 0x5346414F */
245 return "afs";
246 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 */
247 return "anon-inode FS";
248 case S_MAGIC_AUTOFS: /* 0x0187 */
249 return "autofs";
250 case S_MAGIC_BEFS: /* 0x42465331 */
251 return "befs";
252 case S_MAGIC_BFS: /* 0x1BADFACE */
253 return "bfs";
254 case S_MAGIC_BINFMT_MISC: /* 0x42494E4D */
255 return "binfmt_misc";
256 case S_MAGIC_BTRFS: /* 0x9123683E */
257 return "btrfs";
258 case S_MAGIC_CGROUP: /* 0x0027E0EB */
259 return "cgroupfs";
260 case S_MAGIC_CIFS: /* 0xFF534D42 */
261 return "cifs";
262 case S_MAGIC_CODA: /* 0x73757245 */
263 return "coda";
264 case S_MAGIC_COH: /* 0x012FF7B7 */
265 return "coh";
266 case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
267 return "cramfs";
268 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 */
269 return "cramfs-wend";
270 case S_MAGIC_DEBUGFS: /* 0x64626720 */
271 return "debugfs";
272 case S_MAGIC_DEVFS: /* 0x1373 */
273 return "devfs";
274 case S_MAGIC_DEVPTS: /* 0x1CD1 */
275 return "devpts";
276 case S_MAGIC_ECRYPTFS: /* 0xF15F */
277 return "ecryptfs";
278 case S_MAGIC_EFS: /* 0x00414A53 */
279 return "efs";
280 case S_MAGIC_EXT: /* 0x137D */
281 return "ext";
282 case S_MAGIC_EXT2: /* 0xEF53 */
283 return "ext2/ext3";
284 case S_MAGIC_EXT2_OLD: /* 0xEF51 */
285 return "ext2";
286 case S_MAGIC_FAT: /* 0x4006 */
287 return "fat";
288 case S_MAGIC_FUSEBLK: /* 0x65735546 */
289 return "fuseblk";
290 case S_MAGIC_FUSECTL: /* 0x65735543 */
291 return "fusectl";
292 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA */
293 return "futexfs";
294 case S_MAGIC_GFS: /* 0x1161970 */
295 return "gfs/gfs2";
296 case S_MAGIC_GPFS: /* 0x47504653 */
297 return "gpfs";
298 case S_MAGIC_HFS: /* 0x4244 */
299 return "hfs";
300 case S_MAGIC_HPFS: /* 0xF995E849 */
301 return "hpfs";
302 case S_MAGIC_HUGETLBFS: /* 0x958458F6 */
303 return "hugetlbfs";
304 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA */
305 return "inotifyfs";
306 case S_MAGIC_ISOFS: /* 0x9660 */
307 return "isofs";
308 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
309 return "isofs";
310 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
311 return "isofs";
312 case S_MAGIC_JFFS: /* 0x07C0 */
313 return "jffs";
314 case S_MAGIC_JFFS2: /* 0x72B6 */
315 return "jffs2";
316 case S_MAGIC_JFS: /* 0x3153464A */
317 return "jfs";
318 case S_MAGIC_KAFS: /* 0x6B414653 */
319 return "k-afs";
320 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 */
321 return "lustre";
322 case S_MAGIC_MINIX: /* 0x137F */
323 return "minix";
324 case S_MAGIC_MINIX_30: /* 0x138F */
325 return "minix (30 char.)";
326 case S_MAGIC_MINIX_V2: /* 0x2468 */
327 return "minix v2";
328 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
329 return "minix v2 (30 char.)";
330 case S_MAGIC_MINIX_V3: /* 0x4D5A */
331 return "minix3";
332 case S_MAGIC_MQUEUE: /* 0x19800202 */
333 return "mqueue";
334 case S_MAGIC_MSDOS: /* 0x4D44 */
335 return "msdos";
336 case S_MAGIC_NCP: /* 0x564C */
337 return "novell";
338 case S_MAGIC_NFS: /* 0x6969 */
339 return "nfs";
340 case S_MAGIC_NFSD: /* 0x6E667364 */
341 return "nfsd";
342 case S_MAGIC_NILFS: /* 0x3434 */
343 return "nilfs";
344 case S_MAGIC_NTFS: /* 0x5346544E */
345 return "ntfs";
346 case S_MAGIC_OPENPROM: /* 0x9FA1 */
347 return "openprom";
348 case S_MAGIC_OCFS2: /* 0x7461636f */
349 return "ocfs2";
350 case S_MAGIC_PROC: /* 0x9FA0 */
351 return "proc";
352 case S_MAGIC_PSTOREFS: /* 0x6165676C */
353 return "pstorefs";
354 case S_MAGIC_QNX4: /* 0x002F */
355 return "qnx4";
356 case S_MAGIC_RAMFS: /* 0x858458F6 */
357 return "ramfs";
358 case S_MAGIC_REISERFS: /* 0x52654973 */
359 return "reiserfs";
360 case S_MAGIC_ROMFS: /* 0x7275 */
361 return "romfs";
362 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 */
363 return "rpc_pipefs";
364 case S_MAGIC_SECURITYFS: /* 0x73636673 */
365 return "securityfs";
366 case S_MAGIC_SELINUX: /* 0xF97CFF8C */
367 return "selinux";
368 case S_MAGIC_SMB: /* 0x517B */
369 return "smb";
370 case S_MAGIC_SOCKFS: /* 0x534F434B */
371 return "sockfs";
372 case S_MAGIC_SQUASHFS: /* 0x73717368 */
373 return "squashfs";
374 case S_MAGIC_SYSFS: /* 0x62656572 */
375 return "sysfs";
376 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
377 return "sysv2";
378 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
379 return "sysv4";
380 case S_MAGIC_TMPFS: /* 0x01021994 */
381 return "tmpfs";
382 case S_MAGIC_UDF: /* 0x15013346 */
383 return "udf";
384 case S_MAGIC_UFS: /* 0x00011954 */
385 return "ufs";
386 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
387 return "ufs";
388 case S_MAGIC_USBDEVFS: /* 0x9FA2 */
389 return "usbdevfs";
390 case S_MAGIC_V9FS: /* 0x01021997 */
391 return "v9fs";
392 case S_MAGIC_VXFS: /* 0xA501FCF5 */
393 return "vxfs";
394 case S_MAGIC_XENFS: /* 0xABBA1974 */
395 return "xenfs";
396 case S_MAGIC_XENIX: /* 0x012FF7B4 */
397 return "xenix";
398 case S_MAGIC_XFS: /* 0x58465342 */
399 return "xfs";
400 case S_MAGIC_XIAFS: /* 0x012FD16D */
401 return "xia";
403 # elif __GNU__
404 case FSTYPE_UFS:
405 return "ufs";
406 case FSTYPE_NFS:
407 return "nfs";
408 case FSTYPE_GFS:
409 return "gfs";
410 case FSTYPE_LFS:
411 return "lfs";
412 case FSTYPE_SYSV:
413 return "sysv";
414 case FSTYPE_FTP:
415 return "ftp";
416 case FSTYPE_TAR:
417 return "tar";
418 case FSTYPE_AR:
419 return "ar";
420 case FSTYPE_CPIO:
421 return "cpio";
422 case FSTYPE_MSLOSS:
423 return "msloss";
424 case FSTYPE_CPM:
425 return "cpm";
426 case FSTYPE_HFS:
427 return "hfs";
428 case FSTYPE_DTFS:
429 return "dtfs";
430 case FSTYPE_GRFS:
431 return "grfs";
432 case FSTYPE_TERM:
433 return "term";
434 case FSTYPE_DEV:
435 return "dev";
436 case FSTYPE_PROC:
437 return "proc";
438 case FSTYPE_IFSOCK:
439 return "ifsock";
440 case FSTYPE_AFS:
441 return "afs";
442 case FSTYPE_DFS:
443 return "dfs";
444 case FSTYPE_PROC9:
445 return "proc9";
446 case FSTYPE_SOCKET:
447 return "socket";
448 case FSTYPE_MISC:
449 return "misc";
450 case FSTYPE_EXT2FS:
451 return "ext2/ext3";
452 case FSTYPE_HTTP:
453 return "http";
454 case FSTYPE_MEMFS:
455 return "memfs";
456 case FSTYPE_ISO9660:
457 return "iso9660";
458 # endif
459 default:
461 unsigned long int type = statfsbuf->f_type;
462 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
463 + (sizeof type * CHAR_BIT + 3) / 4];
464 sprintf (buf, "UNKNOWN (0x%lx)", type);
465 return buf;
468 #endif
471 static char * ATTRIBUTE_WARN_UNUSED_RESULT
472 human_access (struct stat const *statbuf)
474 static char modebuf[12];
475 filemodestring (statbuf, modebuf);
476 modebuf[10] = 0;
477 return modebuf;
480 static char * ATTRIBUTE_WARN_UNUSED_RESULT
481 human_time (struct timespec t)
483 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
484 (INT_STRLEN_BOUND (int) /* YYYY */
485 + 1 /* because YYYY might equal INT_MAX + 1900 */
486 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
487 struct tm const *tm = localtime (&t.tv_sec);
488 if (tm == NULL)
489 return timetostr (t.tv_sec, str);
490 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
491 return str;
494 /* PFORMAT points to a '%' followed by a prefix of a format, all of
495 size PREFIX_LEN. The flags allowed for this format are
496 ALLOWED_FLAGS; remove other printf flags from the prefix, then
497 append SUFFIX. */
498 static void
499 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
500 char const *suffix)
502 char *dst = pformat + 1;
503 char const *src;
504 char const *srclim = pformat + prefix_len;
505 for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
506 if (strchr (allowed_flags, *src))
507 *dst++ = *src;
508 while (src < srclim)
509 *dst++ = *src++;
510 strcpy (dst, suffix);
513 static void
514 out_string (char *pformat, size_t prefix_len, char const *arg)
516 make_format (pformat, prefix_len, "-", "s");
517 printf (pformat, arg);
519 static int
520 out_int (char *pformat, size_t prefix_len, intmax_t arg)
522 make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
523 return printf (pformat, arg);
525 static int
526 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
528 make_format (pformat, prefix_len, "'-0", PRIuMAX);
529 return printf (pformat, arg);
531 static void
532 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
534 make_format (pformat, prefix_len, "-#0", PRIoMAX);
535 printf (pformat, arg);
537 static void
538 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
540 make_format (pformat, prefix_len, "-#0", PRIxMAX);
541 printf (pformat, arg);
543 static int
544 out_minus_zero (char *pformat, size_t prefix_len)
546 make_format (pformat, prefix_len, "'-+ 0", ".0f");
547 return printf (pformat, -0.25);
550 /* Output the number of seconds since the Epoch, using a format that
551 acts like printf's %f format. */
552 static void
553 out_epoch_sec (char *pformat, size_t prefix_len, struct stat const *statbuf,
554 struct timespec arg)
556 char *dot = memchr (pformat, '.', prefix_len);
557 size_t sec_prefix_len = prefix_len;
558 int width = 0;
559 int precision = 0;
560 bool frac_left_adjust = false;
562 if (dot)
564 sec_prefix_len = dot - pformat;
565 pformat[prefix_len] = '\0';
567 if (ISDIGIT (dot[1]))
569 long int lprec = strtol (dot + 1, NULL, 10);
570 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
572 else
574 precision = 9;
577 if (precision && ISDIGIT (dot[-1]))
579 /* If a nontrivial width is given, subtract the width of the
580 decimal point and PRECISION digits that will be output
581 later. */
582 char *p = dot;
583 *dot = '\0';
586 --p;
587 while (ISDIGIT (p[-1]));
589 long int lwidth = strtol (p, NULL, 10);
590 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
591 if (1 < width)
593 p += (*p == '0');
594 sec_prefix_len = p - pformat;
595 int w_d = (decimal_point_len < width
596 ? width - decimal_point_len
597 : 0);
598 if (1 < w_d)
600 int w = w_d - precision;
601 if (1 < w)
603 char *dst = pformat;
604 for (char const *src = dst; src < p; src++)
606 if (*src == '-')
607 frac_left_adjust = true;
608 else
609 *dst++ = *src;
611 sec_prefix_len =
612 (dst - pformat
613 + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
620 int divisor = 1;
621 for (int i = precision; i < 9; i++)
622 divisor *= 10;
623 int frac_sec = arg.tv_nsec / divisor;
624 int int_len;
626 if (TYPE_SIGNED (time_t))
628 bool minus_zero = false;
629 if (arg.tv_sec < 0 && arg.tv_nsec != 0)
631 int frac_sec_modulus = 1000000000 / divisor;
632 frac_sec = (frac_sec_modulus - frac_sec
633 - (arg.tv_nsec % divisor != 0));
634 arg.tv_sec += (frac_sec != 0);
635 minus_zero = (arg.tv_sec == 0);
637 int_len = (minus_zero
638 ? out_minus_zero (pformat, sec_prefix_len)
639 : out_int (pformat, sec_prefix_len, arg.tv_sec));
641 else
642 int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
644 if (precision)
646 int prec = (precision < 9 ? precision : 9);
647 int trailing_prec = precision - prec;
648 int ilen = (int_len < 0 ? 0 : int_len);
649 int trailing_width = (ilen < width && decimal_point_len < width - ilen
650 ? width - ilen - decimal_point_len - prec
651 : 0);
652 printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
653 trailing_width, trailing_prec, 0);
657 /* Print the context information of FILENAME, and return true iff the
658 context could not be obtained. */
659 static bool ATTRIBUTE_WARN_UNUSED_RESULT
660 out_file_context (char *pformat, size_t prefix_len, char const *filename)
662 char *scontext;
663 bool fail = false;
665 if ((follow_links
666 ? getfilecon (filename, &scontext)
667 : lgetfilecon (filename, &scontext)) < 0)
669 error (0, errno, _("failed to get security context of %s"),
670 quote (filename));
671 scontext = NULL;
672 fail = true;
674 strcpy (pformat + prefix_len, "s");
675 printf (pformat, (scontext ? scontext : "?"));
676 if (scontext)
677 freecon (scontext);
678 return fail;
681 /* Print statfs info. Return zero upon success, nonzero upon failure. */
682 static bool ATTRIBUTE_WARN_UNUSED_RESULT
683 print_statfs (char *pformat, size_t prefix_len, unsigned int m,
684 char const *filename,
685 void const *data)
687 STRUCT_STATVFS const *statfsbuf = data;
688 bool fail = false;
690 switch (m)
692 case 'n':
693 out_string (pformat, prefix_len, filename);
694 break;
696 case 'i':
698 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
699 uintmax_t fsid = statfsbuf->f_fsid;
700 #else
701 typedef unsigned int fsid_word;
702 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
703 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
704 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
705 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
707 /* Assume a little-endian word order, as that is compatible
708 with glibc's statvfs implementation. */
709 uintmax_t fsid = 0;
710 int words = sizeof statfsbuf->f_fsid / sizeof *p;
711 int i;
712 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
714 uintmax_t u = p[words - 1 - i];
715 fsid |= u << (i * CHAR_BIT * sizeof *p);
717 #endif
718 out_uint_x (pformat, prefix_len, fsid);
720 break;
722 case 'l':
723 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
724 break;
725 case 't':
726 #if HAVE_STRUCT_STATXFS_F_TYPE
727 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
728 #else
729 fputc ('?', stdout);
730 #endif
731 break;
732 case 'T':
733 out_string (pformat, prefix_len, human_fstype (statfsbuf));
734 break;
735 case 'b':
736 out_int (pformat, prefix_len, statfsbuf->f_blocks);
737 break;
738 case 'f':
739 out_int (pformat, prefix_len, statfsbuf->f_bfree);
740 break;
741 case 'a':
742 out_int (pformat, prefix_len, statfsbuf->f_bavail);
743 break;
744 case 's':
745 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
746 break;
747 case 'S':
749 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
750 if (! frsize)
751 frsize = statfsbuf->f_bsize;
752 out_uint (pformat, prefix_len, frsize);
754 break;
755 case 'c':
756 out_uint (pformat, prefix_len, statfsbuf->f_files);
757 break;
758 case 'd':
759 out_int (pformat, prefix_len, statfsbuf->f_ffree);
760 break;
761 default:
762 fputc ('?', stdout);
763 break;
765 return fail;
768 /* Return any bind mounted source for a path.
769 The caller should not free the returned buffer.
770 Return NULL if no bind mount found. */
771 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
772 find_bind_mount (char const * name)
774 char const * bind_mount = NULL;
776 static struct mount_entry *mount_list;
777 static bool tried_mount_list = false;
778 if (!tried_mount_list) /* attempt/warn once per process. */
780 if (!(mount_list = read_file_system_list (false)))
781 error (0, errno, "%s", _("cannot read table of mounted file systems"));
782 tried_mount_list = true;
785 struct mount_entry *me;
786 for (me = mount_list; me; me = me->me_next)
788 if (me->me_dummy && me->me_devname[0] == '/'
789 && STREQ (me->me_mountdir, name))
791 struct stat name_stats;
792 struct stat dev_stats;
794 if (stat (name, &name_stats) == 0
795 && stat (me->me_devname, &dev_stats) == 0
796 && SAME_INODE (name_stats, dev_stats))
798 bind_mount = me->me_devname;
799 break;
804 return bind_mount;
807 /* Print mount point. Return zero upon success, nonzero upon failure. */
808 static bool ATTRIBUTE_WARN_UNUSED_RESULT
809 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
810 const struct stat *statp)
813 char const *np = "?", *bp = NULL;
814 char *mp = NULL;
815 bool fail = true;
817 /* Look for bind mounts first. Note we output the immediate alias,
818 rather than further resolving to a base device mount point. */
819 if (follow_links || !S_ISLNK (statp->st_mode))
821 char *resolved = canonicalize_file_name (filename);
822 if (!resolved)
824 error (0, errno, _("failed to canonicalize %s"), quote (filename));
825 goto print_mount_point;
827 bp = find_bind_mount (resolved);
828 free (resolved);
829 if (bp)
831 fail = false;
832 goto print_mount_point;
836 /* If there is no direct bind mount, then navigate
837 back up the tree looking for a device change.
838 Note we don't detect if any of the directory components
839 are bind mounted to the same device, but that's OK
840 since we've not directly queried them. */
841 if ((mp = find_mount_point (filename, statp)))
843 /* This dir might be bind mounted to another device,
844 so we resolve the bound source in that case also. */
845 bp = find_bind_mount (mp);
846 fail = false;
849 print_mount_point:
851 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
852 free (mp);
853 return fail;
856 /* Map a TS with negative TS.tv_nsec to {0,0}. */
857 static inline struct timespec
858 neg_to_zero (struct timespec ts)
860 if (0 <= ts.tv_nsec)
861 return ts;
862 struct timespec z = {0, 0};
863 return z;
866 /* Print stat info. Return zero upon success, nonzero upon failure. */
867 static bool
868 print_stat (char *pformat, size_t prefix_len, unsigned int m,
869 char const *filename, void const *data)
871 struct stat *statbuf = (struct stat *) data;
872 struct passwd *pw_ent;
873 struct group *gw_ent;
874 bool fail = false;
876 switch (m)
878 case 'n':
879 out_string (pformat, prefix_len, filename);
880 break;
881 case 'N':
882 out_string (pformat, prefix_len, quote (filename));
883 if (S_ISLNK (statbuf->st_mode))
885 char *linkname = areadlink_with_size (filename, statbuf->st_size);
886 if (linkname == NULL)
888 error (0, errno, _("cannot read symbolic link %s"),
889 quote (filename));
890 return true;
892 printf (" -> ");
893 out_string (pformat, prefix_len, quote (linkname));
894 free (linkname);
896 break;
897 case 'd':
898 out_uint (pformat, prefix_len, statbuf->st_dev);
899 break;
900 case 'D':
901 out_uint_x (pformat, prefix_len, statbuf->st_dev);
902 break;
903 case 'i':
904 out_uint (pformat, prefix_len, statbuf->st_ino);
905 break;
906 case 'a':
907 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
908 break;
909 case 'A':
910 out_string (pformat, prefix_len, human_access (statbuf));
911 break;
912 case 'f':
913 out_uint_x (pformat, prefix_len, statbuf->st_mode);
914 break;
915 case 'F':
916 out_string (pformat, prefix_len, file_type (statbuf));
917 break;
918 case 'h':
919 out_uint (pformat, prefix_len, statbuf->st_nlink);
920 break;
921 case 'u':
922 out_uint (pformat, prefix_len, statbuf->st_uid);
923 break;
924 case 'U':
925 setpwent ();
926 pw_ent = getpwuid (statbuf->st_uid);
927 out_string (pformat, prefix_len,
928 pw_ent ? pw_ent->pw_name : "UNKNOWN");
929 break;
930 case 'g':
931 out_uint (pformat, prefix_len, statbuf->st_gid);
932 break;
933 case 'G':
934 setgrent ();
935 gw_ent = getgrgid (statbuf->st_gid);
936 out_string (pformat, prefix_len,
937 gw_ent ? gw_ent->gr_name : "UNKNOWN");
938 break;
939 case 't':
940 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
941 break;
942 case 'm':
943 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
944 break;
945 case 'T':
946 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
947 break;
948 case 's':
949 out_uint (pformat, prefix_len, statbuf->st_size);
950 break;
951 case 'B':
952 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
953 break;
954 case 'b':
955 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
956 break;
957 case 'o':
958 out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
959 break;
960 case 'w':
962 struct timespec t = get_stat_birthtime (statbuf);
963 if (t.tv_nsec < 0)
964 out_string (pformat, prefix_len, "-");
965 else
966 out_string (pformat, prefix_len, human_time (t));
968 break;
969 case 'W':
970 out_epoch_sec (pformat, prefix_len, statbuf,
971 neg_to_zero (get_stat_birthtime (statbuf)));
972 break;
973 case 'x':
974 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
975 break;
976 case 'X':
977 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
978 break;
979 case 'y':
980 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
981 break;
982 case 'Y':
983 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
984 break;
985 case 'z':
986 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
987 break;
988 case 'Z':
989 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
990 break;
991 case 'C':
992 fail |= out_file_context (pformat, prefix_len, filename);
993 break;
994 default:
995 fputc ('?', stdout);
996 break;
998 return fail;
1001 /* Output a single-character \ escape. */
1003 static void
1004 print_esc_char (char c)
1006 switch (c)
1008 case 'a': /* Alert. */
1009 c ='\a';
1010 break;
1011 case 'b': /* Backspace. */
1012 c ='\b';
1013 break;
1014 case 'e': /* Escape. */
1015 c ='\x1B';
1016 break;
1017 case 'f': /* Form feed. */
1018 c ='\f';
1019 break;
1020 case 'n': /* New line. */
1021 c ='\n';
1022 break;
1023 case 'r': /* Carriage return. */
1024 c ='\r';
1025 break;
1026 case 't': /* Horizontal tab. */
1027 c ='\t';
1028 break;
1029 case 'v': /* Vertical tab. */
1030 c ='\v';
1031 break;
1032 case '"':
1033 case '\\':
1034 break;
1035 default:
1036 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
1037 break;
1039 putchar (c);
1042 /* Print the information specified by the format string, FORMAT,
1043 calling PRINT_FUNC for each %-directive encountered.
1044 Return zero upon success, nonzero upon failure. */
1045 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1046 print_it (char const *format, char const *filename,
1047 bool (*print_func) (char *, size_t, unsigned int,
1048 char const *, void const *),
1049 void const *data)
1051 bool fail = false;
1053 /* Add 2 to accommodate our conversion of the stat `%s' format string
1054 to the longer printf `%llu' one. */
1055 enum
1057 MAX_ADDITIONAL_BYTES =
1058 (MAX (sizeof PRIdMAX,
1059 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
1060 - 1)
1062 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1063 char *dest = xmalloc (n_alloc);
1064 char const *b;
1065 for (b = format; *b; b++)
1067 switch (*b)
1069 case '%':
1071 size_t len = strspn (b + 1, printf_flags);
1072 char const *fmt_char = b + len + 1;
1073 fmt_char += strspn (fmt_char, digits);
1074 if (*fmt_char == '.')
1075 fmt_char += 1 + strspn (fmt_char + 1, digits);
1076 len = fmt_char - (b + 1);
1077 unsigned int fmt_code = *fmt_char;
1078 memcpy (dest, b, len + 1);
1080 b = fmt_char;
1081 switch (fmt_code)
1083 case '\0':
1084 --b;
1085 /* fall through */
1086 case '%':
1087 if (0 < len)
1089 dest[len + 1] = *fmt_char;
1090 dest[len + 2] = '\0';
1091 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
1092 quotearg_colon (dest));
1094 putchar ('%');
1095 break;
1096 default:
1097 fail |= print_func (dest, len + 1, fmt_code, filename, data);
1098 break;
1100 break;
1103 case '\\':
1104 if ( ! interpret_backslash_escapes)
1106 putchar ('\\');
1107 break;
1109 ++b;
1110 if (isodigit (*b))
1112 int esc_value = octtobin (*b);
1113 int esc_length = 1; /* number of octal digits */
1114 for (++b; esc_length < 3 && isodigit (*b);
1115 ++esc_length, ++b)
1117 esc_value = esc_value * 8 + octtobin (*b);
1119 putchar (esc_value);
1120 --b;
1122 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
1124 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
1125 /* A hexadecimal \xhh escape sequence must have
1126 1 or 2 hex. digits. */
1127 ++b;
1128 if (isxdigit (to_uchar (b[1])))
1130 ++b;
1131 esc_value = esc_value * 16 + hextobin (*b);
1133 putchar (esc_value);
1135 else if (*b == '\0')
1137 error (0, 0, _("warning: backslash at end of format"));
1138 putchar ('\\');
1139 /* Arrange to exit the loop. */
1140 --b;
1142 else
1144 print_esc_char (*b);
1146 break;
1148 default:
1149 putchar (*b);
1150 break;
1153 free (dest);
1155 fputs (trailing_delim, stdout);
1157 return fail;
1160 /* Stat the file system and print what we find. */
1161 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1162 do_statfs (char const *filename, bool terse, char const *format)
1164 STRUCT_STATVFS statfsbuf;
1166 if (STREQ (filename, "-"))
1168 error (0, 0, _("using %s to denote standard input does not work"
1169 " in file system mode"), quote (filename));
1170 return false;
1173 if (STATFS (filename, &statfsbuf) != 0)
1175 error (0, errno, _("cannot read file system information for %s"),
1176 quote (filename));
1177 return false;
1180 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1181 return ! fail;
1184 /* stat the file and print what we find */
1185 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1186 do_stat (char const *filename, bool terse, char const *format,
1187 char const *format2)
1189 struct stat statbuf;
1191 if (STREQ (filename, "-"))
1193 if (fstat (STDIN_FILENO, &statbuf) != 0)
1195 error (0, errno, _("cannot stat standard input"));
1196 return false;
1199 /* We can't use the shorter
1200 (follow_links?stat:lstat) (filename, &statbug)
1201 since stat might be a function-like macro. */
1202 else if ((follow_links
1203 ? stat (filename, &statbuf)
1204 : lstat (filename, &statbuf)) != 0)
1206 error (0, errno, _("cannot stat %s"), quote (filename));
1207 return false;
1210 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1211 format = format2;
1213 bool fail = print_it (format, filename, print_stat, &statbuf);
1214 return ! fail;
1217 /* Return an allocated format string in static storage that
1218 corresponds to whether FS and TERSE options were declared. */
1219 static char *
1220 default_format (bool fs, bool terse, bool device)
1222 char *format;
1223 if (fs)
1225 if (terse)
1226 format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1227 else
1229 /* TRANSLATORS: This string uses format specifiers from
1230 'stat --help' with --file-system, and NOT from printf. */
1231 format = xstrdup (_("\
1232 File: \"%n\"\n\
1233 ID: %-8i Namelen: %-7l Type: %T\n\
1234 Block size: %-10s Fundamental block size: %S\n\
1235 Blocks: Total: %-10b Free: %-10f Available: %a\n\
1236 Inodes: Total: %-10c Free: %d\n\
1237 "));
1240 else /* ! fs */
1242 if (terse)
1244 if (0 < is_selinux_enabled ())
1245 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1246 " %X %Y %Z %W %o %C\n");
1247 else
1248 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1249 " %X %Y %Z %W %o\n");
1251 else
1253 char *temp;
1254 /* TRANSLATORS: This string uses format specifiers from
1255 'stat --help' without --file-system, and NOT from printf. */
1256 format = xstrdup (_("\
1257 File: %N\n\
1258 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1259 "));
1261 temp = format;
1262 if (device)
1264 /* TRANSLATORS: This string uses format specifiers from
1265 'stat --help' without --file-system, and NOT from printf. */
1266 format = xasprintf ("%s%s", format, _("\
1267 Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1268 "));
1270 else
1272 /* TRANSLATORS: This string uses format specifiers from
1273 'stat --help' without --file-system, and NOT from printf. */
1274 format = xasprintf ("%s%s", format, _("\
1275 Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1276 "));
1278 free (temp);
1280 temp = format;
1281 /* TRANSLATORS: This string uses format specifiers from
1282 'stat --help' without --file-system, and NOT from printf. */
1283 format = xasprintf ("%s%s", format, _("\
1284 Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1285 "));
1286 free (temp);
1288 if (0 < is_selinux_enabled ())
1290 temp = format;
1291 /* TRANSLATORS: This string uses format specifiers from
1292 'stat --help' without --file-system, and NOT from printf. */
1293 format = xasprintf ("%s%s", format, _("\
1294 Context: %C\n\
1295 "));
1296 free (temp);
1299 temp = format;
1300 /* TRANSLATORS: This string uses format specifiers from
1301 'stat --help' without --file-system, and NOT from printf. */
1302 format = xasprintf ("%s%s", format, _("\
1303 Access: %x\n\
1304 Modify: %y\n\
1305 Change: %z\n\
1306 Birth: %w\n\
1307 "));
1308 free (temp);
1311 return format;
1314 void
1315 usage (int status)
1317 if (status != EXIT_SUCCESS)
1318 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1319 program_name);
1320 else
1322 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1323 fputs (_("\
1324 Display file or file system status.\n\
1326 -L, --dereference follow links\n\
1327 -f, --file-system display file system status instead of file status\n\
1328 "), stdout);
1329 fputs (_("\
1330 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1331 output a newline after each use of FORMAT\n\
1332 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1333 and do not output a mandatory trailing newline.\n\
1334 If you want a newline, include \\n in FORMAT\n\
1335 -t, --terse print the information in terse form\n\
1336 "), stdout);
1337 fputs (HELP_OPTION_DESCRIPTION, stdout);
1338 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1340 fputs (_("\n\
1341 The valid format sequences for files (without --file-system):\n\
1343 %a Access rights in octal\n\
1344 %A Access rights in human readable form\n\
1345 %b Number of blocks allocated (see %B)\n\
1346 %B The size in bytes of each block reported by %b\n\
1347 %C SELinux security context string\n\
1348 "), stdout);
1349 fputs (_("\
1350 %d Device number in decimal\n\
1351 %D Device number in hex\n\
1352 %f Raw mode in hex\n\
1353 %F File type\n\
1354 %g Group ID of owner\n\
1355 %G Group name of owner\n\
1356 "), stdout);
1357 fputs (_("\
1358 %h Number of hard links\n\
1359 %i Inode number\n\
1360 %m Mount point\n\
1361 %n File name\n\
1362 %N Quoted file name with dereference if symbolic link\n\
1363 %o I/O block size\n\
1364 %s Total size, in bytes\n\
1365 %t Major device type in hex\n\
1366 %T Minor device type in hex\n\
1367 "), stdout);
1368 fputs (_("\
1369 %u User ID of owner\n\
1370 %U User name of owner\n\
1371 %w Time of file birth, human-readable; - if unknown\n\
1372 %W Time of file birth, seconds since Epoch; 0 if unknown\n\
1373 %x Time of last access, human-readable\n\
1374 %X Time of last access, seconds since Epoch\n\
1375 %y Time of last modification, human-readable\n\
1376 %Y Time of last modification, seconds since Epoch\n\
1377 %z Time of last change, human-readable\n\
1378 %Z Time of last change, seconds since Epoch\n\
1380 "), stdout);
1382 fputs (_("\
1383 Valid format sequences for file systems:\n\
1385 %a Free blocks available to non-superuser\n\
1386 %b Total data blocks in file system\n\
1387 %c Total file nodes in file system\n\
1388 %d Free file nodes in file system\n\
1389 %f Free blocks in file system\n\
1390 "), stdout);
1391 fputs (_("\
1392 %i File System ID in hex\n\
1393 %l Maximum length of filenames\n\
1394 %n File name\n\
1395 %s Block size (for faster transfers)\n\
1396 %S Fundamental block size (for block counts)\n\
1397 %t Type in hex\n\
1398 %T Type in human readable form\n\
1399 "), stdout);
1400 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1401 emit_ancillary_info ();
1403 exit (status);
1407 main (int argc, char *argv[])
1409 int c;
1410 int i;
1411 bool fs = false;
1412 bool terse = false;
1413 char *format = NULL;
1414 char *format2;
1415 bool ok = true;
1417 initialize_main (&argc, &argv);
1418 set_program_name (argv[0]);
1419 setlocale (LC_ALL, "");
1420 bindtextdomain (PACKAGE, LOCALEDIR);
1421 textdomain (PACKAGE);
1423 struct lconv const *locale = localeconv ();
1424 decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1425 decimal_point_len = strlen (decimal_point);
1427 atexit (close_stdout);
1429 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1431 switch (c)
1433 case PRINTF_OPTION:
1434 format = optarg;
1435 interpret_backslash_escapes = true;
1436 trailing_delim = "";
1437 break;
1439 case 'c':
1440 format = optarg;
1441 interpret_backslash_escapes = false;
1442 trailing_delim = "\n";
1443 break;
1445 case 'L':
1446 follow_links = true;
1447 break;
1449 case 'f':
1450 fs = true;
1451 break;
1453 case 't':
1454 terse = true;
1455 break;
1457 case_GETOPT_HELP_CHAR;
1459 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1461 default:
1462 usage (EXIT_FAILURE);
1466 if (argc == optind)
1468 error (0, 0, _("missing operand"));
1469 usage (EXIT_FAILURE);
1472 if (format)
1473 format2 = format;
1474 else
1476 format = default_format (fs, terse, false);
1477 format2 = default_format (fs, terse, true);
1480 for (i = optind; i < argc; i++)
1481 ok &= (fs
1482 ? do_statfs (argv[i], terse, format)
1483 : do_stat (argv[i], terse, format, format2));
1485 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);