bootstrap: resynchronize from gnulib
[coreutils.git] / src / stat.c
blob5063e708653d7675d3991125f9f7bce6eb4f95b3
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 Written by Michael Meskes. */
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'},
164 {"format", required_argument, NULL, 'c'},
165 {"printf", required_argument, NULL, PRINTF_OPTION},
166 {"terse", no_argument, NULL, 't'},
167 {GETOPT_HELP_OPTION_DECL},
168 {GETOPT_VERSION_OPTION_DECL},
169 {NULL, 0, NULL, 0}
172 /* Whether to follow symbolic links; True for --dereference (-L). */
173 static bool follow_links;
175 /* Whether to interpret backslash-escape sequences.
176 True for --printf=FMT, not for --format=FMT (-c). */
177 static bool interpret_backslash_escapes;
179 /* The trailing delimiter string:
180 "" for --printf=FMT, "\n" for --format=FMT (-c). */
181 static char const *trailing_delim = "";
183 /* Return the type of the specified file system.
184 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
185 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
186 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
187 Still others have neither and have to get by with f_type (GNU/Linux).
188 But f_type may only exist in statfs (Cygwin). */
189 static char const *
190 human_fstype (STRUCT_STATVFS const *statfsbuf)
192 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
193 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
194 #else
195 switch (statfsbuf->f_type)
197 # if defined __linux__
199 /* Compare with what's in libc:
200 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
201 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
202 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
203 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
204 | sort > sym_libc
205 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
206 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
207 | sort > sym_stat
208 diff -u sym_stat sym_libc
211 /* Also compare with the list in "man 2 statfs" using the
212 fs-magic-compare make target. */
214 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
215 statements must be followed by a hexadecimal constant in
216 a comment. The S_MAGIC_... name and constant are automatically
217 combined to produce the #define directives in fs.h. */
219 case S_MAGIC_ADFS: /* 0xADF5 */
220 return "adfs";
221 case S_MAGIC_AFFS: /* 0xADFF */
222 return "affs";
223 case S_MAGIC_AFS: /* 0x5346414F */
224 return "afs";
225 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 */
226 return "anon-inode FS";
227 case S_MAGIC_AUTOFS: /* 0x0187 */
228 return "autofs";
229 case S_MAGIC_BEFS: /* 0x42465331 */
230 return "befs";
231 case S_MAGIC_BFS: /* 0x1BADFACE */
232 return "bfs";
233 case S_MAGIC_BINFMT_MISC: /* 0x42494E4D */
234 return "binfmt_misc";
235 case S_MAGIC_BTRFS: /* 0x9123683E */
236 return "btrfs";
237 case S_MAGIC_CGROUP: /* 0x0027E0EB */
238 return "cgroupfs";
239 case S_MAGIC_CIFS: /* 0xFF534D42 */
240 return "cifs";
241 case S_MAGIC_CODA: /* 0x73757245 */
242 return "coda";
243 case S_MAGIC_COH: /* 0x012FF7B7 */
244 return "coh";
245 case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
246 return "cramfs";
247 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 */
248 return "cramfs-wend";
249 case S_MAGIC_DEBUGFS: /* 0x64626720 */
250 return "debugfs";
251 case S_MAGIC_DEVFS: /* 0x1373 */
252 return "devfs";
253 case S_MAGIC_DEVPTS: /* 0x1CD1 */
254 return "devpts";
255 case S_MAGIC_EFS: /* 0x00414A53 */
256 return "efs";
257 case S_MAGIC_EXT: /* 0x137D */
258 return "ext";
259 case S_MAGIC_EXT2: /* 0xEF53 */
260 return "ext2/ext3";
261 case S_MAGIC_EXT2_OLD: /* 0xEF51 */
262 return "ext2";
263 case S_MAGIC_FAT: /* 0x4006 */
264 return "fat";
265 case S_MAGIC_FUSEBLK: /* 0x65735546 */
266 return "fuseblk";
267 case S_MAGIC_FUSECTL: /* 0x65735543 */
268 return "fusectl";
269 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA */
270 return "futexfs";
271 case S_MAGIC_GFS: /* 0x1161970 */
272 return "gfs/gfs2";
273 case S_MAGIC_HFS: /* 0x4244 */
274 return "hfs";
275 case S_MAGIC_HPFS: /* 0xF995E849 */
276 return "hpfs";
277 case S_MAGIC_HUGETLBFS: /* 0x958458F6 */
278 return "hugetlbfs";
279 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA */
280 return "inotifyfs";
281 case S_MAGIC_ISOFS: /* 0x9660 */
282 return "isofs";
283 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
284 return "isofs";
285 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
286 return "isofs";
287 case S_MAGIC_JFFS: /* 0x07C0 */
288 return "jffs";
289 case S_MAGIC_JFFS2: /* 0x72B6 */
290 return "jffs2";
291 case S_MAGIC_JFS: /* 0x3153464A */
292 return "jfs";
293 case S_MAGIC_KAFS: /* 0x6B414653 */
294 return "k-afs";
295 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 */
296 return "lustre";
297 case S_MAGIC_MINIX: /* 0x137F */
298 return "minix";
299 case S_MAGIC_MINIX_30: /* 0x138F */
300 return "minix (30 char.)";
301 case S_MAGIC_MINIX_V2: /* 0x2468 */
302 return "minix v2";
303 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
304 return "minix v2 (30 char.)";
305 case S_MAGIC_MINIX_V3: /* 0x4D5A */
306 return "minix3";
307 case S_MAGIC_MSDOS: /* 0x4D44 */
308 return "msdos";
309 case S_MAGIC_NCP: /* 0x564C */
310 return "novell";
311 case S_MAGIC_NFS: /* 0x6969 */
312 return "nfs";
313 case S_MAGIC_NFSD: /* 0x6E667364 */
314 return "nfsd";
315 case S_MAGIC_NILFS: /* 0x3434 */
316 return "nilfs";
317 case S_MAGIC_NTFS: /* 0x5346544E */
318 return "ntfs";
319 case S_MAGIC_OPENPROM: /* 0x9FA1 */
320 return "openprom";
321 case S_MAGIC_OCFS2: /* 0x7461636f */
322 return "ocfs2";
323 case S_MAGIC_PROC: /* 0x9FA0 */
324 return "proc";
325 case S_MAGIC_QNX4: /* 0x002F */
326 return "qnx4";
327 case S_MAGIC_RAMFS: /* 0x858458F6 */
328 return "ramfs";
329 case S_MAGIC_REISERFS: /* 0x52654973 */
330 return "reiserfs";
331 case S_MAGIC_ROMFS: /* 0x7275 */
332 return "romfs";
333 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 */
334 return "rpc_pipefs";
335 case S_MAGIC_SECURITYFS: /* 0x73636673 */
336 return "securityfs";
337 case S_MAGIC_SELINUX: /* 0xF97CFF8C */
338 return "selinux";
339 case S_MAGIC_SMB: /* 0x517B */
340 return "smb";
341 case S_MAGIC_SOCKFS: /* 0x534F434B */
342 return "sockfs";
343 case S_MAGIC_SQUASHFS: /* 0x73717368 */
344 return "squashfs";
345 case S_MAGIC_SYSFS: /* 0x62656572 */
346 return "sysfs";
347 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
348 return "sysv2";
349 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
350 return "sysv4";
351 case S_MAGIC_TMPFS: /* 0x01021994 */
352 return "tmpfs";
353 case S_MAGIC_UDF: /* 0x15013346 */
354 return "udf";
355 case S_MAGIC_UFS: /* 0x00011954 */
356 return "ufs";
357 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
358 return "ufs";
359 case S_MAGIC_USBDEVFS: /* 0x9FA2 */
360 return "usbdevfs";
361 case S_MAGIC_VXFS: /* 0xA501FCF5 */
362 return "vxfs";
363 case S_MAGIC_XENFS: /* 0xABBA1974 */
364 return "xenfs";
365 case S_MAGIC_XENIX: /* 0x012FF7B4 */
366 return "xenix";
367 case S_MAGIC_XFS: /* 0x58465342 */
368 return "xfs";
369 case S_MAGIC_XIAFS: /* 0x012FD16D */
370 return "xia";
372 # elif __GNU__
373 case FSTYPE_UFS:
374 return "ufs";
375 case FSTYPE_NFS:
376 return "nfs";
377 case FSTYPE_GFS:
378 return "gfs";
379 case FSTYPE_LFS:
380 return "lfs";
381 case FSTYPE_SYSV:
382 return "sysv";
383 case FSTYPE_FTP:
384 return "ftp";
385 case FSTYPE_TAR:
386 return "tar";
387 case FSTYPE_AR:
388 return "ar";
389 case FSTYPE_CPIO:
390 return "cpio";
391 case FSTYPE_MSLOSS:
392 return "msloss";
393 case FSTYPE_CPM:
394 return "cpm";
395 case FSTYPE_HFS:
396 return "hfs";
397 case FSTYPE_DTFS:
398 return "dtfs";
399 case FSTYPE_GRFS:
400 return "grfs";
401 case FSTYPE_TERM:
402 return "term";
403 case FSTYPE_DEV:
404 return "dev";
405 case FSTYPE_PROC:
406 return "proc";
407 case FSTYPE_IFSOCK:
408 return "ifsock";
409 case FSTYPE_AFS:
410 return "afs";
411 case FSTYPE_DFS:
412 return "dfs";
413 case FSTYPE_PROC9:
414 return "proc9";
415 case FSTYPE_SOCKET:
416 return "socket";
417 case FSTYPE_MISC:
418 return "misc";
419 case FSTYPE_EXT2FS:
420 return "ext2/ext3";
421 case FSTYPE_HTTP:
422 return "http";
423 case FSTYPE_MEMFS:
424 return "memfs";
425 case FSTYPE_ISO9660:
426 return "iso9660";
427 # endif
428 default:
430 unsigned long int type = statfsbuf->f_type;
431 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
432 + (sizeof type * CHAR_BIT + 3) / 4];
433 sprintf (buf, "UNKNOWN (0x%lx)", type);
434 return buf;
437 #endif
440 static char *
441 human_access (struct stat const *statbuf)
443 static char modebuf[12];
444 filemodestring (statbuf, modebuf);
445 modebuf[10] = 0;
446 return modebuf;
449 static char *
450 human_time (struct timespec t)
452 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
453 (INT_STRLEN_BOUND (int) /* YYYY */
454 + 1 /* because YYYY might equal INT_MAX + 1900 */
455 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
456 struct tm const *tm = localtime (&t.tv_sec);
457 if (tm == NULL)
458 return timetostr (t.tv_sec, str);
459 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
460 return str;
463 static void
464 out_string (char *pformat, size_t prefix_len, char const *arg)
466 strcpy (pformat + prefix_len, "s");
467 printf (pformat, arg);
469 static void
470 out_int (char *pformat, size_t prefix_len, intmax_t arg)
472 strcpy (pformat + prefix_len, PRIdMAX);
473 printf (pformat, arg);
475 static void
476 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
478 strcpy (pformat + prefix_len, PRIuMAX);
479 printf (pformat, arg);
481 static void
482 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
484 strcpy (pformat + prefix_len, PRIoMAX);
485 printf (pformat, arg);
487 static void
488 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
490 strcpy (pformat + prefix_len, PRIxMAX);
491 printf (pformat, arg);
494 /* Very specialized function (modifies FORMAT), just so as to avoid
495 duplicating this code between both print_statfs and print_stat. */
496 static void
497 out_file_context (char const *filename, char *pformat, size_t prefix_len)
499 char *scontext;
500 if ((follow_links
501 ? getfilecon (filename, &scontext)
502 : lgetfilecon (filename, &scontext)) < 0)
504 error (0, errno, _("failed to get security context of %s"),
505 quote (filename));
506 scontext = NULL;
508 strcpy (pformat + prefix_len, "s");
509 printf (pformat, (scontext ? scontext : "?"));
510 if (scontext)
511 freecon (scontext);
514 /* print statfs info */
515 static void
516 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
517 void const *data)
519 STRUCT_STATVFS const *statfsbuf = data;
521 switch (m)
523 case 'n':
524 out_string (pformat, prefix_len, filename);
525 break;
527 case 'i':
529 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
530 uintmax_t fsid = statfsbuf->f_fsid;
531 #else
532 typedef unsigned int fsid_word;
533 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
534 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
535 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
536 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
538 /* Assume a little-endian word order, as that is compatible
539 with glibc's statvfs implementation. */
540 uintmax_t fsid = 0;
541 int words = sizeof statfsbuf->f_fsid / sizeof *p;
542 int i;
543 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
545 uintmax_t u = p[words - 1 - i];
546 fsid |= u << (i * CHAR_BIT * sizeof *p);
548 #endif
549 out_uint_x (pformat, prefix_len, fsid);
551 break;
553 case 'l':
554 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
555 break;
556 case 't':
557 #if HAVE_STRUCT_STATXFS_F_TYPE
558 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
559 #else
560 fputc ('?', stdout);
561 #endif
562 break;
563 case 'T':
564 out_string (pformat, prefix_len, human_fstype (statfsbuf));
565 break;
566 case 'b':
567 out_int (pformat, prefix_len, statfsbuf->f_blocks);
568 break;
569 case 'f':
570 out_int (pformat, prefix_len, statfsbuf->f_bfree);
571 break;
572 case 'a':
573 out_int (pformat, prefix_len, statfsbuf->f_bavail);
574 break;
575 case 's':
576 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
577 break;
578 case 'S':
580 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
581 if (! frsize)
582 frsize = statfsbuf->f_bsize;
583 out_uint (pformat, prefix_len, frsize);
585 break;
586 case 'c':
587 out_uint (pformat, prefix_len, statfsbuf->f_files);
588 break;
589 case 'd':
590 out_int (pformat, prefix_len, statfsbuf->f_ffree);
591 break;
592 case 'C':
593 out_file_context (filename, pformat, prefix_len);
594 break;
595 default:
596 fputc ('?', stdout);
597 break;
601 /* print stat info */
602 static void
603 print_stat (char *pformat, size_t prefix_len, char m,
604 char const *filename, void const *data)
606 struct stat *statbuf = (struct stat *) data;
607 struct passwd *pw_ent;
608 struct group *gw_ent;
610 switch (m)
612 case 'n':
613 out_string (pformat, prefix_len, filename);
614 break;
615 case 'N':
616 out_string (pformat, prefix_len, quote (filename));
617 if (S_ISLNK (statbuf->st_mode))
619 char *linkname = areadlink_with_size (filename, statbuf->st_size);
620 if (linkname == NULL)
622 error (0, errno, _("cannot read symbolic link %s"),
623 quote (filename));
624 return;
626 printf (" -> ");
627 out_string (pformat, prefix_len, quote (linkname));
629 break;
630 case 'd':
631 out_uint (pformat, prefix_len, statbuf->st_dev);
632 break;
633 case 'D':
634 out_uint_x (pformat, prefix_len, statbuf->st_dev);
635 break;
636 case 'i':
637 out_uint (pformat, prefix_len, statbuf->st_ino);
638 break;
639 case 'a':
640 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
641 break;
642 case 'A':
643 out_string (pformat, prefix_len, human_access (statbuf));
644 break;
645 case 'f':
646 out_uint_x (pformat, prefix_len, statbuf->st_mode);
647 break;
648 case 'F':
649 out_string (pformat, prefix_len, file_type (statbuf));
650 break;
651 case 'h':
652 out_uint (pformat, prefix_len, statbuf->st_nlink);
653 break;
654 case 'u':
655 out_uint (pformat, prefix_len, statbuf->st_uid);
656 break;
657 case 'U':
658 setpwent ();
659 pw_ent = getpwuid (statbuf->st_uid);
660 out_string (pformat, prefix_len,
661 pw_ent ? pw_ent->pw_name : "UNKNOWN");
662 break;
663 case 'g':
664 out_uint (pformat, prefix_len, statbuf->st_gid);
665 break;
666 case 'G':
667 setgrent ();
668 gw_ent = getgrgid (statbuf->st_gid);
669 out_string (pformat, prefix_len,
670 gw_ent ? gw_ent->gr_name : "UNKNOWN");
671 break;
672 case 't':
673 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
674 break;
675 case 'T':
676 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
677 break;
678 case 's':
679 out_uint (pformat, prefix_len, statbuf->st_size);
680 break;
681 case 'B':
682 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
683 break;
684 case 'b':
685 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
686 break;
687 case 'o':
688 out_uint (pformat, prefix_len, statbuf->st_blksize);
689 break;
690 case 'x':
691 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
692 break;
693 case 'X':
694 if (TYPE_SIGNED (time_t))
695 out_int (pformat, prefix_len, statbuf->st_atime);
696 else
697 out_uint (pformat, prefix_len, statbuf->st_atime);
698 break;
699 case 'y':
700 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
701 break;
702 case 'Y':
703 if (TYPE_SIGNED (time_t))
704 out_int (pformat, prefix_len, statbuf->st_mtime);
705 else
706 out_uint (pformat, prefix_len, statbuf->st_mtime);
707 break;
708 case 'z':
709 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
710 break;
711 case 'Z':
712 if (TYPE_SIGNED (time_t))
713 out_int (pformat, prefix_len, statbuf->st_ctime);
714 else
715 out_uint (pformat, prefix_len, statbuf->st_ctime);
716 break;
717 case 'C':
718 out_file_context (filename, pformat, prefix_len);
719 break;
720 default:
721 fputc ('?', stdout);
722 break;
726 /* Output a single-character \ escape. */
728 static void
729 print_esc_char (char c)
731 switch (c)
733 case 'a': /* Alert. */
734 c ='\a';
735 break;
736 case 'b': /* Backspace. */
737 c ='\b';
738 break;
739 case 'e': /* Escape. */
740 c ='\x1B';
741 break;
742 case 'f': /* Form feed. */
743 c ='\f';
744 break;
745 case 'n': /* New line. */
746 c ='\n';
747 break;
748 case 'r': /* Carriage return. */
749 c ='\r';
750 break;
751 case 't': /* Horizontal tab. */
752 c ='\t';
753 break;
754 case 'v': /* Vertical tab. */
755 c ='\v';
756 break;
757 case '"':
758 case '\\':
759 break;
760 default:
761 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
762 break;
764 putchar (c);
767 static void
768 print_it (char const *format, char const *filename,
769 void (*print_func) (char *, size_t, char, char const *, void const *),
770 void const *data)
772 /* Add 2 to accommodate our conversion of the stat `%s' format string
773 to the longer printf `%llu' one. */
774 enum
776 MAX_ADDITIONAL_BYTES =
777 (MAX (sizeof PRIdMAX,
778 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
779 - 1)
781 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
782 char *dest = xmalloc (n_alloc);
783 char const *b;
784 for (b = format; *b; b++)
786 switch (*b)
788 case '%':
790 size_t len = strspn (b + 1, "#-+.I 0123456789");
791 char const *fmt_char = b + len + 1;
792 memcpy (dest, b, len + 1);
794 b = fmt_char;
795 switch (*fmt_char)
797 case '\0':
798 --b;
799 /* fall through */
800 case '%':
801 if (0 < len)
803 dest[len + 1] = *fmt_char;
804 dest[len + 2] = '\0';
805 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
806 quotearg_colon (dest));
808 putchar ('%');
809 break;
810 default:
811 print_func (dest, len + 1, *fmt_char, filename, data);
812 break;
814 break;
817 case '\\':
818 if ( ! interpret_backslash_escapes)
820 putchar ('\\');
821 break;
823 ++b;
824 if (isodigit (*b))
826 int esc_value = octtobin (*b);
827 int esc_length = 1; /* number of octal digits */
828 for (++b; esc_length < 3 && isodigit (*b);
829 ++esc_length, ++b)
831 esc_value = esc_value * 8 + octtobin (*b);
833 putchar (esc_value);
834 --b;
836 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
838 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
839 /* A hexadecimal \xhh escape sequence must have
840 1 or 2 hex. digits. */
841 ++b;
842 if (isxdigit (to_uchar (b[1])))
844 ++b;
845 esc_value = esc_value * 16 + hextobin (*b);
847 putchar (esc_value);
849 else if (*b == '\0')
851 error (0, 0, _("warning: backslash at end of format"));
852 putchar ('\\');
853 /* Arrange to exit the loop. */
854 --b;
856 else
858 print_esc_char (*b);
860 break;
862 default:
863 putchar (*b);
864 break;
867 free (dest);
869 fputs (trailing_delim, stdout);
872 /* Stat the file system and print what we find. */
873 static bool
874 do_statfs (char const *filename, bool terse, char const *format)
876 STRUCT_STATVFS statfsbuf;
878 if (STREQ (filename, "-"))
880 error (0, 0, _("using %s to denote standard input does not work"
881 " in file system mode"), quote (filename));
882 return false;
885 if (STATFS (filename, &statfsbuf) != 0)
887 error (0, errno, _("cannot read file system information for %s"),
888 quote (filename));
889 return false;
892 if (format == NULL)
894 format = (terse
895 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
896 : " File: \"%n\"\n"
897 " ID: %-8i Namelen: %-7l Type: %T\n"
898 "Block size: %-10s Fundamental block size: %S\n"
899 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
900 "Inodes: Total: %-10c Free: %d\n");
903 print_it (format, filename, print_statfs, &statfsbuf);
904 return true;
907 /* stat the file and print what we find */
908 static bool
909 do_stat (char const *filename, bool terse, char const *format)
911 struct stat statbuf;
913 if (STREQ (filename, "-"))
915 if (fstat (STDIN_FILENO, &statbuf) != 0)
917 error (0, errno, _("cannot stat standard input"));
918 return false;
921 /* We can't use the shorter
922 (follow_links?stat:lstat) (filename, &statbug)
923 since stat might be a function-like macro. */
924 else if ((follow_links
925 ? stat (filename, &statbuf)
926 : lstat (filename, &statbuf)) != 0)
928 error (0, errno, _("cannot stat %s"), quote (filename));
929 return false;
932 if (format == NULL)
934 if (terse)
936 format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
938 else
940 /* Temporary hack to match original output until conditional
941 implemented. */
942 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
944 format =
945 " File: %N\n"
946 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
947 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
948 " Device type: %t,%T\n"
949 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
950 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
952 else
954 format =
955 " File: %N\n"
956 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
957 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
958 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
959 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
963 print_it (format, filename, print_stat, &statbuf);
964 return true;
967 void
968 usage (int status)
970 if (status != EXIT_SUCCESS)
971 fprintf (stderr, _("Try `%s --help' for more information.\n"),
972 program_name);
973 else
975 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
976 fputs (_("\
977 Display file or file system status.\n\
979 -L, --dereference follow links\n\
980 -f, --file-system display file system status instead of file status\n\
981 "), stdout);
982 fputs (_("\
983 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
984 output a newline after each use of FORMAT\n\
985 --printf=FORMAT like --format, but interpret backslash escapes,\n\
986 and do not output a mandatory trailing newline.\n\
987 If you want a newline, include \\n in FORMAT\n\
988 -t, --terse print the information in terse form\n\
989 "), stdout);
990 fputs (HELP_OPTION_DESCRIPTION, stdout);
991 fputs (VERSION_OPTION_DESCRIPTION, stdout);
993 fputs (_("\n\
994 The valid format sequences for files (without --file-system):\n\
996 %a Access rights in octal\n\
997 %A Access rights in human readable form\n\
998 %b Number of blocks allocated (see %B)\n\
999 %B The size in bytes of each block reported by %b\n\
1000 %C SELinux security context string\n\
1001 "), stdout);
1002 fputs (_("\
1003 %d Device number in decimal\n\
1004 %D Device number in hex\n\
1005 %f Raw mode in hex\n\
1006 %F File type\n\
1007 %g Group ID of owner\n\
1008 %G Group name of owner\n\
1009 "), stdout);
1010 fputs (_("\
1011 %h Number of hard links\n\
1012 %i Inode number\n\
1013 %n File name\n\
1014 %N Quoted file name with dereference if symbolic link\n\
1015 %o I/O block size\n\
1016 %s Total size, in bytes\n\
1017 %t Major device type in hex\n\
1018 %T Minor device type in hex\n\
1019 "), stdout);
1020 fputs (_("\
1021 %u User ID of owner\n\
1022 %U User name of owner\n\
1023 %x Time of last access\n\
1024 %X Time of last access as seconds since Epoch\n\
1025 %y Time of last modification\n\
1026 %Y Time of last modification as seconds since Epoch\n\
1027 %z Time of last change\n\
1028 %Z Time of last change as seconds since Epoch\n\
1030 "), stdout);
1032 fputs (_("\
1033 Valid format sequences for file systems:\n\
1035 %a Free blocks available to non-superuser\n\
1036 %b Total data blocks in file system\n\
1037 %c Total file nodes in file system\n\
1038 %d Free file nodes in file system\n\
1039 %f Free blocks in file system\n\
1040 %C SELinux security context string\n\
1041 "), stdout);
1042 fputs (_("\
1043 %i File System ID in hex\n\
1044 %l Maximum length of filenames\n\
1045 %n File name\n\
1046 %s Block size (for faster transfers)\n\
1047 %S Fundamental block size (for block counts)\n\
1048 %t Type in hex\n\
1049 %T Type in human readable form\n\
1050 "), stdout);
1051 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1052 emit_ancillary_info ();
1054 exit (status);
1058 main (int argc, char *argv[])
1060 int c;
1061 int i;
1062 bool fs = false;
1063 bool terse = false;
1064 char *format = NULL;
1065 bool ok = true;
1067 initialize_main (&argc, &argv);
1068 set_program_name (argv[0]);
1069 setlocale (LC_ALL, "");
1070 bindtextdomain (PACKAGE, LOCALEDIR);
1071 textdomain (PACKAGE);
1073 atexit (close_stdout);
1075 while ((c = getopt_long (argc, argv, "c:fLtZ", long_options, NULL)) != -1)
1077 switch (c)
1079 case PRINTF_OPTION:
1080 format = optarg;
1081 interpret_backslash_escapes = true;
1082 trailing_delim = "";
1083 break;
1085 case 'c':
1086 format = optarg;
1087 interpret_backslash_escapes = false;
1088 trailing_delim = "\n";
1089 break;
1091 case 'L':
1092 follow_links = true;
1093 break;
1095 case 'f':
1096 fs = true;
1097 break;
1099 case 't':
1100 terse = true;
1101 break;
1103 case 'Z': /* FIXME: remove in 2010 */
1104 /* Ignore, for compatibility with distributions
1105 that implemented this before upstream.
1106 But warn of impending removal. */
1107 error (0, 0,
1108 _("the --context (-Z) option is obsolete and will be removed\n"
1109 "in a future release"));
1110 break;
1112 case_GETOPT_HELP_CHAR;
1114 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1116 default:
1117 usage (EXIT_FAILURE);
1121 if (argc == optind)
1123 error (0, 0, _("missing operand"));
1124 usage (EXIT_FAILURE);
1127 for (i = optind; i < argc; i++)
1128 ok &= (fs
1129 ? do_statfs (argv[i], terse, format)
1130 : do_stat (argv[i], terse, format));
1132 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);