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