1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2010 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. */
21 /* Keep this conditional in sync with the similar conditional in
22 ../m4/stat-prog.m4. */
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
28 # define USE_STATVFS 0
33 #include <sys/types.h>
37 # include <sys/statvfs.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>
53 #elif HAVE_OS_H /* BeOS */
56 #include <selinux/selinux.h>
61 #include "areadlink.h"
63 #include "file-type.h"
67 #include "mountlist.h"
70 #include "stat-time.h"
72 #include "find-mount-point.h"
75 # define STRUCT_STATVFS struct statvfs
76 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
77 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
78 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
79 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
81 # define STATFS statvfs
82 # define STATFS_FRSIZE(S) ((S)->f_frsize)
84 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
85 # if HAVE_STRUCT_STATFS_F_NAMELEN
86 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
88 # define STATFS statfs
89 # if HAVE_OS_H /* BeOS */
90 /* BeOS has a statvfs function, but it does not return sensible values
91 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
92 f_fstypename. Use 'struct fs_info' instead. */
93 static int ATTRIBUTE_WARN_UNUSED_RESULT
94 statfs (char const *filename
, struct fs_info
*buf
)
96 dev_t device
= dev_for_path (filename
);
99 errno
= (device
== B_ENTRY_NOT_FOUND
? ENOENT
100 : device
== B_BAD_VALUE
? EINVAL
101 : device
== B_NAME_TOO_LONG
? ENAMETOOLONG
102 : device
== B_NO_MEMORY
? ENOMEM
103 : device
== B_FILE_ERROR
? EIO
107 /* If successful, buf->dev will be == device. */
108 return fs_stat_dev (device
, buf
);
111 # define f_blocks total_blocks
112 # define f_bfree free_blocks
113 # define f_bavail free_blocks
114 # define f_bsize io_size
115 # define f_files total_nodes
116 # define f_ffree free_nodes
117 # define STRUCT_STATVFS struct fs_info
118 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
119 # define STATFS_FRSIZE(S) ((S)->block_size)
121 # define STRUCT_STATVFS struct statfs
122 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
123 # define STATFS_FRSIZE(S) 0
128 # define OUT_NAMEMAX out_uint
130 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
131 # define SB_F_NAMEMAX(S) "*"
132 # define OUT_NAMEMAX out_string
135 #if HAVE_STRUCT_STATVFS_F_BASETYPE
136 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
138 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
139 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
140 # elif HAVE_OS_H /* BeOS */
141 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
145 /* FIXME: these are used by printf.c, too */
146 #define isodigit(c) ('0' <= (c) && (c) <= '7')
147 #define octtobin(c) ((c) - '0')
148 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
149 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
151 #define PROGRAM_NAME "stat"
153 #define AUTHORS proper_name ("Michael Meskes")
157 PRINTF_OPTION
= CHAR_MAX
+ 1
160 static struct option
const long_options
[] =
162 {"context", no_argument
, 0, 'Z'},
163 {"dereference", no_argument
, NULL
, 'L'},
164 {"file-system", no_argument
, NULL
, 'f'},
165 {"format", required_argument
, NULL
, 'c'},
166 {"printf", required_argument
, NULL
, PRINTF_OPTION
},
167 {"terse", no_argument
, NULL
, 't'},
168 {GETOPT_HELP_OPTION_DECL
},
169 {GETOPT_VERSION_OPTION_DECL
},
173 /* Whether to follow symbolic links; True for --dereference (-L). */
174 static bool follow_links
;
176 /* Whether to interpret backslash-escape sequences.
177 True for --printf=FMT, not for --format=FMT (-c). */
178 static bool interpret_backslash_escapes
;
180 /* The trailing delimiter string:
181 "" for --printf=FMT, "\n" for --format=FMT (-c). */
182 static char const *trailing_delim
= "";
184 /* Return the type of the specified file system.
185 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
186 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
187 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
188 Still others have neither and have to get by with f_type (GNU/Linux).
189 But f_type may only exist in statfs (Cygwin). */
190 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
191 human_fstype (STRUCT_STATVFS
const *statfsbuf
)
193 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
194 return statfsbuf
->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
;
196 switch (statfsbuf
->f_type
)
198 # if defined __linux__
200 /* Compare with what's in libc:
201 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
202 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
203 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
204 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
206 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
207 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
209 diff -u sym_stat sym_libc
212 /* Also compare with the list in "man 2 statfs" using the
213 fs-magic-compare make target. */
215 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
216 statements must be followed by a hexadecimal constant in
217 a comment. The S_MAGIC_... name and constant are automatically
218 combined to produce the #define directives in fs.h. */
220 case S_MAGIC_ADFS
: /* 0xADF5 */
222 case S_MAGIC_AFFS
: /* 0xADFF */
224 case S_MAGIC_AFS
: /* 0x5346414F */
226 case S_MAGIC_ANON_INODE_FS
: /* 0x09041934 */
227 return "anon-inode FS";
228 case S_MAGIC_AUTOFS
: /* 0x0187 */
230 case S_MAGIC_BEFS
: /* 0x42465331 */
232 case S_MAGIC_BFS
: /* 0x1BADFACE */
234 case S_MAGIC_BINFMT_MISC
: /* 0x42494E4D */
235 return "binfmt_misc";
236 case S_MAGIC_BTRFS
: /* 0x9123683E */
238 case S_MAGIC_CGROUP
: /* 0x0027E0EB */
240 case S_MAGIC_CIFS
: /* 0xFF534D42 */
242 case S_MAGIC_CODA
: /* 0x73757245 */
244 case S_MAGIC_COH
: /* 0x012FF7B7 */
246 case S_MAGIC_CRAMFS
: /* 0x28CD3D45 */
248 case S_MAGIC_CRAMFS_WEND
: /* 0x453DCD28 */
249 return "cramfs-wend";
250 case S_MAGIC_DEBUGFS
: /* 0x64626720 */
252 case S_MAGIC_DEVFS
: /* 0x1373 */
254 case S_MAGIC_DEVPTS
: /* 0x1CD1 */
256 case S_MAGIC_EFS
: /* 0x00414A53 */
258 case S_MAGIC_EXT
: /* 0x137D */
260 case S_MAGIC_EXT2
: /* 0xEF53 */
262 case S_MAGIC_EXT2_OLD
: /* 0xEF51 */
264 case S_MAGIC_FAT
: /* 0x4006 */
266 case S_MAGIC_FUSEBLK
: /* 0x65735546 */
268 case S_MAGIC_FUSECTL
: /* 0x65735543 */
270 case S_MAGIC_FUTEXFS
: /* 0x0BAD1DEA */
272 case S_MAGIC_GFS
: /* 0x1161970 */
274 case S_MAGIC_HFS
: /* 0x4244 */
276 case S_MAGIC_HPFS
: /* 0xF995E849 */
278 case S_MAGIC_HUGETLBFS
: /* 0x958458F6 */
280 case S_MAGIC_INOTIFYFS
: /* 0x2BAD1DEA */
282 case S_MAGIC_ISOFS
: /* 0x9660 */
284 case S_MAGIC_ISOFS_R_WIN
: /* 0x4004 */
286 case S_MAGIC_ISOFS_WIN
: /* 0x4000 */
288 case S_MAGIC_JFFS
: /* 0x07C0 */
290 case S_MAGIC_JFFS2
: /* 0x72B6 */
292 case S_MAGIC_JFS
: /* 0x3153464A */
294 case S_MAGIC_KAFS
: /* 0x6B414653 */
296 case S_MAGIC_LUSTRE
: /* 0x0BD00BD0 */
298 case S_MAGIC_MINIX
: /* 0x137F */
300 case S_MAGIC_MINIX_30
: /* 0x138F */
301 return "minix (30 char.)";
302 case S_MAGIC_MINIX_V2
: /* 0x2468 */
304 case S_MAGIC_MINIX_V2_30
: /* 0x2478 */
305 return "minix v2 (30 char.)";
306 case S_MAGIC_MINIX_V3
: /* 0x4D5A */
308 case S_MAGIC_MSDOS
: /* 0x4D44 */
310 case S_MAGIC_NCP
: /* 0x564C */
312 case S_MAGIC_NFS
: /* 0x6969 */
314 case S_MAGIC_NFSD
: /* 0x6E667364 */
316 case S_MAGIC_NILFS
: /* 0x3434 */
318 case S_MAGIC_NTFS
: /* 0x5346544E */
320 case S_MAGIC_OPENPROM
: /* 0x9FA1 */
322 case S_MAGIC_OCFS2
: /* 0x7461636f */
324 case S_MAGIC_PROC
: /* 0x9FA0 */
326 case S_MAGIC_QNX4
: /* 0x002F */
328 case S_MAGIC_RAMFS
: /* 0x858458F6 */
330 case S_MAGIC_REISERFS
: /* 0x52654973 */
332 case S_MAGIC_ROMFS
: /* 0x7275 */
334 case S_MAGIC_RPC_PIPEFS
: /* 0x67596969 */
336 case S_MAGIC_SECURITYFS
: /* 0x73636673 */
338 case S_MAGIC_SELINUX
: /* 0xF97CFF8C */
340 case S_MAGIC_SMB
: /* 0x517B */
342 case S_MAGIC_SOCKFS
: /* 0x534F434B */
344 case S_MAGIC_SQUASHFS
: /* 0x73717368 */
346 case S_MAGIC_SYSFS
: /* 0x62656572 */
348 case S_MAGIC_SYSV2
: /* 0x012FF7B6 */
350 case S_MAGIC_SYSV4
: /* 0x012FF7B5 */
352 case S_MAGIC_TMPFS
: /* 0x01021994 */
354 case S_MAGIC_UDF
: /* 0x15013346 */
356 case S_MAGIC_UFS
: /* 0x00011954 */
358 case S_MAGIC_UFS_BYTESWAPPED
: /* 0x54190100 */
360 case S_MAGIC_USBDEVFS
: /* 0x9FA2 */
362 case S_MAGIC_VXFS
: /* 0xA501FCF5 */
364 case S_MAGIC_XENFS
: /* 0xABBA1974 */
366 case S_MAGIC_XENIX
: /* 0x012FF7B4 */
368 case S_MAGIC_XFS
: /* 0x58465342 */
370 case S_MAGIC_XIAFS
: /* 0x012FD16D */
431 unsigned long int type
= statfsbuf
->f_type
;
432 static char buf
[sizeof "UNKNOWN (0x%lx)" - 3
433 + (sizeof type
* CHAR_BIT
+ 3) / 4];
434 sprintf (buf
, "UNKNOWN (0x%lx)", type
);
441 static char * ATTRIBUTE_WARN_UNUSED_RESULT
442 human_access (struct stat
const *statbuf
)
444 static char modebuf
[12];
445 filemodestring (statbuf
, modebuf
);
450 static char * ATTRIBUTE_WARN_UNUSED_RESULT
451 human_time (struct timespec t
)
453 static char str
[MAX (INT_BUFSIZE_BOUND (intmax_t),
454 (INT_STRLEN_BOUND (int) /* YYYY */
455 + 1 /* because YYYY might equal INT_MAX + 1900 */
456 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
457 struct tm
const *tm
= localtime (&t
.tv_sec
);
459 return timetostr (t
.tv_sec
, str
);
460 nstrftime (str
, sizeof str
, "%Y-%m-%d %H:%M:%S.%N %z", tm
, 0, t
.tv_nsec
);
465 out_string (char *pformat
, size_t prefix_len
, char const *arg
)
467 strcpy (pformat
+ prefix_len
, "s");
468 printf (pformat
, arg
);
471 out_int (char *pformat
, size_t prefix_len
, intmax_t arg
)
473 strcpy (pformat
+ prefix_len
, PRIdMAX
);
474 printf (pformat
, arg
);
477 out_uint (char *pformat
, size_t prefix_len
, uintmax_t arg
)
479 strcpy (pformat
+ prefix_len
, PRIuMAX
);
480 printf (pformat
, arg
);
483 out_uint_o (char *pformat
, size_t prefix_len
, uintmax_t arg
)
485 strcpy (pformat
+ prefix_len
, PRIoMAX
);
486 printf (pformat
, arg
);
489 out_uint_x (char *pformat
, size_t prefix_len
, uintmax_t arg
)
491 strcpy (pformat
+ prefix_len
, PRIxMAX
);
492 printf (pformat
, arg
);
495 /* Very specialized function (modifies FORMAT), just so as to avoid
496 duplicating this code between both print_statfs and print_stat.
497 Return zero upon success, nonzero upon failure. */
498 static bool ATTRIBUTE_WARN_UNUSED_RESULT
499 out_file_context (char const *filename
, char *pformat
, size_t prefix_len
)
505 ? getfilecon (filename
, &scontext
)
506 : lgetfilecon (filename
, &scontext
)) < 0)
508 error (0, errno
, _("failed to get security context of %s"),
513 strcpy (pformat
+ prefix_len
, "s");
514 printf (pformat
, (scontext
? scontext
: "?"));
520 /* Print statfs info. Return zero upon success, nonzero upon failure. */
521 static bool ATTRIBUTE_WARN_UNUSED_RESULT
522 print_statfs (char *pformat
, size_t prefix_len
, char m
, char const *filename
,
525 STRUCT_STATVFS
const *statfsbuf
= data
;
531 out_string (pformat
, prefix_len
, filename
);
536 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
537 uintmax_t fsid
= statfsbuf
->f_fsid
;
539 typedef unsigned int fsid_word
;
540 verify (alignof (STRUCT_STATVFS
) % alignof (fsid_word
) == 0);
541 verify (offsetof (STRUCT_STATVFS
, f_fsid
) % alignof (fsid_word
) == 0);
542 verify (sizeof statfsbuf
->f_fsid
% alignof (fsid_word
) == 0);
543 fsid_word
const *p
= (fsid_word
*) &statfsbuf
->f_fsid
;
545 /* Assume a little-endian word order, as that is compatible
546 with glibc's statvfs implementation. */
548 int words
= sizeof statfsbuf
->f_fsid
/ sizeof *p
;
550 for (i
= 0; i
< words
&& i
* sizeof *p
< sizeof fsid
; i
++)
552 uintmax_t u
= p
[words
- 1 - i
];
553 fsid
|= u
<< (i
* CHAR_BIT
* sizeof *p
);
556 out_uint_x (pformat
, prefix_len
, fsid
);
561 OUT_NAMEMAX (pformat
, prefix_len
, SB_F_NAMEMAX (statfsbuf
));
564 #if HAVE_STRUCT_STATXFS_F_TYPE
565 out_uint_x (pformat
, prefix_len
, statfsbuf
->f_type
);
571 out_string (pformat
, prefix_len
, human_fstype (statfsbuf
));
574 out_int (pformat
, prefix_len
, statfsbuf
->f_blocks
);
577 out_int (pformat
, prefix_len
, statfsbuf
->f_bfree
);
580 out_int (pformat
, prefix_len
, statfsbuf
->f_bavail
);
583 out_uint (pformat
, prefix_len
, statfsbuf
->f_bsize
);
587 uintmax_t frsize
= STATFS_FRSIZE (statfsbuf
);
589 frsize
= statfsbuf
->f_bsize
;
590 out_uint (pformat
, prefix_len
, frsize
);
594 out_uint (pformat
, prefix_len
, statfsbuf
->f_files
);
597 out_int (pformat
, prefix_len
, statfsbuf
->f_ffree
);
600 fail
|= out_file_context (filename
, pformat
, prefix_len
);
609 /* Return any bind mounted source for a path.
610 The caller should not free the returned buffer.
611 Return NULL if no bind mount found. */
612 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
613 find_bind_mount (char const * name
)
615 char const * bind_mount
= NULL
;
617 static struct mount_entry
*mount_list
;
618 static bool tried_mount_list
= false;
619 if (!tried_mount_list
) /* attempt/warn once per process. */
621 if (!(mount_list
= read_file_system_list (false)))
622 error (0, errno
, "%s", _("cannot read table of mounted file systems"));
623 tried_mount_list
= true;
626 struct mount_entry
*me
;
627 for (me
= mount_list
; me
; me
= me
->me_next
)
629 if (me
->me_dummy
&& me
->me_devname
[0] == '/'
630 && STREQ (me
->me_mountdir
, name
))
632 struct stat name_stats
;
633 struct stat dev_stats
;
635 if (stat (name
, &name_stats
) == 0
636 && stat (me
->me_devname
, &dev_stats
) == 0
637 && SAME_INODE (name_stats
, dev_stats
))
639 bind_mount
= me
->me_devname
;
648 /* Print mount point. Return zero upon success, nonzero upon failure. */
649 static bool ATTRIBUTE_WARN_UNUSED_RESULT
650 out_mount_point (char const *filename
, char *pformat
, size_t prefix_len
,
651 const struct stat
*statp
)
654 char const *np
= "?", *bp
= NULL
;
658 /* Look for bind mounts first. Note we output the immediate alias,
659 rather than further resolving to a base device mount point. */
660 if (follow_links
|| !S_ISLNK (statp
->st_mode
))
662 char *resolved
= canonicalize_file_name (filename
);
665 error (0, errno
, _("failed to canonicalize %s"), quote (filename
));
666 goto print_mount_point
;
668 bp
= find_bind_mount (resolved
);
673 goto print_mount_point
;
677 /* If there is no direct bind mount, then navigate
678 back up the tree looking for a device change.
679 Note we don't detect if any of the directory components
680 are bind mounted to the same device, but that's OK
681 since we've not directly queried them. */
682 if ((mp
= find_mount_point (filename
, statp
)))
684 /* This dir might be bind mounted to another device,
685 so we resolve the bound source in that case also. */
686 bp
= find_bind_mount (mp
);
692 out_string (pformat
, prefix_len
, bp
? bp
: mp
? mp
: np
);
697 /* Print stat info. Return zero upon success, nonzero upon failure. */
699 print_stat (char *pformat
, size_t prefix_len
, char m
,
700 char const *filename
, void const *data
)
702 struct stat
*statbuf
= (struct stat
*) data
;
703 struct passwd
*pw_ent
;
704 struct group
*gw_ent
;
710 out_string (pformat
, prefix_len
, filename
);
713 out_string (pformat
, prefix_len
, quote (filename
));
714 if (S_ISLNK (statbuf
->st_mode
))
716 char *linkname
= areadlink_with_size (filename
, statbuf
->st_size
);
717 if (linkname
== NULL
)
719 error (0, errno
, _("cannot read symbolic link %s"),
724 out_string (pformat
, prefix_len
, quote (linkname
));
729 out_uint (pformat
, prefix_len
, statbuf
->st_dev
);
732 out_uint_x (pformat
, prefix_len
, statbuf
->st_dev
);
735 out_uint (pformat
, prefix_len
, statbuf
->st_ino
);
738 out_uint_o (pformat
, prefix_len
, statbuf
->st_mode
& CHMOD_MODE_BITS
);
741 out_string (pformat
, prefix_len
, human_access (statbuf
));
744 out_uint_x (pformat
, prefix_len
, statbuf
->st_mode
);
747 out_string (pformat
, prefix_len
, file_type (statbuf
));
750 out_uint (pformat
, prefix_len
, statbuf
->st_nlink
);
753 out_uint (pformat
, prefix_len
, statbuf
->st_uid
);
757 pw_ent
= getpwuid (statbuf
->st_uid
);
758 out_string (pformat
, prefix_len
,
759 pw_ent
? pw_ent
->pw_name
: "UNKNOWN");
762 out_uint (pformat
, prefix_len
, statbuf
->st_gid
);
766 gw_ent
= getgrgid (statbuf
->st_gid
);
767 out_string (pformat
, prefix_len
,
768 gw_ent
? gw_ent
->gr_name
: "UNKNOWN");
771 out_uint_x (pformat
, prefix_len
, major (statbuf
->st_rdev
));
774 fail
|= out_mount_point (filename
, pformat
, prefix_len
, statbuf
);
777 out_uint_x (pformat
, prefix_len
, minor (statbuf
->st_rdev
));
780 out_uint (pformat
, prefix_len
, statbuf
->st_size
);
783 out_uint (pformat
, prefix_len
, ST_NBLOCKSIZE
);
786 out_uint (pformat
, prefix_len
, ST_NBLOCKS (*statbuf
));
789 out_uint (pformat
, prefix_len
, statbuf
->st_blksize
);
792 out_string (pformat
, prefix_len
, human_time (get_stat_atime (statbuf
)));
795 if (TYPE_SIGNED (time_t))
796 out_int (pformat
, prefix_len
, statbuf
->st_atime
);
798 out_uint (pformat
, prefix_len
, statbuf
->st_atime
);
801 out_string (pformat
, prefix_len
, human_time (get_stat_mtime (statbuf
)));
804 if (TYPE_SIGNED (time_t))
805 out_int (pformat
, prefix_len
, statbuf
->st_mtime
);
807 out_uint (pformat
, prefix_len
, statbuf
->st_mtime
);
810 out_string (pformat
, prefix_len
, human_time (get_stat_ctime (statbuf
)));
813 if (TYPE_SIGNED (time_t))
814 out_int (pformat
, prefix_len
, statbuf
->st_ctime
);
816 out_uint (pformat
, prefix_len
, statbuf
->st_ctime
);
819 fail
|= out_file_context (filename
, pformat
, prefix_len
);
828 /* Output a single-character \ escape. */
831 print_esc_char (char c
)
835 case 'a': /* Alert. */
838 case 'b': /* Backspace. */
841 case 'e': /* Escape. */
844 case 'f': /* Form feed. */
847 case 'n': /* New line. */
850 case 'r': /* Carriage return. */
853 case 't': /* Horizontal tab. */
856 case 'v': /* Vertical tab. */
863 error (0, 0, _("warning: unrecognized escape `\\%c'"), c
);
869 /* Print the information specified by the format string, FORMAT,
870 calling PRINT_FUNC for each %-directive encountered.
871 Return zero upon success, nonzero upon failure. */
872 static bool ATTRIBUTE_WARN_UNUSED_RESULT
873 print_it (char const *format
, char const *filename
,
874 bool (*print_func
) (char *, size_t, char, char const *, void const *),
879 /* Add 2 to accommodate our conversion of the stat `%s' format string
880 to the longer printf `%llu' one. */
883 MAX_ADDITIONAL_BYTES
=
884 (MAX (sizeof PRIdMAX
,
885 MAX (sizeof PRIoMAX
, MAX (sizeof PRIuMAX
, sizeof PRIxMAX
)))
888 size_t n_alloc
= strlen (format
) + MAX_ADDITIONAL_BYTES
+ 1;
889 char *dest
= xmalloc (n_alloc
);
891 for (b
= format
; *b
; b
++)
897 size_t len
= strspn (b
+ 1, "#-+.I 0123456789");
898 char const *fmt_char
= b
+ len
+ 1;
899 memcpy (dest
, b
, len
+ 1);
910 dest
[len
+ 1] = *fmt_char
;
911 dest
[len
+ 2] = '\0';
912 error (EXIT_FAILURE
, 0, _("%s: invalid directive"),
913 quotearg_colon (dest
));
918 fail
|= print_func (dest
, len
+ 1, *fmt_char
, filename
, data
);
925 if ( ! interpret_backslash_escapes
)
933 int esc_value
= octtobin (*b
);
934 int esc_length
= 1; /* number of octal digits */
935 for (++b
; esc_length
< 3 && isodigit (*b
);
938 esc_value
= esc_value
* 8 + octtobin (*b
);
943 else if (*b
== 'x' && isxdigit (to_uchar (b
[1])))
945 int esc_value
= hextobin (b
[1]); /* Value of \xhh escape. */
946 /* A hexadecimal \xhh escape sequence must have
947 1 or 2 hex. digits. */
949 if (isxdigit (to_uchar (b
[1])))
952 esc_value
= esc_value
* 16 + hextobin (*b
);
958 error (0, 0, _("warning: backslash at end of format"));
960 /* Arrange to exit the loop. */
976 fputs (trailing_delim
, stdout
);
981 /* Stat the file system and print what we find. */
982 static bool ATTRIBUTE_WARN_UNUSED_RESULT
983 do_statfs (char const *filename
, bool terse
, char const *format
)
985 STRUCT_STATVFS statfsbuf
;
987 if (STREQ (filename
, "-"))
989 error (0, 0, _("using %s to denote standard input does not work"
990 " in file system mode"), quote (filename
));
994 if (STATFS (filename
, &statfsbuf
) != 0)
996 error (0, errno
, _("cannot read file system information for %s"),
1004 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
1006 " ID: %-8i Namelen: %-7l Type: %T\n"
1007 "Block size: %-10s Fundamental block size: %S\n"
1008 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1009 "Inodes: Total: %-10c Free: %d\n");
1012 bool fail
= print_it (format
, filename
, print_statfs
, &statfsbuf
);
1016 /* stat the file and print what we find */
1017 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1018 do_stat (char const *filename
, bool terse
, char const *format
)
1020 struct stat statbuf
;
1022 if (STREQ (filename
, "-"))
1024 if (fstat (STDIN_FILENO
, &statbuf
) != 0)
1026 error (0, errno
, _("cannot stat standard input"));
1030 /* We can't use the shorter
1031 (follow_links?stat:lstat) (filename, &statbug)
1032 since stat might be a function-like macro. */
1033 else if ((follow_links
1034 ? stat (filename
, &statbuf
)
1035 : lstat (filename
, &statbuf
)) != 0)
1037 error (0, errno
, _("cannot stat %s"), quote (filename
));
1045 format
= "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
1049 /* Temporary hack to match original output until conditional
1051 if (S_ISBLK (statbuf
.st_mode
) || S_ISCHR (statbuf
.st_mode
))
1055 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
1056 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
1057 " Device type: %t,%T\n"
1058 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
1059 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
1065 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
1066 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
1067 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
1068 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
1072 bool fail
= print_it (format
, filename
, print_stat
, &statbuf
);
1079 if (status
!= EXIT_SUCCESS
)
1080 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
1084 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
1086 Display file or file system status.\n\
1088 -L, --dereference follow links\n\
1089 -f, --file-system display file system status instead of file status\n\
1092 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1093 output a newline after each use of FORMAT\n\
1094 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1095 and do not output a mandatory trailing newline.\n\
1096 If you want a newline, include \\n in FORMAT\n\
1097 -t, --terse print the information in terse form\n\
1099 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
1100 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
1103 The valid format sequences for files (without --file-system):\n\
1105 %a Access rights in octal\n\
1106 %A Access rights in human readable form\n\
1107 %b Number of blocks allocated (see %B)\n\
1108 %B The size in bytes of each block reported by %b\n\
1109 %C SELinux security context string\n\
1112 %d Device number in decimal\n\
1113 %D Device number in hex\n\
1114 %f Raw mode in hex\n\
1116 %g Group ID of owner\n\
1117 %G Group name of owner\n\
1120 %h Number of hard links\n\
1124 %N Quoted file name with dereference if symbolic link\n\
1125 %o I/O block size\n\
1126 %s Total size, in bytes\n\
1127 %t Major device type in hex\n\
1128 %T Minor device type in hex\n\
1131 %u User ID of owner\n\
1132 %U User name of owner\n\
1133 %x Time of last access\n\
1134 %X Time of last access as seconds since Epoch\n\
1135 %y Time of last modification\n\
1136 %Y Time of last modification as seconds since Epoch\n\
1137 %z Time of last change\n\
1138 %Z Time of last change as seconds since Epoch\n\
1143 Valid format sequences for file systems:\n\
1145 %a Free blocks available to non-superuser\n\
1146 %b Total data blocks in file system\n\
1147 %c Total file nodes in file system\n\
1148 %d Free file nodes in file system\n\
1149 %f Free blocks in file system\n\
1150 %C SELinux security context string\n\
1153 %i File System ID in hex\n\
1154 %l Maximum length of filenames\n\
1156 %s Block size (for faster transfers)\n\
1157 %S Fundamental block size (for block counts)\n\
1159 %T Type in human readable form\n\
1161 printf (USAGE_BUILTIN_WARNING
, PROGRAM_NAME
);
1162 emit_ancillary_info ();
1168 main (int argc
, char *argv
[])
1174 char *format
= NULL
;
1177 initialize_main (&argc
, &argv
);
1178 set_program_name (argv
[0]);
1179 setlocale (LC_ALL
, "");
1180 bindtextdomain (PACKAGE
, LOCALEDIR
);
1181 textdomain (PACKAGE
);
1183 atexit (close_stdout
);
1185 while ((c
= getopt_long (argc
, argv
, "c:fLt", long_options
, NULL
)) != -1)
1191 interpret_backslash_escapes
= true;
1192 trailing_delim
= "";
1197 interpret_backslash_escapes
= false;
1198 trailing_delim
= "\n";
1202 follow_links
= true;
1213 case_GETOPT_HELP_CHAR
;
1215 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
1218 usage (EXIT_FAILURE
);
1224 error (0, 0, _("missing operand"));
1225 usage (EXIT_FAILURE
);
1228 for (i
= optind
; i
< argc
; i
++)
1230 ? do_statfs (argv
[i
], terse
, format
)
1231 : do_stat (argv
[i
], terse
, format
));
1233 exit (ok
? EXIT_SUCCESS
: EXIT_FAILURE
);