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