src/stat.c - coreutils-8.23
Global variables defined
Functions defined
Macros defined
Source code
#include <config.h>
#if ((STAT_STATVFS || STAT_STATVFS64) \
&& (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
|| (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
# define USE_STATVFS 1
#else
# define USE_STATVFS 0
#endif
#include <stddef.h>
#include <stdio.h>
#include <stdalign.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#if USE_STATVFS
# include <sys/statvfs.h>
#elif HAVE_SYS_VFS_H
# include <sys/vfs.h>
#elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
# include <sys/param.h>
# include <sys/mount.h>
# if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
# include <netinet/in.h>
# include <nfs/nfs_clnt.h>
# include <nfs/vfs.h>
# endif
#elif HAVE_OS_H # include <fs_info.h>
#endif
#include <selinux/selinux.h>
#include "system.h"
#include "areadlink.h"
#include "error.h"
#include "file-type.h"
#include "filemode.h"
#include "fs.h"
#include "getopt.h"
#include "mountlist.h"
#include "quote.h"
#include "quotearg.h"
#include "stat-size.h"
#include "stat-time.h"
#include "strftime.h"
#include "find-mount-point.h"
#include "xvasprintf.h"
#if USE_STATVFS
# define STRUCT_STATVFS struct statvfs
# define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
# if HAVE_STRUCT_STATVFS_F_NAMEMAX
# define SB_F_NAMEMAX(S) ((S)->f_namemax)
# endif
# if ! STAT_STATVFS && STAT_STATVFS64
# define STATFS statvfs64
# else
# define STATFS statvfs
# endif
# define STATFS_FRSIZE(S) ((S)->f_frsize)
#else
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
# if HAVE_STRUCT_STATFS_F_NAMELEN
# define SB_F_NAMEMAX(S) ((S)->f_namelen)
# endif
# define STATFS statfs
# if HAVE_OS_H static int ATTRIBUTE_WARN_UNUSED_RESULT
statfs (char const *filename, struct fs_info *buf)
{
dev_t device = dev_for_path (filename);
if (device < 0)
{
errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
: device == B_BAD_VALUE ? EINVAL
: device == B_NAME_TOO_LONG ? ENAMETOOLONG
: device == B_NO_MEMORY ? ENOMEM
: device == B_FILE_ERROR ? EIO
: 0);
return -1;
}
return fs_stat_dev (device, buf);
}
# define f_fsid dev
# define f_blocks total_blocks
# define f_bfree free_blocks
# define f_bavail free_blocks
# define f_bsize io_size
# define f_files total_nodes
# define f_ffree free_nodes
# define STRUCT_STATVFS struct fs_info
# define STRUCT_STATXFS_F_FSID_IS_INTEGER true
# define STATFS_FRSIZE(S) ((S)->block_size)
# else
# define STRUCT_STATVFS struct statfs
# define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
# if HAVE_STRUCT_STATFS_F_FRSIZE
# define STATFS_FRSIZE(S) ((S)->f_frsize)
# else
# define STATFS_FRSIZE(S) 0
# endif
# endif
#endif
#ifdef SB_F_NAMEMAX
# define OUT_NAMEMAX out_uint
#else
# define SB_F_NAMEMAX(S) "*"
# define OUT_NAMEMAX out_string
#endif
#if HAVE_STRUCT_STATVFS_F_BASETYPE
# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
#else
# if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
# elif HAVE_OS_H # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
# endif
#endif
#if HAVE_GETATTRAT
# include <attr.h>
# include <sys/nvpair.h>
#endif
FIXME#define isodigit(c) ('0' <= (c) && (c) <= '7')
#define octtobin(c) ((c) - '0')
#define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
(c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
static char const digits[] = "0123456789";
static char const printf_flags[] = "'-+ #0I";
#define PROGRAM_NAME "stat"
#define AUTHORS proper_name ("Michael Meskes")
enum
{
PRINTF_OPTION = CHAR_MAX + 1
};
static struct option const long_options[] =
{
{"dereference", no_argument, NULL, 'L'},
{"file-system", no_argument, NULL, 'f'},
{"format", required_argument, NULL, 'c'},
{"printf", required_argument, NULL, PRINTF_OPTION},
{"terse", no_argument, NULL, 't'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
static bool follow_links;
static bool interpret_backslash_escapes;
static char const *trailing_delim = "";
static char const *decimal_point;
static size_t decimal_point_len;
static char const * ATTRIBUTE_WARN_UNUSED_RESULT
human_fstype (STRUCT_STATVFS const *statfsbuf)
{
#ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
#else
switch (statfsbuf->f_type)
{
# if defined __linux__
case S_MAGIC_ADFS: return "adfs";
case S_MAGIC_AFFS: return "affs";
case S_MAGIC_AFS: return "afs";
case S_MAGIC_ANON_INODE_FS: return "anon-inode FS";
case S_MAGIC_AUFS: FIXME return "aufs";
case S_MAGIC_AUTOFS: return "autofs";
case S_MAGIC_BEFS: return "befs";
case S_MAGIC_BDEVFS: return "bdevfs";
case S_MAGIC_BFS: return "bfs";
case S_MAGIC_BINFMTFS: return "binfmt_misc";
case S_MAGIC_BTRFS: return "btrfs";
case S_MAGIC_CEPH: return "ceph";
case S_MAGIC_CGROUP: return "cgroupfs";
case S_MAGIC_CIFS: return "cifs";
case S_MAGIC_CODA: return "coda";
case S_MAGIC_COH: return "coh";
case S_MAGIC_CONFIGFS: return "configfs";
case S_MAGIC_CRAMFS: return "cramfs";
case S_MAGIC_CRAMFS_WEND: return "cramfs-wend";
case S_MAGIC_DEBUGFS: return "debugfs";
case S_MAGIC_DEVFS: return "devfs";
case S_MAGIC_DEVPTS: return "devpts";
case S_MAGIC_ECRYPTFS: return "ecryptfs";
case S_MAGIC_EFIVARFS: return "efivarfs";
case S_MAGIC_EFS: return "efs";
case S_MAGIC_EXOFS: return "exofs";
case S_MAGIC_EXT: return "ext";
case S_MAGIC_EXT2: return "ext2/ext3";
case S_MAGIC_EXT2_OLD: return "ext2";
case S_MAGIC_F2FS: return "f2fs";
case S_MAGIC_FAT: return "fat";
case S_MAGIC_FHGFS: return "fhgfs";
case S_MAGIC_FUSEBLK: return "fuseblk";
case S_MAGIC_FUSECTL: return "fusectl";
case S_MAGIC_FUTEXFS: return "futexfs";
case S_MAGIC_GFS: return "gfs/gfs2";
case S_MAGIC_GPFS: return "gpfs";
case S_MAGIC_HFS: return "hfs";
case S_MAGIC_HFS_PLUS: return "hfs+";
case S_MAGIC_HFS_X: return "hfsx";
case S_MAGIC_HOSTFS: return "hostfs";
case S_MAGIC_HPFS: return "hpfs";
case S_MAGIC_HUGETLBFS: return "hugetlbfs";
case S_MAGIC_MTD_INODE_FS: return "inodefs";
case S_MAGIC_INOTIFYFS: return "inotifyfs";
case S_MAGIC_ISOFS: return "isofs";
case S_MAGIC_ISOFS_R_WIN: return "isofs";
case S_MAGIC_ISOFS_WIN: return "isofs";
case S_MAGIC_JFFS: return "jffs";
case S_MAGIC_JFFS2: return "jffs2";
case S_MAGIC_JFS: return "jfs";
case S_MAGIC_KAFS: return "k-afs";
case S_MAGIC_LOGFS: return "logfs";
case S_MAGIC_LUSTRE: return "lustre";
case S_MAGIC_MINIX: return "minix";
case S_MAGIC_MINIX_30: return "minix (30 char.)";
case S_MAGIC_MINIX_V2: return "minix v2";
case S_MAGIC_MINIX_V2_30: return "minix v2 (30 char.)";
case S_MAGIC_MINIX_V3: return "minix3";
case S_MAGIC_MQUEUE: return "mqueue";
case S_MAGIC_MSDOS: return "msdos";
case S_MAGIC_NCP: return "novell";
case S_MAGIC_NFS: return "nfs";
case S_MAGIC_NFSD: return "nfsd";
case S_MAGIC_NILFS: return "nilfs";
case S_MAGIC_NTFS: return "ntfs";
case S_MAGIC_OPENPROM: return "openprom";
case S_MAGIC_OCFS2: return "ocfs2";
case S_MAGIC_PANFS: return "panfs";
case S_MAGIC_PIPEFS: FIXME return "pipefs";
case S_MAGIC_PROC: return "proc";
case S_MAGIC_PSTOREFS: return "pstorefs";
case S_MAGIC_QNX4: return "qnx4";
case S_MAGIC_QNX6: return "qnx6";
case S_MAGIC_RAMFS: return "ramfs";
case S_MAGIC_REISERFS: return "reiserfs";
case S_MAGIC_ROMFS: return "romfs";
case S_MAGIC_RPC_PIPEFS: return "rpc_pipefs";
case S_MAGIC_SECURITYFS: return "securityfs";
case S_MAGIC_SELINUX: return "selinux";
case S_MAGIC_SMACK: return "smackfs";
case S_MAGIC_SMB: return "smb";
case S_MAGIC_SNFS: return "snfs";
case S_MAGIC_SOCKFS: return "sockfs";
case S_MAGIC_SQUASHFS: return "squashfs";
case S_MAGIC_SYSFS: return "sysfs";
case S_MAGIC_SYSV2: return "sysv2";
case S_MAGIC_SYSV4: return "sysv4";
case S_MAGIC_TMPFS: return "tmpfs";
case S_MAGIC_UBIFS: return "ubifs";
case S_MAGIC_UDF: return "udf";
case S_MAGIC_UFS: return "ufs";
case S_MAGIC_UFS_BYTESWAPPED: return "ufs";
case S_MAGIC_USBDEVFS: return "usbdevfs";
case S_MAGIC_V9FS: return "v9fs";
case S_MAGIC_VMHGFS: return "vmhgfs";
case S_MAGIC_VXFS: return "vxfs";
case S_MAGIC_VZFS: return "vzfs";
case S_MAGIC_XENFS: return "xenfs";
case S_MAGIC_XENIX: return "xenix";
case S_MAGIC_XFS: return "xfs";
case S_MAGIC_XIAFS: return "xia";
case S_MAGIC_ZFS: return "zfs";
# elif __GNU__
case FSTYPE_UFS:
return "ufs";
case FSTYPE_NFS:
return "nfs";
case FSTYPE_GFS:
return "gfs";
case FSTYPE_LFS:
return "lfs";
case FSTYPE_SYSV:
return "sysv";
case FSTYPE_FTP:
return "ftp";
case FSTYPE_TAR:
return "tar";
case FSTYPE_AR:
return "ar";
case FSTYPE_CPIO:
return "cpio";
case FSTYPE_MSLOSS:
return "msloss";
case FSTYPE_CPM:
return "cpm";
case FSTYPE_HFS:
return "hfs";
case FSTYPE_DTFS:
return "dtfs";
case FSTYPE_GRFS:
return "grfs";
case FSTYPE_TERM:
return "term";
case FSTYPE_DEV:
return "dev";
case FSTYPE_PROC:
return "proc";
case FSTYPE_IFSOCK:
return "ifsock";
case FSTYPE_AFS:
return "afs";
case FSTYPE_DFS:
return "dfs";
case FSTYPE_PROC9:
return "proc9";
case FSTYPE_SOCKET:
return "socket";
case FSTYPE_MISC:
return "misc";
case FSTYPE_EXT2FS:
return "ext2/ext3";
case FSTYPE_HTTP:
return "http";
case FSTYPE_MEMFS:
return "memfs";
case FSTYPE_ISO9660:
return "iso9660";
# endif
default:
{
unsigned long int type = statfsbuf->f_type;
static char buf[sizeof "UNKNOWN (0x%lx)" - 3
+ (sizeof type * CHAR_BIT + 3) / 4];
sprintf (buf, "UNKNOWN (0x%lx)", type);
return buf;
}
}
#endif
}
static char * ATTRIBUTE_WARN_UNUSED_RESULT
human_access (struct stat const *statbuf)
{
static char modebuf[12];
filemodestring (statbuf, modebuf);
modebuf[10] = 0;
return modebuf;
}
static char * ATTRIBUTE_WARN_UNUSED_RESULT
human_time (struct timespec t)
{
static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
(INT_STRLEN_BOUND (int) + 1 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
struct tm const *tm = localtime (&t.tv_sec);
if (tm == NULL)
return timetostr (t.tv_sec, str);
nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
return str;
}
static void
make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
char const *suffix)
{
char *dst = pformat + 1;
char const *src;
char const *srclim = pformat + prefix_len;
for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
if (strchr (allowed_flags, *src))
*dst++ = *src;
while (src < srclim)
*dst++ = *src++;
strcpy (dst, suffix);
}
static void
out_string (char *pformat, size_t prefix_len, char const *arg)
{
make_format (pformat, prefix_len, "-", "s");
printf (pformat, arg);
}
static int
out_int (char *pformat, size_t prefix_len, intmax_t arg)
{
make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
return printf (pformat, arg);
}
static int
out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
{
make_format (pformat, prefix_len, "'-0", PRIuMAX);
return printf (pformat, arg);
}
static void
out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
{
make_format (pformat, prefix_len, "-#0", PRIoMAX);
printf (pformat, arg);
}
static void
out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
{
make_format (pformat, prefix_len, "-#0", PRIxMAX);
printf (pformat, arg);
}
static int
out_minus_zero (char *pformat, size_t prefix_len)
{
make_format (pformat, prefix_len, "'-+ 0", ".0f");
return printf (pformat, -0.25);
}
static void
out_epoch_sec (char *pformat, size_t prefix_len,
struct stat const *statbuf _GL_UNUSED,
struct timespec arg)
{
char *dot = memchr (pformat, '.', prefix_len);
size_t sec_prefix_len = prefix_len;
int width = 0;
int precision = 0;
bool frac_left_adjust = false;
if (dot)
{
sec_prefix_len = dot - pformat;
pformat[prefix_len] = '\0';
if (ISDIGIT (dot[1]))
{
long int lprec = strtol (dot + 1, NULL, 10);
precision = (lprec <= INT_MAX ? lprec : INT_MAX);
}
else
{
precision = 9;
}
if (precision && ISDIGIT (dot[-1]))
{
char *p = dot;
*dot = '\0';
do
--p;
while (ISDIGIT (p[-1]));
long int lwidth = strtol (p, NULL, 10);
width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
if (1 < width)
{
p += (*p == '0');
sec_prefix_len = p - pformat;
int w_d = (decimal_point_len < width
? width - decimal_point_len
: 0);
if (1 < w_d)
{
int w = w_d - precision;
if (1 < w)
{
char *dst = pformat;
for (char const *src = dst; src < p; src++)
{
if (*src == '-')
frac_left_adjust = true;
else
*dst++ = *src;
}
sec_prefix_len =
(dst - pformat
+ (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
}
}
}
}
}
int divisor = 1;
for (int i = precision; i < 9; i++)
divisor *= 10;
int frac_sec = arg.tv_nsec / divisor;
int int_len;
if (TYPE_SIGNED (time_t))
{
bool minus_zero = false;
if (arg.tv_sec < 0 && arg.tv_nsec != 0)
{
int frac_sec_modulus = 1000000000 / divisor;
frac_sec = (frac_sec_modulus - frac_sec
- (arg.tv_nsec % divisor != 0));
arg.tv_sec += (frac_sec != 0);
minus_zero = (arg.tv_sec == 0);
}
int_len = (minus_zero
? out_minus_zero (pformat, sec_prefix_len)
: out_int (pformat, sec_prefix_len, arg.tv_sec));
}
else
int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
if (precision)
{
int prec = (precision < 9 ? precision : 9);
int trailing_prec = precision - prec;
int ilen = (int_len < 0 ? 0 : int_len);
int trailing_width = (ilen < width && decimal_point_len < width - ilen
? width - ilen - decimal_point_len - prec
: 0);
printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
trailing_width, trailing_prec, 0);
}
}
static bool ATTRIBUTE_WARN_UNUSED_RESULT
out_file_context (char *pformat, size_t prefix_len, char const *filename)
{
char *scontext;
bool fail = false;
if ((follow_links
? getfilecon (filename, &scontext)
: lgetfilecon (filename, &scontext)) < 0)
{
error (0, errno, _("failed to get security context of %s"),
quote (filename));
scontext = NULL;
fail = true;
}
strcpy (pformat + prefix_len, "s");
printf (pformat, (scontext ? scontext : "?"));
if (scontext)
freecon (scontext);
return fail;
}
static bool ATTRIBUTE_WARN_UNUSED_RESULT
print_statfs (char *pformat, size_t prefix_len, unsigned int m,
int fd, char const *filename,
void const *data)
{
STRUCT_STATVFS const *statfsbuf = data;
bool fail = false;
switch (m)
{
case 'n':
out_string (pformat, prefix_len, filename);
break;
case 'i':
{
#if STRUCT_STATXFS_F_FSID_IS_INTEGER
uintmax_t fsid = statfsbuf->f_fsid;
#else
typedef unsigned int fsid_word;
verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
uintmax_t fsid = 0;
int words = sizeof statfsbuf->f_fsid / sizeof *p;
int i;
for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
{
uintmax_t u = p[words - 1 - i];
fsid |= u << (i * CHAR_BIT * sizeof *p);
}
#endif
out_uint_x (pformat, prefix_len, fsid);
}
break;
case 'l':
OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
break;
case 't':
#if HAVE_STRUCT_STATXFS_F_TYPE
out_uint_x (pformat, prefix_len, statfsbuf->f_type);
#else
fputc ('?', stdout);
#endif
break;
case 'T':
out_string (pformat, prefix_len, human_fstype (statfsbuf));
break;
case 'b':
out_int (pformat, prefix_len, statfsbuf->f_blocks);
break;
case 'f':
out_int (pformat, prefix_len, statfsbuf->f_bfree);
break;
case 'a':
out_int (pformat, prefix_len, statfsbuf->f_bavail);
break;
case 's':
out_uint (pformat, prefix_len, statfsbuf->f_bsize);
break;
case 'S':
{
uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
if (! frsize)
frsize = statfsbuf->f_bsize;
out_uint (pformat, prefix_len, frsize);
}
break;
case 'c':
out_uint (pformat, prefix_len, statfsbuf->f_files);
break;
case 'd':
out_int (pformat, prefix_len, statfsbuf->f_ffree);
break;
default:
fputc ('?', stdout);
break;
}
return fail;
}
static char const * ATTRIBUTE_WARN_UNUSED_RESULT
find_bind_mount (char const * name)
{
char const * bind_mount = NULL;
static struct mount_entry *mount_list;
static bool tried_mount_list = false;
if (!tried_mount_list) {
if (!(mount_list = read_file_system_list (false)))
error (0, errno, "%s", _("cannot read table of mounted file systems"));
tried_mount_list = true;
}
struct stat name_stats;
if (stat (name, &name_stats) != 0)
return NULL;
struct mount_entry *me;
for (me = mount_list; me; me = me->me_next)
{
if (me->me_dummy && me->me_devname[0] == '/'
&& STREQ (me->me_mountdir, name))
{
struct stat dev_stats;
if (stat (me->me_devname, &dev_stats) == 0
&& SAME_INODE (name_stats, dev_stats))
{
bind_mount = me->me_devname;
break;
}
}
}
return bind_mount;
}
static bool ATTRIBUTE_WARN_UNUSED_RESULT
out_mount_point (char const *filename, char *pformat, size_t prefix_len,
const struct stat *statp)
{
char const *np = "?", *bp = NULL;
char *mp = NULL;
bool fail = true;
if (follow_links || !S_ISLNK (statp->st_mode))
{
char *resolved = canonicalize_file_name (filename);
if (!resolved)
{
error (0, errno, _("failed to canonicalize %s"), quote (filename));
goto print_mount_point;
}
bp = find_bind_mount (resolved);
free (resolved);
if (bp)
{
fail = false;
goto print_mount_point;
}
}
if ((mp = find_mount_point (filename, statp)))
{
bp = find_bind_mount (mp);
fail = false;
}
print_mount_point:
out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
free (mp);
return fail;
}
static struct timespec
get_birthtime (int fd, char const *filename, struct stat const *st)
{
struct timespec ts = get_stat_birthtime (st);
#if HAVE_GETATTRAT
if (ts.tv_nsec < 0)
{
nvlist_t *response;
if ((fd < 0
? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
: fgetattr (fd, XATTR_VIEW_READWRITE, &response))
== 0)
{
uint64_t *val;
uint_t n;
if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
&& 2 <= n
&& val[0] <= TYPE_MAXIMUM (time_t)
&& val[1] < 1000000000 * 2 )
{
ts.tv_sec = val[0];
ts.tv_nsec = val[1];
}
nvlist_free (response);
}
}
#endif
return ts;
}
static inline struct timespec
neg_to_zero (struct timespec ts)
{
if (0 <= ts.tv_nsec)
return ts;
struct timespec z = {0, 0};
return z;
}
static bool
print_stat (char *pformat, size_t prefix_len, unsigned int m,
int fd, char const *filename, void const *data)
{
struct stat *statbuf = (struct stat *) data;
struct passwd *pw_ent;
struct group *gw_ent;
bool fail = false;
switch (m)
{
case 'n':
out_string (pformat, prefix_len, filename);
break;
case 'N':
out_string (pformat, prefix_len, quote (filename));
if (S_ISLNK (statbuf->st_mode))
{
char *linkname = areadlink_with_size (filename, statbuf->st_size);
if (linkname == NULL)
{
error (0, errno, _("cannot read symbolic link %s"),
quote (filename));
return true;
}
printf (" -> ");
out_string (pformat, prefix_len, quote (linkname));
free (linkname);
}
break;
case 'd':
out_uint (pformat, prefix_len, statbuf->st_dev);
break;
case 'D':
out_uint_x (pformat, prefix_len, statbuf->st_dev);
break;
case 'i':
out_uint (pformat, prefix_len, statbuf->st_ino);
break;
case 'a':
out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
break;
case 'A':
out_string (pformat, prefix_len, human_access (statbuf));
break;
case 'f':
out_uint_x (pformat, prefix_len, statbuf->st_mode);
break;
case 'F':
out_string (pformat, prefix_len, file_type (statbuf));
break;
case 'h':
out_uint (pformat, prefix_len, statbuf->st_nlink);
break;
case 'u':
out_uint (pformat, prefix_len, statbuf->st_uid);
break;
case 'U':
pw_ent = getpwuid (statbuf->st_uid);
out_string (pformat, prefix_len,
pw_ent ? pw_ent->pw_name : "UNKNOWN");
break;
case 'g':
out_uint (pformat, prefix_len, statbuf->st_gid);
break;
case 'G':
gw_ent = getgrgid (statbuf->st_gid);
out_string (pformat, prefix_len,
gw_ent ? gw_ent->gr_name : "UNKNOWN");
break;
case 't':
out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
break;
case 'm':
fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
break;
case 'T':
out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
break;
case 's':
out_int (pformat, prefix_len, statbuf->st_size);
break;
case 'B':
out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
break;
case 'b':
out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
break;
case 'o':
out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
break;
case 'w':
{
struct timespec t = get_birthtime (fd, filename, statbuf);
if (t.tv_nsec < 0)
out_string (pformat, prefix_len, "-");
else
out_string (pformat, prefix_len, human_time (t));
}
break;
case 'W':
out_epoch_sec (pformat, prefix_len, statbuf,
neg_to_zero (get_birthtime (fd, filename, statbuf)));
break;
case 'x':
out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
break;
case 'X':
out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
break;
case 'y':
out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
break;
case 'Y':
out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
break;
case 'z':
out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
break;
case 'Z':
out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
break;
case 'C':
fail |= out_file_context (pformat, prefix_len, filename);
break;
default:
fputc ('?', stdout);
break;
}
return fail;
}
static void
print_esc_char (char c)
{
switch (c)
{
case 'a': c ='\a';
break;
case 'b': c ='\b';
break;
case 'e': c ='\x1B';
break;
case 'f': c ='\f';
break;
case 'n': c ='\n';
break;
case 'r': c ='\r';
break;
case 't': c ='\t';
break;
case 'v': c ='\v';
break;
case '"':
case '\\':
break;
default:
error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
break;
}
putchar (c);
}
static bool ATTRIBUTE_WARN_UNUSED_RESULT
print_it (char const *format, int fd, char const *filename,
bool (*print_func) (char *, size_t, unsigned int,
int, char const *, void const *),
void const *data)
{
bool fail = false;
enum
{
MAX_ADDITIONAL_BYTES =
(MAX (sizeof PRIdMAX,
MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
- 1)
};
size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
char *dest = xmalloc (n_alloc);
char const *b;
for (b = format; *b; b++)
{
switch (*b)
{
case '%':
{
size_t len = strspn (b + 1, printf_flags);
char const *fmt_char = b + len + 1;
fmt_char += strspn (fmt_char, digits);
if (*fmt_char == '.')
fmt_char += 1 + strspn (fmt_char + 1, digits);
len = fmt_char - (b + 1);
unsigned int fmt_code = *fmt_char;
memcpy (dest, b, len + 1);
b = fmt_char;
switch (fmt_code)
{
case '\0':
--b;
case '%':
if (0 < len)
{
dest[len + 1] = *fmt_char;
dest[len + 2] = '\0';
error (EXIT_FAILURE, 0, _("%s: invalid directive"),
quotearg_colon (dest));
}
putchar ('%');
break;
default:
fail |= print_func (dest, len + 1, fmt_code,
fd, filename, data);
break;
}
break;
}
case '\\':
if ( ! interpret_backslash_escapes)
{
putchar ('\\');
break;
}
++b;
if (isodigit (*b))
{
int esc_value = octtobin (*b);
int esc_length = 1; for (++b; esc_length < 3 && isodigit (*b);
++esc_length, ++b)
{
esc_value = esc_value * 8 + octtobin (*b);
}
putchar (esc_value);
--b;
}
else if (*b == 'x' && isxdigit (to_uchar (b[1])))
{
int esc_value = hextobin (b[1]); ++b;
if (isxdigit (to_uchar (b[1])))
{
++b;
esc_value = esc_value * 16 + hextobin (*b);
}
putchar (esc_value);
}
else if (*b == '\0')
{
error (0, 0, _("warning: backslash at end of format"));
putchar ('\\');
--b;
}
else
{
print_esc_char (*b);
}
break;
default:
putchar (*b);
break;
}
}
free (dest);
fputs (trailing_delim, stdout);
return fail;
}
static bool ATTRIBUTE_WARN_UNUSED_RESULT
do_statfs (char const *filename, char const *format)
{
STRUCT_STATVFS statfsbuf;
if (STREQ (filename, "-"))
{
error (0, 0, _("using %s to denote standard input does not work"
" in file system mode"), quote (filename));
return false;
}
if (STATFS (filename, &statfsbuf) != 0)
{
error (0, errno, _("cannot read file system information for %s"),
quote (filename));
return false;
}
bool fail = print_it (format, -1, filename, print_statfs, &statfsbuf);
return ! fail;
}
static bool ATTRIBUTE_WARN_UNUSED_RESULT
do_stat (char const *filename, char const *format,
char const *format2)
{
int fd = STREQ (filename, "-") ? 0 : -1;
struct stat statbuf;
if (0 <= fd)
{
if (fstat (fd, &statbuf) != 0)
{
error (0, errno, _("cannot stat standard input"));
return false;
}
}
else if ((follow_links
? stat (filename, &statbuf)
: lstat (filename, &statbuf)) != 0)
{
error (0, errno, _("cannot stat %s"), quote (filename));
return false;
}
if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
format = format2;
bool fail = print_it (format, fd, filename, print_stat, &statbuf);
return ! fail;
}
static char *
default_format (bool fs, bool terse, bool device)
{
char *format;
if (fs)
{
if (terse)
format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
else
{
format = xstrdup (_(" File: \"%n\"\n"
" ID: %-8i Namelen: %-7l Type: %T\n"
"Block size: %-10s Fundamental block size: %S\n"
"Blocks: Total: %-10b Free: %-10f Available: %a\n"
"Inodes: Total: %-10c Free: %d\n"));
}
}
else {
if (terse)
{
if (0 < is_selinux_enabled ())
format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
" %X %Y %Z %W %o %C\n");
else
format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
" %X %Y %Z %W %o\n");
}
else
{
char *temp;
format = xstrdup (_("\
File: %N\n\
Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
"));
temp = format;
if (device)
{
format = xasprintf ("%s%s", format, _("\
" "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
"));
}
else
{
format = xasprintf ("%s%s", format, _("\
" "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
"));
}
free (temp);
temp = format;
format = xasprintf ("%s%s", format, _("\
" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
"));
free (temp);
if (0 < is_selinux_enabled ())
{
temp = format;
format = xasprintf ("%s%s", format, _("Context: %C\n"));
free (temp);
}
temp = format;
format = xasprintf ("%s%s", format,
_("Access: %x\n"
"Modify: %y\n"
"Change: %z\n"
" Birth: %w\n"));
free (temp);
}
}
return format;
}
void
usage (int status)
{
if (status != EXIT_SUCCESS)
emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
fputs (_("\
Display file or file system status.\n\
"), stdout);
emit_mandatory_arg_note ();
fputs (_("\
-L, --dereference follow links\n\
-f, --file-system display file system status instead of file status\n\
"), stdout);
fputs (_("\
-c --format=FORMAT use the specified FORMAT instead of the default;\n\
output a newline after each use of FORMAT\n\
--printf=FORMAT like --format, but interpret backslash escapes,\n\
and do not output a mandatory trailing newline;\n\
if you want a newline, include \\n in FORMAT\n\
-t, --terse print the information in terse form\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\n\
The valid format sequences for files (without --file-system):\n\
\n\
%a access rights in octal\n\
%A access rights in human readable form\n\
%b number of blocks allocated (see %B)\n\
%B the size in bytes of each block reported by %b\n\
%C SELinux security context string\n\
"), stdout);
fputs (_("\
%d device number in decimal\n\
%D device number in hex\n\
%f raw mode in hex\n\
%F file type\n\
%g group ID of owner\n\
%G group name of owner\n\
"), stdout);
fputs (_("\
%h number of hard links\n\
%i inode number\n\
%m mount point\n\
%n file name\n\
%N quoted file name with dereference if symbolic link\n\
%o optimal I/O transfer size hint\n\
%s total size, in bytes\n\
%t major device type in hex, for character/block device special files\n\
%T minor device type in hex, for character/block device special files\n\
"), stdout);
fputs (_("\
%u user ID of owner\n\
%U user name of owner\n\
%w time of file birth, human-readable; - if unknown\n\
%W time of file birth, seconds since Epoch; 0 if unknown\n\
%x time of last access, human-readable\n\
%X time of last access, seconds since Epoch\n\
%y time of last data modification, human-readable\n\
%Y time of last data modification, seconds since Epoch\n\
%z time of last status change, human-readable\n\
%Z time of last status change, seconds since Epoch\n\
\n\
"), stdout);
fputs (_("\
Valid format sequences for file systems:\n\
\n\
%a free blocks available to non-superuser\n\
%b total data blocks in file system\n\
%c total file nodes in file system\n\
%d free file nodes in file system\n\
%f free blocks in file system\n\
"), stdout);
fputs (_("\
%i file system ID in hex\n\
%l maximum length of filenames\n\
%n file name\n\
%s block size (for faster transfers)\n\
%S fundamental block size (for block counts)\n\
%t file system type in hex\n\
%T file system type in human readable form\n\
"), stdout);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
emit_ancillary_info ();
}
exit (status);
}
int
main (int argc, char *argv[])
{
int c;
int i;
bool fs = false;
bool terse = false;
char *format = NULL;
char *format2;
bool ok = true;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
struct lconv const *locale = localeconv ();
decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
decimal_point_len = strlen (decimal_point);
atexit (close_stdout);
while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
{
switch (c)
{
case PRINTF_OPTION:
format = optarg;
interpret_backslash_escapes = true;
trailing_delim = "";
break;
case 'c':
format = optarg;
interpret_backslash_escapes = false;
trailing_delim = "\n";
break;
case 'L':
follow_links = true;
break;
case 'f':
fs = true;
break;
case 't':
terse = true;
break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
usage (EXIT_FAILURE);
}
}
if (argc == optind)
{
error (0, 0, _("missing operand"));
usage (EXIT_FAILURE);
}
if (format)
format2 = format;
else
{
format = default_format (fs, terse, false);
format2 = default_format (fs, terse, true);
}
for (i = optind; i < argc; i++)
ok &= (fs
? do_statfs (argv[i], format)
: do_stat (argv[i], format, format2));
exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
}