ls: --color now highlights files with capabilities, too
[coreutils.git] / src / stat.c
blob491c190a4db9073957b86666aab045489d596896
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2008 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 Written by Michael Meskes. */
19 #include <config.h>
21 /* Keep this conditional in sync with the similar conditional in
22 ../m4/stat-prog.m4. */
23 #if (STAT_STATVFS \
24 && (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
25 || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
26 # define USE_STATVFS 1
27 #else
28 # define USE_STATVFS 0
29 #endif
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #if USE_STATVFS
37 # include <sys/statvfs.h>
38 #elif HAVE_SYS_VFS_H
39 # include <sys/vfs.h>
40 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
41 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
42 It does have statvfs.h, but shouldn't use it, since it doesn't
43 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
44 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
45 # include <sys/param.h>
46 # include <sys/mount.h>
47 # if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
48 /* Ultrix 4.4 needs these for the declaration of struct statfs. */
49 # include <netinet/in.h>
50 # include <nfs/nfs_clnt.h>
51 # include <nfs/vfs.h>
52 # endif
53 #elif HAVE_OS_H /* BeOS */
54 # include <fs_info.h>
55 #endif
56 #include <selinux/selinux.h>
58 #include "system.h"
60 #include "error.h"
61 #include "filemode.h"
62 #include "file-type.h"
63 #include "fs.h"
64 #include "getopt.h"
65 #include "quote.h"
66 #include "quotearg.h"
67 #include "stat-time.h"
68 #include "strftime.h"
69 #include "areadlink.h"
71 #define alignof(type) offsetof (struct { char c; type x; }, x)
73 #if USE_STATVFS
74 # define STRUCT_STATVFS struct statvfs
75 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
76 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
77 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
78 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
79 # endif
80 # define STATFS statvfs
81 # define STATFS_FRSIZE(S) ((S)->f_frsize)
82 #else
83 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
84 # if HAVE_STRUCT_STATFS_F_NAMELEN
85 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
86 # endif
87 # define STATFS statfs
88 # if HAVE_OS_H /* BeOS */
89 /* BeOS has a statvfs function, but it does not return sensible values
90 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
91 f_fstypename. Use 'struct fs_info' instead. */
92 static int
93 statfs (char const *filename, struct fs_info *buf)
95 dev_t device = dev_for_path (filename);
96 if (device < 0)
98 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
99 : device == B_BAD_VALUE ? EINVAL
100 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
101 : device == B_NO_MEMORY ? ENOMEM
102 : device == B_FILE_ERROR ? EIO
103 : 0);
104 return -1;
106 /* If successful, buf->dev will be == device. */
107 return fs_stat_dev (device, buf);
109 # define f_fsid dev
110 # define f_blocks total_blocks
111 # define f_bfree free_blocks
112 # define f_bavail free_blocks
113 # define f_bsize io_size
114 # define f_files total_nodes
115 # define f_ffree free_nodes
116 # define STRUCT_STATVFS struct fs_info
117 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
118 # define STATFS_FRSIZE(S) ((S)->block_size)
119 # else
120 # define STRUCT_STATVFS struct statfs
121 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
122 # define STATFS_FRSIZE(S) 0
123 # endif
124 #endif
126 #ifdef SB_F_NAMEMAX
127 # define OUT_NAMEMAX out_uint
128 #else
129 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
130 # define SB_F_NAMEMAX(S) "*"
131 # define OUT_NAMEMAX out_string
132 #endif
134 #if HAVE_STRUCT_STATVFS_F_BASETYPE
135 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
136 #else
137 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
138 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
139 # elif HAVE_OS_H /* BeOS */
140 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
141 # endif
142 #endif
144 /* FIXME: these are used by printf.c, too */
145 #define isodigit(c) ('0' <= (c) && (c) <= '7')
146 #define octtobin(c) ((c) - '0')
147 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
148 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
150 #define PROGRAM_NAME "stat"
152 #define AUTHORS proper_name ("Michael Meskes")
154 enum
156 PRINTF_OPTION = CHAR_MAX + 1
159 static struct option const long_options[] =
161 {"context", no_argument, 0, 'Z'},
162 {"dereference", no_argument, NULL, 'L'},
163 {"file-system", no_argument, NULL, 'f'},
165 /* obsolete and undocumented alias: FIXME: remove in 2009 */
166 {"filesystem", no_argument, NULL, 'f'},
168 {"format", required_argument, NULL, 'c'},
169 {"printf", required_argument, NULL, PRINTF_OPTION},
170 {"terse", no_argument, NULL, 't'},
171 {GETOPT_HELP_OPTION_DECL},
172 {GETOPT_VERSION_OPTION_DECL},
173 {NULL, 0, NULL, 0}
176 /* Whether to follow symbolic links; True for --dereference (-L). */
177 static bool follow_links;
179 /* Whether to interpret backslash-escape sequences.
180 True for --printf=FMT, not for --format=FMT (-c). */
181 static bool interpret_backslash_escapes;
183 /* The trailing delimiter string:
184 "" for --printf=FMT, "\n" for --format=FMT (-c). */
185 static char const *trailing_delim = "";
187 /* Return the type of the specified file system.
188 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
189 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
190 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
191 Still others have neither and have to get by with f_type (Linux).
192 But f_type may only exist in statfs (Cygwin). */
193 static char const *
194 human_fstype (STRUCT_STATVFS const *statfsbuf)
196 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
197 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
198 #else
199 switch (statfsbuf->f_type)
201 # if defined __linux__
203 /* Compare with what's in libc:
204 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
205 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
206 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
207 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
208 | sort > sym_libc
209 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
210 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
211 | sort > sym_stat
212 diff -u sym_stat sym_libc
215 /* Also sync from the list in "man 2 statfs". */
217 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
218 statements must be followed by a hexadecimal constant in
219 a comment. The S_MAGIC_... name and constant are automatically
220 combined to produce the #define directives in fs.h. */
222 case S_MAGIC_ADFS: /* 0xADF5 */
223 return "adfs";
224 case S_MAGIC_AFFS: /* 0xADFF */
225 return "affs";
226 case S_MAGIC_AUTOFS: /* 0x187 */
227 return "autofs";
228 case S_MAGIC_BEFS: /* 0x42465331 */
229 return "befs";
230 case S_MAGIC_BFS: /* 0x1BADFACE */
231 return "bfs";
232 case S_MAGIC_BINFMT_MISC: /* 0x42494e4d */
233 return "binfmt_misc";
234 case S_MAGIC_CODA: /* 0x73757245 */
235 return "coda";
236 case S_MAGIC_COH: /* 0x012FF7B7 */
237 return "coh";
238 case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
239 return "cramfs";
240 case S_MAGIC_DEVFS: /* 0x1373 */
241 return "devfs";
242 case S_MAGIC_DEVPTS: /* 0x1CD1 */
243 return "devpts";
244 case S_MAGIC_EFS: /* 0x414A53 */
245 return "efs";
246 case S_MAGIC_EXT: /* 0x137D */
247 return "ext";
248 case S_MAGIC_EXT2: /* 0xEF53 */
249 return "ext2/ext3";
250 case S_MAGIC_EXT2_OLD: /* 0xEF51 */
251 return "ext2";
252 case S_MAGIC_FAT: /* 0x4006 */
253 return "fat";
254 case S_MAGIC_FUSECTL: /* 0x65735543 */
255 return "fusectl";
256 case S_MAGIC_HPFS: /* 0xF995E849 */
257 return "hpfs";
258 case S_MAGIC_HUGETLBFS: /* 0x958458f6 */
259 return "hugetlbfs";
260 case S_MAGIC_ISOFS: /* 0x9660 */
261 return "isofs";
262 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
263 return "isofs";
264 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
265 return "isofs";
266 case S_MAGIC_JFFS2: /* 0x72B6 */
267 return "jffs2";
268 case S_MAGIC_JFFS: /* 0x07C0 */
269 return "jffs";
270 case S_MAGIC_JFS: /* 0x3153464A */
271 return "jfs";
272 case S_MAGIC_MINIX: /* 0x137F */
273 return "minix";
274 case S_MAGIC_MINIX_30: /* 0x138F */
275 return "minix (30 char.)";
276 case S_MAGIC_MINIX_V2: /* 0x2468 */
277 return "minix v2";
278 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
279 return "minix v2 (30 char.)";
280 case S_MAGIC_MSDOS: /* 0x4D44 */
281 return "msdos";
282 case S_MAGIC_NCP: /* 0x564C */
283 return "novell";
284 case S_MAGIC_NFS: /* 0x6969 */
285 return "nfs";
286 case S_MAGIC_NFSD: /* 0x6E667364 */
287 return "nfsd";
288 case S_MAGIC_NTFS: /* 0x5346544E */
289 return "ntfs";
290 case S_MAGIC_OPENPROM: /* 0x9fa1 */
291 return "openprom";
292 case S_MAGIC_PROC: /* 0x9FA0 */
293 return "proc";
294 case S_MAGIC_QNX4: /* 0x002F */
295 return "qnx4";
296 case S_MAGIC_RAMFS: /* 0x858458F6 */
297 return "ramfs";
298 case S_MAGIC_REISERFS: /* 0x52654973 */
299 return "reiserfs";
300 case S_MAGIC_ROMFS: /* 0x7275 */
301 return "romfs";
302 case S_MAGIC_SMB: /* 0x517B */
303 return "smb";
304 case S_MAGIC_SQUASHFS: /* 0x73717368 */
305 return "squashfs";
306 case S_MAGIC_SYSFS: /* 0x62656572 */
307 return "sysfs";
308 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
309 return "sysv2";
310 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
311 return "sysv4";
312 case S_MAGIC_TMPFS: /* 0x1021994 */
313 return "tmpfs";
314 case S_MAGIC_UDF: /* 0x15013346 */
315 return "udf";
316 case S_MAGIC_UFS: /* 0x00011954 */
317 return "ufs";
318 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
319 return "ufs";
320 case S_MAGIC_USBDEVFS: /* 0x9FA2 */
321 return "usbdevfs";
322 case S_MAGIC_VXFS: /* 0xA501FCF5 */
323 return "vxfs";
324 case S_MAGIC_XENIX: /* 0x012FF7B4 */
325 return "xenix";
326 case S_MAGIC_XFS: /* 0x58465342 */
327 return "xfs";
328 case S_MAGIC_XIAFS: /* 0x012FD16D */
329 return "xia";
331 # elif __GNU__
332 case FSTYPE_UFS:
333 return "ufs";
334 case FSTYPE_NFS:
335 return "nfs";
336 case FSTYPE_GFS:
337 return "gfs";
338 case FSTYPE_LFS:
339 return "lfs";
340 case FSTYPE_SYSV:
341 return "sysv";
342 case FSTYPE_FTP:
343 return "ftp";
344 case FSTYPE_TAR:
345 return "tar";
346 case FSTYPE_AR:
347 return "ar";
348 case FSTYPE_CPIO:
349 return "cpio";
350 case FSTYPE_MSLOSS:
351 return "msloss";
352 case FSTYPE_CPM:
353 return "cpm";
354 case FSTYPE_HFS:
355 return "hfs";
356 case FSTYPE_DTFS:
357 return "dtfs";
358 case FSTYPE_GRFS:
359 return "grfs";
360 case FSTYPE_TERM:
361 return "term";
362 case FSTYPE_DEV:
363 return "dev";
364 case FSTYPE_PROC:
365 return "proc";
366 case FSTYPE_IFSOCK:
367 return "ifsock";
368 case FSTYPE_AFS:
369 return "afs";
370 case FSTYPE_DFS:
371 return "dfs";
372 case FSTYPE_PROC9:
373 return "proc9";
374 case FSTYPE_SOCKET:
375 return "socket";
376 case FSTYPE_MISC:
377 return "misc";
378 case FSTYPE_EXT2FS:
379 return "ext2/ext3";
380 case FSTYPE_HTTP:
381 return "http";
382 case FSTYPE_MEMFS:
383 return "memfs";
384 case FSTYPE_ISO9660:
385 return "iso9660";
386 # endif
387 default:
389 unsigned long int type = statfsbuf->f_type;
390 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
391 + (sizeof type * CHAR_BIT + 3) / 4];
392 sprintf (buf, "UNKNOWN (0x%lx)", type);
393 return buf;
396 #endif
399 static char *
400 human_access (struct stat const *statbuf)
402 static char modebuf[12];
403 filemodestring (statbuf, modebuf);
404 modebuf[10] = 0;
405 return modebuf;
408 static char *
409 human_time (struct timespec t)
411 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
412 (INT_STRLEN_BOUND (int) /* YYYY */
413 + 1 /* because YYYY might equal INT_MAX + 1900 */
414 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
415 struct tm const *tm = localtime (&t.tv_sec);
416 if (tm == NULL)
417 return timetostr (t.tv_sec, str);
418 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
419 return str;
422 static void
423 out_string (char *pformat, size_t prefix_len, char const *arg)
425 strcpy (pformat + prefix_len, "s");
426 printf (pformat, arg);
428 static void
429 out_int (char *pformat, size_t prefix_len, intmax_t arg)
431 strcpy (pformat + prefix_len, PRIdMAX);
432 printf (pformat, arg);
434 static void
435 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
437 strcpy (pformat + prefix_len, PRIuMAX);
438 printf (pformat, arg);
440 static void
441 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
443 strcpy (pformat + prefix_len, PRIoMAX);
444 printf (pformat, arg);
446 static void
447 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
449 strcpy (pformat + prefix_len, PRIxMAX);
450 printf (pformat, arg);
453 /* Very specialized function (modifies FORMAT), just so as to avoid
454 duplicating this code between both print_statfs and print_stat. */
455 static void
456 out_file_context (char const *filename, char *pformat, size_t prefix_len)
458 char *scontext;
459 if ((follow_links
460 ? getfilecon (filename, &scontext)
461 : lgetfilecon (filename, &scontext)) < 0)
463 error (0, errno, _("failed to get security context of %s"),
464 quote (filename));
465 scontext = NULL;
467 strcpy (pformat + prefix_len, "s");
468 printf (pformat, (scontext ? scontext : "?"));
469 if (scontext)
470 freecon (scontext);
473 /* print statfs info */
474 static void
475 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
476 void const *data)
478 STRUCT_STATVFS const *statfsbuf = data;
480 switch (m)
482 case 'n':
483 out_string (pformat, prefix_len, filename);
484 break;
486 case 'i':
488 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
489 uintmax_t fsid = statfsbuf->f_fsid;
490 #else
491 typedef unsigned int fsid_word;
492 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
493 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
494 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
495 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
497 /* Assume a little-endian word order, as that is compatible
498 with glibc's statvfs implementation. */
499 uintmax_t fsid = 0;
500 int words = sizeof statfsbuf->f_fsid / sizeof *p;
501 int i;
502 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
504 uintmax_t u = p[words - 1 - i];
505 fsid |= u << (i * CHAR_BIT * sizeof *p);
507 #endif
508 out_uint_x (pformat, prefix_len, fsid);
510 break;
512 case 'l':
513 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
514 break;
515 case 't':
516 #if HAVE_STRUCT_STATXFS_F_TYPE
517 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
518 #else
519 fputc ('?', stdout);
520 #endif
521 break;
522 case 'T':
523 out_string (pformat, prefix_len, human_fstype (statfsbuf));
524 break;
525 case 'b':
526 out_int (pformat, prefix_len, statfsbuf->f_blocks);
527 break;
528 case 'f':
529 out_int (pformat, prefix_len, statfsbuf->f_bfree);
530 break;
531 case 'a':
532 out_int (pformat, prefix_len, statfsbuf->f_bavail);
533 break;
534 case 's':
535 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
536 break;
537 case 'S':
539 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
540 if (! frsize)
541 frsize = statfsbuf->f_bsize;
542 out_uint (pformat, prefix_len, frsize);
544 break;
545 case 'c':
546 out_int (pformat, prefix_len, statfsbuf->f_files);
547 break;
548 case 'd':
549 out_int (pformat, prefix_len, statfsbuf->f_ffree);
550 break;
551 case 'C':
552 out_file_context (filename, pformat, prefix_len);
553 break;
554 default:
555 fputc ('?', stdout);
556 break;
560 /* print stat info */
561 static void
562 print_stat (char *pformat, size_t prefix_len, char m,
563 char const *filename, void const *data)
565 struct stat *statbuf = (struct stat *) data;
566 struct passwd *pw_ent;
567 struct group *gw_ent;
569 switch (m)
571 case 'n':
572 out_string (pformat, prefix_len, filename);
573 break;
574 case 'N':
575 out_string (pformat, prefix_len, quote (filename));
576 if (S_ISLNK (statbuf->st_mode))
578 char *linkname = areadlink_with_size (filename, statbuf->st_size);
579 if (linkname == NULL)
581 error (0, errno, _("cannot read symbolic link %s"),
582 quote (filename));
583 return;
585 printf (" -> ");
586 out_string (pformat, prefix_len, quote (linkname));
588 break;
589 case 'd':
590 out_uint (pformat, prefix_len, statbuf->st_dev);
591 break;
592 case 'D':
593 out_uint_x (pformat, prefix_len, statbuf->st_dev);
594 break;
595 case 'i':
596 out_uint (pformat, prefix_len, statbuf->st_ino);
597 break;
598 case 'a':
599 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
600 break;
601 case 'A':
602 out_string (pformat, prefix_len, human_access (statbuf));
603 break;
604 case 'f':
605 out_uint_x (pformat, prefix_len, statbuf->st_mode);
606 break;
607 case 'F':
608 out_string (pformat, prefix_len, file_type (statbuf));
609 break;
610 case 'h':
611 out_uint (pformat, prefix_len, statbuf->st_nlink);
612 break;
613 case 'u':
614 out_uint (pformat, prefix_len, statbuf->st_uid);
615 break;
616 case 'U':
617 setpwent ();
618 pw_ent = getpwuid (statbuf->st_uid);
619 out_string (pformat, prefix_len,
620 pw_ent ? pw_ent->pw_name : "UNKNOWN");
621 break;
622 case 'g':
623 out_uint (pformat, prefix_len, statbuf->st_gid);
624 break;
625 case 'G':
626 setgrent ();
627 gw_ent = getgrgid (statbuf->st_gid);
628 out_string (pformat, prefix_len,
629 gw_ent ? gw_ent->gr_name : "UNKNOWN");
630 break;
631 case 't':
632 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
633 break;
634 case 'T':
635 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
636 break;
637 case 's':
638 out_uint (pformat, prefix_len, statbuf->st_size);
639 break;
640 case 'B':
641 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
642 break;
643 case 'b':
644 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
645 break;
646 case 'o':
647 out_uint (pformat, prefix_len, statbuf->st_blksize);
648 break;
649 case 'x':
650 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
651 break;
652 case 'X':
653 if (TYPE_SIGNED (time_t))
654 out_int (pformat, prefix_len, statbuf->st_atime);
655 else
656 out_uint (pformat, prefix_len, statbuf->st_atime);
657 break;
658 case 'y':
659 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
660 break;
661 case 'Y':
662 if (TYPE_SIGNED (time_t))
663 out_int (pformat, prefix_len, statbuf->st_mtime);
664 else
665 out_uint (pformat, prefix_len, statbuf->st_mtime);
666 break;
667 case 'z':
668 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
669 break;
670 case 'Z':
671 if (TYPE_SIGNED (time_t))
672 out_int (pformat, prefix_len, statbuf->st_ctime);
673 else
674 out_uint (pformat, prefix_len, statbuf->st_ctime);
675 break;
676 case 'C':
677 out_file_context (filename, pformat, prefix_len);
678 break;
679 default:
680 fputc ('?', stdout);
681 break;
685 /* Output a single-character \ escape. */
687 static void
688 print_esc_char (char c)
690 switch (c)
692 case 'a': /* Alert. */
693 c ='\a';
694 break;
695 case 'b': /* Backspace. */
696 c ='\b';
697 break;
698 case 'f': /* Form feed. */
699 c ='\f';
700 break;
701 case 'n': /* New line. */
702 c ='\n';
703 break;
704 case 'r': /* Carriage return. */
705 c ='\r';
706 break;
707 case 't': /* Horizontal tab. */
708 c ='\t';
709 break;
710 case 'v': /* Vertical tab. */
711 c ='\v';
712 break;
713 case '"':
714 case '\\':
715 break;
716 default:
717 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
718 break;
720 putchar (c);
723 static void
724 print_it (char const *format, char const *filename,
725 void (*print_func) (char *, size_t, char, char const *, void const *),
726 void const *data)
728 /* Add 2 to accommodate our conversion of the stat `%s' format string
729 to the longer printf `%llu' one. */
730 enum
732 MAX_ADDITIONAL_BYTES =
733 (MAX (sizeof PRIdMAX,
734 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
735 - 1)
737 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
738 char *dest = xmalloc (n_alloc);
739 char const *b;
740 for (b = format; *b; b++)
742 switch (*b)
744 case '%':
746 size_t len = strspn (b + 1, "#-+.I 0123456789");
747 char const *fmt_char = b + len + 1;
748 memcpy (dest, b, len + 1);
750 b = fmt_char;
751 switch (*fmt_char)
753 case '\0':
754 --b;
755 /* fall through */
756 case '%':
757 if (0 < len)
759 dest[len + 1] = *fmt_char;
760 dest[len + 2] = '\0';
761 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
762 quotearg_colon (dest));
764 putchar ('%');
765 break;
766 default:
767 print_func (dest, len + 1, *fmt_char, filename, data);
768 break;
770 break;
773 case '\\':
774 if ( ! interpret_backslash_escapes)
776 putchar ('\\');
777 break;
779 ++b;
780 if (isodigit (*b))
782 int esc_value = octtobin (*b);
783 int esc_length = 1; /* number of octal digits */
784 for (++b; esc_length < 3 && isodigit (*b);
785 ++esc_length, ++b)
787 esc_value = esc_value * 8 + octtobin (*b);
789 putchar (esc_value);
790 --b;
792 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
794 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
795 /* A hexadecimal \xhh escape sequence must have
796 1 or 2 hex. digits. */
797 ++b;
798 if (isxdigit (to_uchar (b[1])))
800 ++b;
801 esc_value = esc_value * 16 + hextobin (*b);
803 putchar (esc_value);
805 else if (*b == '\0')
807 error (0, 0, _("warning: backslash at end of format"));
808 putchar ('\\');
809 /* Arrange to exit the loop. */
810 --b;
812 else
814 print_esc_char (*b);
816 break;
818 default:
819 putchar (*b);
820 break;
823 free (dest);
825 fputs (trailing_delim, stdout);
828 /* Stat the file system and print what we find. */
829 static bool
830 do_statfs (char const *filename, bool terse, char const *format)
832 STRUCT_STATVFS statfsbuf;
834 if (STATFS (filename, &statfsbuf) != 0)
836 error (0, errno, _("cannot read file system information for %s"),
837 quote (filename));
838 return false;
841 if (format == NULL)
843 format = (terse
844 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
845 : " File: \"%n\"\n"
846 " ID: %-8i Namelen: %-7l Type: %T\n"
847 "Block size: %-10s Fundamental block size: %S\n"
848 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
849 "Inodes: Total: %-10c Free: %d\n");
852 print_it (format, filename, print_statfs, &statfsbuf);
853 return true;
856 /* stat the file and print what we find */
857 static bool
858 do_stat (char const *filename, bool terse, char const *format)
860 struct stat statbuf;
862 if ((follow_links ? stat : lstat) (filename, &statbuf) != 0)
864 error (0, errno, _("cannot stat %s"), quote (filename));
865 return false;
868 if (format == NULL)
870 if (terse)
872 format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
874 else
876 /* Temporary hack to match original output until conditional
877 implemented. */
878 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
880 format =
881 " File: %N\n"
882 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
883 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
884 " Device type: %t,%T\n"
885 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
886 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
888 else
890 format =
891 " File: %N\n"
892 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
893 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
894 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
895 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
899 print_it (format, filename, print_stat, &statbuf);
900 return true;
903 void
904 usage (int status)
906 if (status != EXIT_SUCCESS)
907 fprintf (stderr, _("Try `%s --help' for more information.\n"),
908 program_name);
909 else
911 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
912 fputs (_("\
913 Display file or file system status.\n\
915 -L, --dereference follow links\n\
916 -f, --file-system display file system status instead of file status\n\
917 "), stdout);
918 fputs (_("\
919 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
920 output a newline after each use of FORMAT\n\
921 --printf=FORMAT like --format, but interpret backslash escapes,\n\
922 and do not output a mandatory trailing newline.\n\
923 If you want a newline, include \\n in FORMAT.\n\
924 -t, --terse print the information in terse form\n\
925 "), stdout);
926 fputs (HELP_OPTION_DESCRIPTION, stdout);
927 fputs (VERSION_OPTION_DESCRIPTION, stdout);
929 fputs (_("\n\
930 The valid format sequences for files (without --file-system):\n\
932 %a Access rights in octal\n\
933 %A Access rights in human readable form\n\
934 %b Number of blocks allocated (see %B)\n\
935 %B The size in bytes of each block reported by %b\n\
936 %C SELinux security context string\n\
937 "), stdout);
938 fputs (_("\
939 %d Device number in decimal\n\
940 %D Device number in hex\n\
941 %f Raw mode in hex\n\
942 %F File type\n\
943 %g Group ID of owner\n\
944 %G Group name of owner\n\
945 "), stdout);
946 fputs (_("\
947 %h Number of hard links\n\
948 %i Inode number\n\
949 %n File name\n\
950 %N Quoted file name with dereference if symbolic link\n\
951 %o I/O block size\n\
952 %s Total size, in bytes\n\
953 %t Major device type in hex\n\
954 %T Minor device type in hex\n\
955 "), stdout);
956 fputs (_("\
957 %u User ID of owner\n\
958 %U User name of owner\n\
959 %x Time of last access\n\
960 %X Time of last access as seconds since Epoch\n\
961 %y Time of last modification\n\
962 %Y Time of last modification as seconds since Epoch\n\
963 %z Time of last change\n\
964 %Z Time of last change as seconds since Epoch\n\
966 "), stdout);
968 fputs (_("\
969 Valid format sequences for file systems:\n\
971 %a Free blocks available to non-superuser\n\
972 %b Total data blocks in file system\n\
973 %c Total file nodes in file system\n\
974 %d Free file nodes in file system\n\
975 %f Free blocks in file system\n\
976 %C SELinux security context string\n\
977 "), stdout);
978 fputs (_("\
979 %i File System ID in hex\n\
980 %l Maximum length of filenames\n\
981 %n File name\n\
982 %s Block size (for faster transfers)\n\
983 %S Fundamental block size (for block counts)\n\
984 %t Type in hex\n\
985 %T Type in human readable form\n\
986 "), stdout);
987 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
988 emit_bug_reporting_address ();
990 exit (status);
994 main (int argc, char *argv[])
996 int c;
997 int i;
998 bool fs = false;
999 bool terse = false;
1000 char *format = NULL;
1001 bool ok = true;
1003 initialize_main (&argc, &argv);
1004 set_program_name (argv[0]);
1005 setlocale (LC_ALL, "");
1006 bindtextdomain (PACKAGE, LOCALEDIR);
1007 textdomain (PACKAGE);
1009 atexit (close_stdout);
1011 while ((c = getopt_long (argc, argv, "c:fLtZ", long_options, NULL)) != -1)
1013 switch (c)
1015 case PRINTF_OPTION:
1016 format = optarg;
1017 interpret_backslash_escapes = true;
1018 trailing_delim = "";
1019 break;
1021 case 'c':
1022 format = optarg;
1023 interpret_backslash_escapes = false;
1024 trailing_delim = "\n";
1025 break;
1027 case 'L':
1028 follow_links = true;
1029 break;
1031 case 'f':
1032 fs = true;
1033 break;
1035 case 't':
1036 terse = true;
1037 break;
1039 case 'Z': /* FIXME: remove in 2010 */
1040 /* Ignore, for compatibility with distributions
1041 that implemented this before upstream.
1042 But warn of impending removal. */
1043 error (0, 0,
1044 _("the --context (-Z) option is obsolete and will be removed\n"
1045 "in a future release"));
1046 break;
1048 case_GETOPT_HELP_CHAR;
1050 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1052 default:
1053 usage (EXIT_FAILURE);
1057 if (argc == optind)
1059 error (0, 0, _("missing operand"));
1060 usage (EXIT_FAILURE);
1063 for (i = optind; i < argc; i++)
1064 ok &= (fs
1065 ? do_statfs (argv[i], terse, format)
1066 : do_stat (argv[i], terse, format));
1068 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);