diff options
Diffstat (limited to 'src/stat.c')
-rw-r--r-- | src/stat.c | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/src/stat.c b/src/stat.c new file mode 100644 index 0000000..ca84236 --- /dev/null +++ b/src/stat.c @@ -0,0 +1,979 @@ +/* stat.c -- display file or file system status + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Written by Michael Meskes. */ + +#include <config.h> + +/* Keep this conditional in sync with the similar conditional in + ../m4/stat-prog.m4. */ +#if (STAT_STATVFS \ + && (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 <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 +/* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs. + It does have statvfs.h, but shouldn't use it, since it doesn't + HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */ +/* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */ +# include <sys/param.h> +# include <sys/mount.h> +# if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H +/* Ultrix 4.4 needs these for the declaration of struct statfs. */ +# include <netinet/in.h> +# include <nfs/nfs_clnt.h> +# include <nfs/vfs.h> +# endif +#elif HAVE_OS_H /* BeOS */ +# include <fs_info.h> +#endif + +#include "system.h" + +#include "error.h" +#include "filemode.h" +#include "file-type.h" +#include "fs.h" +#include "getopt.h" +#include "inttostr.h" +#include "quote.h" +#include "quotearg.h" +#include "stat-time.h" +#include "strftime.h" +#include "xreadlink.h" + +#define alignof(type) offsetof (struct { char c; type x; }, x) + +#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 +# define STATFS statvfs +# 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 /* BeOS */ +/* BeOS has a statvfs function, but it does not return sensible values + for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and + f_fstypename. Use 'struct fs_info' instead. */ +static int +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; + } + /* If successful, buf->dev will be == device. */ + 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 +# define STATFS_FRSIZE(S) 0 +# endif +#endif + +#ifdef SB_F_NAMEMAX +# define OUT_NAMEMAX out_uint +#else +/* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */ +# 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 /* BeOS */ +# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name +# endif +#endif + +/* FIXME: these are used by printf.c, too */ +#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') + +#define PROGRAM_NAME "stat" + +#define AUTHORS "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'}, + {"filesystem", no_argument, NULL, 'f'}, /* obsolete and undocumented alias */ + {"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} +}; + +char *program_name; + +/* Whether to interpret backslash-escape sequences. + True for --printf=FMT, not for --format=FMT (-c). */ +static bool interpret_backslash_escapes; + +/* The trailing delimiter string: + "" for --printf=FMT, "\n" for --format=FMT (-c). */ +static char const *trailing_delim = ""; + +/* Return the type of the specified file system. + Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris). + Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0). + Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2). + Still others have neither and have to get by with f_type (Linux). + But f_type may only exist in statfs (Cygwin). */ +static char const * +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__ + + /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:' + statements must be followed by a hexadecimal constant in + a comment. The S_MAGIC_... name and constant are automatically + combined to produce the #define directives in fs.h. */ + + case S_MAGIC_AFFS: /* 0xADFF */ + return "affs"; + case S_MAGIC_DEVPTS: /* 0x1CD1 */ + return "devpts"; + case S_MAGIC_EXT: /* 0x137D */ + return "ext"; + case S_MAGIC_EXT2_OLD: /* 0xEF51 */ + return "ext2"; + case S_MAGIC_EXT2: /* 0xEF53 */ + return "ext2/ext3"; + case S_MAGIC_JFS: /* 0x3153464a */ + return "jfs"; + case S_MAGIC_XFS: /* 0x58465342 */ + return "xfs"; + case S_MAGIC_HPFS: /* 0xF995E849 */ + return "hpfs"; + case S_MAGIC_ISOFS: /* 0x9660 */ + return "isofs"; + case S_MAGIC_ISOFS_WIN: /* 0x4000 */ + return "isofs"; + case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */ + return "isofs"; + case S_MAGIC_MINIX: /* 0x137F */ + return "minix"; + case S_MAGIC_MINIX_30: /* 0x138F */ + return "minix (30 char.)"; + case S_MAGIC_MINIX_V2: /* 0x2468 */ + return "minix v2"; + case S_MAGIC_MINIX_V2_30: /* 0x2478 */ + return "minix v2 (30 char.)"; + case S_MAGIC_MSDOS: /* 0x4d44 */ + return "msdos"; + case S_MAGIC_FAT: /* 0x4006 */ + return "fat"; + case S_MAGIC_NCP: /* 0x564c */ + return "novell"; + case S_MAGIC_NFS: /* 0x6969 */ + return "nfs"; + case S_MAGIC_PROC: /* 0x9fa0 */ + return "proc"; + case S_MAGIC_SMB: /* 0x517B */ + return "smb"; + case S_MAGIC_XENIX: /* 0x012FF7B4 */ + return "xenix"; + case S_MAGIC_SYSV4: /* 0x012FF7B5 */ + return "sysv4"; + case S_MAGIC_SYSV2: /* 0x012FF7B6 */ + return "sysv2"; + case S_MAGIC_COH: /* 0x012FF7B7 */ + return "coh"; + case S_MAGIC_UFS: /* 0x00011954 */ + return "ufs"; + case S_MAGIC_XIAFS: /* 0x012FD16D */ + return "xia"; + case S_MAGIC_NTFS: /* 0x5346544e */ + return "ntfs"; + case S_MAGIC_TMPFS: /* 0x1021994 */ + return "tmpfs"; + case S_MAGIC_REISERFS: /* 0x52654973 */ + return "reiserfs"; + case S_MAGIC_CRAMFS: /* 0x28cd3d45 */ + return "cramfs"; + case S_MAGIC_ROMFS: /* 0x7275 */ + return "romfs"; + case S_MAGIC_RAMFS: /* 0x858458f6 */ + return "ramfs"; + case S_MAGIC_SQUASHFS: /* 0x73717368 */ + return "squashfs"; + case S_MAGIC_SYSFS: /* 0x62656572 */ + return "sysfs"; +# 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 * +human_access (struct stat const *statbuf) +{ + static char modebuf[12]; + filemodestring (statbuf, modebuf); + modebuf[10] = 0; + return modebuf; +} + +static char * +human_time (struct timespec t) +{ + static char str[MAX (INT_BUFSIZE_BOUND (intmax_t), + (INT_STRLEN_BOUND (int) /* YYYY */ + + 1 /* because YYYY might equal INT_MAX + 1900 */ + + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))]; + struct tm const *tm = localtime (&t.tv_sec); + if (tm == NULL) + return (TYPE_SIGNED (time_t) + ? imaxtostr (t.tv_sec, str) + : umaxtostr (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 +out_string (char *pformat, size_t prefix_len, char const *arg) +{ + strcpy (pformat + prefix_len, "s"); + printf (pformat, arg); +} +static void +out_int (char *pformat, size_t prefix_len, intmax_t arg) +{ + strcpy (pformat + prefix_len, PRIdMAX); + printf (pformat, arg); +} +static void +out_uint (char *pformat, size_t prefix_len, uintmax_t arg) +{ + strcpy (pformat + prefix_len, PRIuMAX); + printf (pformat, arg); +} +static void +out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg) +{ + strcpy (pformat + prefix_len, PRIoMAX); + printf (pformat, arg); +} +static void +out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg) +{ + strcpy (pformat + prefix_len, PRIxMAX); + printf (pformat, arg); +} + +/* print statfs info */ +static void +print_statfs (char *pformat, size_t prefix_len, char m, char const *filename, + void const *data) +{ + STRUCT_STATVFS const *statfsbuf = data; + + 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; + + /* Assume a little-endian word order, as that is compatible + with glibc's statvfs implementation. */ + 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_int (pformat, prefix_len, statfsbuf->f_files); + break; + case 'd': + out_int (pformat, prefix_len, statfsbuf->f_ffree); + break; + + default: + fputc ('?', stdout); + break; + } +} + +/* print stat info */ +static void +print_stat (char *pformat, size_t prefix_len, char m, + char const *filename, void const *data) +{ + struct stat *statbuf = (struct stat *) data; + struct passwd *pw_ent; + struct group *gw_ent; + + 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 = xreadlink_with_size (filename, statbuf->st_size); + if (linkname == NULL) + { + error (0, errno, _("cannot read symbolic link %s"), + quote (filename)); + return; + } + printf (" -> "); + out_string (pformat, prefix_len, quote (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': + setpwent (); + 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': + setgrent (); + 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 'T': + out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev)); + break; + case 's': + out_uint (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, statbuf->st_blksize); + break; + case 'x': + out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf))); + break; + case 'X': + if (TYPE_SIGNED (time_t)) + out_int (pformat, prefix_len, statbuf->st_atime); + else + out_uint (pformat, prefix_len, statbuf->st_atime); + break; + case 'y': + out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf))); + break; + case 'Y': + if (TYPE_SIGNED (time_t)) + out_int (pformat, prefix_len, statbuf->st_mtime); + else + out_uint (pformat, prefix_len, statbuf->st_mtime); + break; + case 'z': + out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf))); + break; + case 'Z': + if (TYPE_SIGNED (time_t)) + out_int (pformat, prefix_len, statbuf->st_ctime); + else + out_uint (pformat, prefix_len, statbuf->st_ctime); + break; + default: + fputc ('?', stdout); + break; + } +} + +/* Output a single-character \ escape. */ + +static void +print_esc_char (char c) +{ + switch (c) + { + case 'a': /* Alert. */ + c ='\a'; + break; + case 'b': /* Backspace. */ + c ='\b'; + break; + case 'f': /* Form feed. */ + c ='\f'; + break; + case 'n': /* New line. */ + c ='\n'; + break; + case 'r': /* Carriage return. */ + c ='\r'; + break; + case 't': /* Horizontal tab. */ + c ='\t'; + break; + case 'v': /* Vertical tab. */ + c ='\v'; + break; + case '"': + case '\\': + break; + default: + error (0, 0, _("warning: unrecognized escape `\\%c'"), c); + break; + } + putchar (c); +} + +static void +print_it (char const *format, char const *filename, + void (*print_func) (char *, size_t, char, char const *, void const *), + void const *data) +{ + /* Add 2 to accommodate our conversion of the stat `%s' format string + to the longer printf `%llu' one. */ + 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, "#-+.I 0123456789"); + char const *fmt_char = b + len + 1; + memcpy (dest, b, len + 1); + + b = fmt_char; + switch (*fmt_char) + { + case '\0': + --b; + /* fall through */ + 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: + print_func (dest, len + 1, *fmt_char, filename, data); + break; + } + break; + } + + case '\\': + if ( ! interpret_backslash_escapes) + { + putchar ('\\'); + break; + } + ++b; + if (isodigit (*b)) + { + int esc_value = octtobin (*b); + int esc_length = 1; /* number of octal digits */ + 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]); /* Value of \xhh escape. */ + /* A hexadecimal \xhh escape sequence must have + 1 or 2 hex. digits. */ + ++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 ('\\'); + /* Arrange to exit the loop. */ + --b; + } + else + { + print_esc_char (*b); + } + break; + + default: + putchar (*b); + break; + } + } + free (dest); + + fputs (trailing_delim, stdout); +} + +/* Stat the file system and print what we find. */ +static bool +do_statfs (char const *filename, bool terse, char const *format) +{ + STRUCT_STATVFS statfsbuf; + + if (STATFS (filename, &statfsbuf) != 0) + { + error (0, errno, _("cannot read file system information for %s"), + quote (filename)); + return false; + } + + if (format == NULL) + { + format = (terse + ? "%n %i %l %t %s %S %b %f %a %c %d\n" + : " 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"); + } + + print_it (format, filename, print_statfs, &statfsbuf); + return true; +} + +/* stat the file and print what we find */ +static bool +do_stat (char const *filename, bool follow_links, bool terse, + char const *format) +{ + struct stat statbuf; + + if ((follow_links ? stat : lstat) (filename, &statbuf) != 0) + { + error (0, errno, _("cannot stat %s"), quote (filename)); + return false; + } + + if (format == NULL) + { + if (terse) + { + format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"; + } + else + { + /* Temporary hack to match original output until conditional + implemented. */ + if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode)) + { + format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } + else + { + format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } + } + } + print_it (format, filename, print_stat, &statbuf); + return true; +} + +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("Usage: %s [OPTION] FILE...\n"), program_name); + fputs (_("\ +Display file or file system status.\n\ +\n\ + -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\ +"), 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\ + %n File name\n\ + %N Quoted file name with dereference if symbolic link\n\ + %o I/O block size\n\ + %s Total size, in bytes\n\ + %t Major device type in hex\n\ + %T Minor device type in hex\n\ +"), stdout); + fputs (_("\ + %u User ID of owner\n\ + %U User name of owner\n\ + %x Time of last access\n\ + %X Time of last access as seconds since Epoch\n\ + %y Time of last modification\n\ + %Y Time of last modification as seconds since Epoch\n\ + %z Time of last change\n\ + %Z Time of last change as 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 Type in hex\n\ + %T Type in human readable form\n\ +"), stdout); + printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME); + printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + } + exit (status); +} + +int +main (int argc, char *argv[]) +{ + int c; + int i; + bool follow_links = false; + bool fs = false; + bool terse = false; + char *format = NULL; + bool ok = true; + + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + 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); + } + + for (i = optind; i < argc; i++) + ok &= (fs + ? do_statfs (argv[i], terse, format) + : do_stat (argv[i], follow_links, terse, format)); + + exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); +} |