/* stat.c -- display file or file system status Copyright (C) 2001-2008 Free Software Foundation, Inc. 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 3 of the License, 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, see . Written by Michael Meskes. */ #include /* 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 #include #include #include #include #if USE_STATVFS # include #elif HAVE_SYS_VFS_H # include #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 # include # 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 # include # include # endif #elif HAVE_OS_H /* BeOS */ # include #endif #include #include "system.h" #include "error.h" #include "filemode.h" #include "file-type.h" #include "fs.h" #include "getopt.h" #include "quote.h" #include "quotearg.h" #include "stat-time.h" #include "strftime.h" #include "areadlink.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 proper_name ("Michael Meskes") enum { PRINTF_OPTION = CHAR_MAX + 1 }; static struct option const long_options[] = { {"context", no_argument, 0, 'Z'}, {"dereference", no_argument, NULL, 'L'}, {"file-system", no_argument, NULL, 'f'}, /* obsolete and undocumented alias: FIXME: remove in 2009 */ {"filesystem", 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} }; /* Whether to follow symbolic links; True for --dereference (-L). */ static bool follow_links; /* 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__ /* Compare with what's in libc: f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \ | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \ -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \ | sort > sym_libc perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \ -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \ | sort > sym_stat diff -u sym_stat sym_libc */ /* Also sync from the list in "man 2 statfs". */ /* 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_ADFS: /* 0xADF5 */ return "adfs"; case S_MAGIC_AFFS: /* 0xADFF */ return "affs"; case S_MAGIC_AUTOFS: /* 0x187 */ return "autofs"; case S_MAGIC_BEFS: /* 0x42465331 */ return "befs"; case S_MAGIC_BFS: /* 0x1BADFACE */ return "bfs"; case S_MAGIC_BINFMT_MISC: /* 0x42494e4d */ return "binfmt_misc"; case S_MAGIC_CODA: /* 0x73757245 */ return "coda"; case S_MAGIC_COH: /* 0x012FF7B7 */ return "coh"; case S_MAGIC_CRAMFS: /* 0x28CD3D45 */ return "cramfs"; case S_MAGIC_DEVFS: /* 0x1373 */ return "devfs"; case S_MAGIC_DEVPTS: /* 0x1CD1 */ return "devpts"; case S_MAGIC_EFS: /* 0x414A53 */ return "efs"; case S_MAGIC_EXT: /* 0x137D */ return "ext"; case S_MAGIC_EXT2: /* 0xEF53 */ return "ext2/ext3"; case S_MAGIC_EXT2_OLD: /* 0xEF51 */ return "ext2"; case S_MAGIC_FAT: /* 0x4006 */ return "fat"; case S_MAGIC_FUSECTL: /* 0x65735543 */ return "fusectl"; case S_MAGIC_HPFS: /* 0xF995E849 */ return "hpfs"; case S_MAGIC_HUGETLBFS: /* 0x958458f6 */ return "hugetlbfs"; case S_MAGIC_ISOFS: /* 0x9660 */ return "isofs"; case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */ return "isofs"; case S_MAGIC_ISOFS_WIN: /* 0x4000 */ return "isofs"; case S_MAGIC_JFFS2: /* 0x72B6 */ return "jffs2"; case S_MAGIC_JFFS: /* 0x07C0 */ return "jffs"; case S_MAGIC_JFS: /* 0x3153464A */ return "jfs"; 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_NCP: /* 0x564C */ return "novell"; case S_MAGIC_NFS: /* 0x6969 */ return "nfs"; case S_MAGIC_NFSD: /* 0x6E667364 */ return "nfsd"; case S_MAGIC_NTFS: /* 0x5346544E */ return "ntfs"; case S_MAGIC_OPENPROM: /* 0x9fa1 */ return "openprom"; case S_MAGIC_PROC: /* 0x9FA0 */ return "proc"; case S_MAGIC_QNX4: /* 0x002F */ return "qnx4"; case S_MAGIC_RAMFS: /* 0x858458F6 */ return "ramfs"; case S_MAGIC_REISERFS: /* 0x52654973 */ return "reiserfs"; case S_MAGIC_ROMFS: /* 0x7275 */ return "romfs"; case S_MAGIC_SMB: /* 0x517B */ return "smb"; case S_MAGIC_SQUASHFS: /* 0x73717368 */ return "squashfs"; case S_MAGIC_SYSFS: /* 0x62656572 */ return "sysfs"; case S_MAGIC_SYSV2: /* 0x012FF7B6 */ return "sysv2"; case S_MAGIC_SYSV4: /* 0x012FF7B5 */ return "sysv4"; case S_MAGIC_TMPFS: /* 0x1021994 */ return "tmpfs"; case S_MAGIC_UDF: /* 0x15013346 */ return "udf"; case S_MAGIC_UFS: /* 0x00011954 */ return "ufs"; case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */ return "ufs"; case S_MAGIC_USBDEVFS: /* 0x9FA2 */ return "usbdevfs"; case S_MAGIC_VXFS: /* 0xA501FCF5 */ return "vxfs"; case S_MAGIC_XENIX: /* 0x012FF7B4 */ return "xenix"; case S_MAGIC_XFS: /* 0x58465342 */ return "xfs"; case S_MAGIC_XIAFS: /* 0x012FD16D */ return "xia"; # 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 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 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); } /* Very specialized function (modifies FORMAT), just so as to avoid duplicating this code between both print_statfs and print_stat. */ static void out_file_context (char const *filename, char *pformat, size_t prefix_len) { char *scontext; if ((follow_links ? getfilecon (filename, &scontext) : lgetfilecon (filename, &scontext)) < 0) { error (0, errno, _("failed to get security context of %s"), quote (filename)); scontext = NULL; } strcpy (pformat + prefix_len, "s"); printf (pformat, (scontext ? scontext : "?")); if (scontext) freecon (scontext); } /* 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; case 'C': out_file_context (filename, pformat, prefix_len); 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 = areadlink_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; case 'C': out_file_context (filename, pformat, prefix_len); 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 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\ %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\ %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\ %C SELinux security context string\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); emit_bug_reporting_address (); } exit (status); } int main (int argc, char *argv[]) { int c; int i; bool fs = false; bool terse = false; char *format = NULL; bool ok = true; initialize_main (&argc, &argv); set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); atexit (close_stdout); while ((c = getopt_long (argc, argv, "c:fLtZ", 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 'Z': /* FIXME: remove in 2010 */ /* Ignore, for compatibility with distributions that implemented this before upstream. But warn of impending removal. */ error (0, 0, _("the --context (-Z) option is obsolete and will be removed\n" "in a future release")); 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], terse, format)); exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); }