Tizen 2.0 Release
[external/tizen-coreutils.git] / src / stat.c
1 /* stat.c -- display file or file system status
2    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation.
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 2, or (at your option)
7    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, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18    Written by Michael Meskes.  */
19
20 #include <config.h>
21
22 /* Keep this conditional in sync with the similar conditional in
23    ../m4/stat-prog.m4.  */
24 #if (STAT_STATVFS \
25      && (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
26          || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
27 # define USE_STATVFS 1
28 #else
29 # define USE_STATVFS 0
30 #endif
31
32 #include <stddef.h>
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <pwd.h>
36 #include <grp.h>
37 #if USE_STATVFS
38 # include <sys/statvfs.h>
39 #elif HAVE_SYS_VFS_H
40 # include <sys/vfs.h>
41 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
42 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
43    It does have statvfs.h, but shouldn't use it, since it doesn't
44    HAVE_STRUCT_STATVFS_F_BASETYPE.  So find a clean way to fix it.  */
45 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
46 # include <sys/param.h>
47 # include <sys/mount.h>
48 # if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
49 /* Ultrix 4.4 needs these for the declaration of struct statfs.  */
50 #  include <netinet/in.h>
51 #  include <nfs/nfs_clnt.h>
52 #  include <nfs/vfs.h>
53 # endif
54 #elif HAVE_OS_H /* BeOS */
55 # include <fs_info.h>
56 #endif
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 "xreadlink.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 "Michael Meskes"
154
155 enum
156 {
157   PRINTF_OPTION = CHAR_MAX + 1
158 };
159
160 static struct option const long_options[] = {
161   {"dereference", no_argument, NULL, 'L'},
162   {"file-system", no_argument, NULL, 'f'},
163   {"filesystem", no_argument, NULL, 'f'}, /* obsolete and undocumented alias */
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}
170 };
171
172 char *program_name;
173
174 /* Whether to interpret backslash-escape sequences.
175    True for --printf=FMT, not for --format=FMT (-c).  */
176 static bool interpret_backslash_escapes;
177
178 /* The trailing delimiter string:
179    "" for --printf=FMT, "\n" for --format=FMT (-c).  */
180 static char const *trailing_delim = "";
181
182 /* Return the type of the specified file system.
183    Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
184    Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
185    Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
186    Still others have neither and have to get by with f_type (Linux).
187    But f_type may only exist in statfs (Cygwin).  */
188 static char const *
189 human_fstype (STRUCT_STATVFS const *statfsbuf)
190 {
191 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
192   return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
193 #else
194   switch (statfsbuf->f_type)
195     {
196 # if defined __linux__
197
198       /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
199          statements must be followed by a hexadecimal constant in
200          a comment.  The S_MAGIC_... name and constant are automatically
201          combined to produce the #define directives in fs.h.  */
202
203     case S_MAGIC_AFFS: /* 0xADFF */
204       return "affs";
205     case S_MAGIC_DEVPTS: /* 0x1CD1 */
206       return "devpts";
207     case S_MAGIC_EXT: /* 0x137D */
208       return "ext";
209     case S_MAGIC_EXT2_OLD: /* 0xEF51 */
210       return "ext2";
211     case S_MAGIC_EXT2: /* 0xEF53 */
212       return "ext2/ext3";
213     case S_MAGIC_JFS: /* 0x3153464a */
214       return "jfs";
215     case S_MAGIC_XFS: /* 0x58465342 */
216       return "xfs";
217     case S_MAGIC_HPFS: /* 0xF995E849 */
218       return "hpfs";
219     case S_MAGIC_ISOFS: /* 0x9660 */
220       return "isofs";
221     case S_MAGIC_ISOFS_WIN: /* 0x4000 */
222       return "isofs";
223     case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
224       return "isofs";
225     case S_MAGIC_MINIX: /* 0x137F */
226       return "minix";
227     case S_MAGIC_MINIX_30: /* 0x138F */
228       return "minix (30 char.)";
229     case S_MAGIC_MINIX_V2: /* 0x2468 */
230       return "minix v2";
231     case S_MAGIC_MINIX_V2_30: /* 0x2478 */
232       return "minix v2 (30 char.)";
233     case S_MAGIC_MSDOS: /* 0x4d44 */
234       return "msdos";
235     case S_MAGIC_FAT: /* 0x4006 */
236       return "fat";
237     case S_MAGIC_NCP: /* 0x564c */
238       return "novell";
239     case S_MAGIC_NFS: /* 0x6969 */
240       return "nfs";
241     case S_MAGIC_PROC: /* 0x9fa0 */
242       return "proc";
243     case S_MAGIC_SMB: /* 0x517B */
244       return "smb";
245     case S_MAGIC_XENIX: /* 0x012FF7B4 */
246       return "xenix";
247     case S_MAGIC_SYSV4: /* 0x012FF7B5 */
248       return "sysv4";
249     case S_MAGIC_SYSV2: /* 0x012FF7B6 */
250       return "sysv2";
251     case S_MAGIC_COH: /* 0x012FF7B7 */
252       return "coh";
253     case S_MAGIC_UFS: /* 0x00011954 */
254       return "ufs";
255     case S_MAGIC_XIAFS: /* 0x012FD16D */
256       return "xia";
257     case S_MAGIC_NTFS: /* 0x5346544e */
258       return "ntfs";
259     case S_MAGIC_TMPFS: /* 0x1021994 */
260       return "tmpfs";
261     case S_MAGIC_REISERFS: /* 0x52654973 */
262       return "reiserfs";
263     case S_MAGIC_CRAMFS: /* 0x28cd3d45 */
264       return "cramfs";
265     case S_MAGIC_ROMFS: /* 0x7275 */
266       return "romfs";
267     case S_MAGIC_RAMFS: /* 0x858458f6 */
268       return "ramfs";
269     case S_MAGIC_SQUASHFS: /* 0x73717368 */
270       return "squashfs";
271     case S_MAGIC_SYSFS: /* 0x62656572 */
272       return "sysfs";
273 # elif __GNU__
274     case FSTYPE_UFS:
275       return "ufs";
276     case FSTYPE_NFS:
277       return "nfs";
278     case FSTYPE_GFS:
279       return "gfs";
280     case FSTYPE_LFS:
281       return "lfs";
282     case FSTYPE_SYSV:
283       return "sysv";
284     case FSTYPE_FTP:
285       return "ftp";
286     case FSTYPE_TAR:
287       return "tar";
288     case FSTYPE_AR:
289       return "ar";
290     case FSTYPE_CPIO:
291       return "cpio";
292     case FSTYPE_MSLOSS:
293       return "msloss";
294     case FSTYPE_CPM:
295       return "cpm";
296     case FSTYPE_HFS:
297       return "hfs";
298     case FSTYPE_DTFS:
299       return "dtfs";
300     case FSTYPE_GRFS:
301       return "grfs";
302     case FSTYPE_TERM:
303       return "term";
304     case FSTYPE_DEV:
305       return "dev";
306     case FSTYPE_PROC:
307       return "proc";
308     case FSTYPE_IFSOCK:
309       return "ifsock";
310     case FSTYPE_AFS:
311       return "afs";
312     case FSTYPE_DFS:
313       return "dfs";
314     case FSTYPE_PROC9:
315       return "proc9";
316     case FSTYPE_SOCKET:
317       return "socket";
318     case FSTYPE_MISC:
319       return "misc";
320     case FSTYPE_EXT2FS:
321       return "ext2/ext3";
322     case FSTYPE_HTTP:
323       return "http";
324     case FSTYPE_MEMFS:
325       return "memfs";
326     case FSTYPE_ISO9660:
327       return "iso9660";
328 # endif
329     default:
330       {
331         unsigned long int type = statfsbuf->f_type;
332         static char buf[sizeof "UNKNOWN (0x%lx)" - 3
333                         + (sizeof type * CHAR_BIT + 3) / 4];
334         sprintf (buf, "UNKNOWN (0x%lx)", type);
335         return buf;
336       }
337     }
338 #endif
339 }
340
341 static char *
342 human_access (struct stat const *statbuf)
343 {
344   static char modebuf[12];
345   filemodestring (statbuf, modebuf);
346   modebuf[10] = 0;
347   return modebuf;
348 }
349
350 static char *
351 human_time (struct timespec t)
352 {
353   static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
354                        (INT_STRLEN_BOUND (int) /* YYYY */
355                         + 1 /* because YYYY might equal INT_MAX + 1900 */
356                         + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
357   struct tm const *tm = localtime (&t.tv_sec);
358   if (tm == NULL)
359     return (TYPE_SIGNED (time_t)
360             ? imaxtostr (t.tv_sec, str)
361             : umaxtostr (t.tv_sec, str));
362   nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
363   return str;
364 }
365
366 static void
367 out_string (char *pformat, size_t prefix_len, char const *arg)
368 {
369   strcpy (pformat + prefix_len, "s");
370   printf (pformat, arg);
371 }
372 static void
373 out_int (char *pformat, size_t prefix_len, intmax_t arg)
374 {
375   strcpy (pformat + prefix_len, PRIdMAX);
376   printf (pformat, arg);
377 }
378 static void
379 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
380 {
381   strcpy (pformat + prefix_len, PRIuMAX);
382   printf (pformat, arg);
383 }
384 static void
385 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
386 {
387   strcpy (pformat + prefix_len, PRIoMAX);
388   printf (pformat, arg);
389 }
390 static void
391 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
392 {
393   strcpy (pformat + prefix_len, PRIxMAX);
394   printf (pformat, arg);
395 }
396
397 /* print statfs info */
398 static void
399 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
400               void const *data)
401 {
402   STRUCT_STATVFS const *statfsbuf = data;
403
404   switch (m)
405     {
406     case 'n':
407       out_string (pformat, prefix_len, filename);
408       break;
409
410     case 'i':
411       {
412 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
413         uintmax_t fsid = statfsbuf->f_fsid;
414 #else
415         typedef unsigned int fsid_word;
416         verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
417         verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
418         verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
419         fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
420
421         /* Assume a little-endian word order, as that is compatible
422            with glibc's statvfs implementation.  */
423         uintmax_t fsid = 0;
424         int words = sizeof statfsbuf->f_fsid / sizeof *p;
425         int i;
426         for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
427           {
428             uintmax_t u = p[words - 1 - i];
429             fsid |= u << (i * CHAR_BIT * sizeof *p);
430           }
431 #endif
432         out_uint_x (pformat, prefix_len, fsid);
433       }
434       break;
435
436     case 'l':
437       OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
438       break;
439     case 't':
440 #if HAVE_STRUCT_STATXFS_F_TYPE
441       out_uint_x (pformat, prefix_len, statfsbuf->f_type);
442 #else
443       fputc ('?', stdout);
444 #endif
445       break;
446     case 'T':
447       out_string (pformat, prefix_len, human_fstype (statfsbuf));
448       break;
449     case 'b':
450       out_int (pformat, prefix_len, statfsbuf->f_blocks);
451       break;
452     case 'f':
453       out_int (pformat, prefix_len, statfsbuf->f_bfree);
454       break;
455     case 'a':
456       out_int (pformat, prefix_len, statfsbuf->f_bavail);
457       break;
458     case 's':
459       out_uint (pformat, prefix_len, statfsbuf->f_bsize);
460       break;
461     case 'S':
462       {
463         uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
464         if (! frsize)
465           frsize = statfsbuf->f_bsize;
466         out_uint (pformat, prefix_len, frsize);
467       }
468       break;
469     case 'c':
470       out_int (pformat, prefix_len, statfsbuf->f_files);
471       break;
472     case 'd':
473       out_int (pformat, prefix_len, statfsbuf->f_ffree);
474       break;
475
476     default:
477       fputc ('?', stdout);
478       break;
479     }
480 }
481
482 /* print stat info */
483 static void
484 print_stat (char *pformat, size_t prefix_len, char m,
485             char const *filename, void const *data)
486 {
487   struct stat *statbuf = (struct stat *) data;
488   struct passwd *pw_ent;
489   struct group *gw_ent;
490
491   switch (m)
492     {
493     case 'n':
494       out_string (pformat, prefix_len, filename);
495       break;
496     case 'N':
497       out_string (pformat, prefix_len, quote (filename));
498       if (S_ISLNK (statbuf->st_mode))
499         {
500           char *linkname = xreadlink_with_size (filename, statbuf->st_size);
501           if (linkname == NULL)
502             {
503               error (0, errno, _("cannot read symbolic link %s"),
504                      quote (filename));
505               return;
506             }
507           printf (" -> ");
508           out_string (pformat, prefix_len, quote (linkname));
509         }
510       break;
511     case 'd':
512       out_uint (pformat, prefix_len, statbuf->st_dev);
513       break;
514     case 'D':
515       out_uint_x (pformat, prefix_len, statbuf->st_dev);
516       break;
517     case 'i':
518       out_uint (pformat, prefix_len, statbuf->st_ino);
519       break;
520     case 'a':
521       out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
522       break;
523     case 'A':
524       out_string (pformat, prefix_len, human_access (statbuf));
525       break;
526     case 'f':
527       out_uint_x (pformat, prefix_len, statbuf->st_mode);
528       break;
529     case 'F':
530       out_string (pformat, prefix_len, file_type (statbuf));
531       break;
532     case 'h':
533       out_uint (pformat, prefix_len, statbuf->st_nlink);
534       break;
535     case 'u':
536       out_uint (pformat, prefix_len, statbuf->st_uid);
537       break;
538     case 'U':
539       setpwent ();
540       pw_ent = getpwuid (statbuf->st_uid);
541       out_string (pformat, prefix_len,
542                   pw_ent ? pw_ent->pw_name : "UNKNOWN");
543       break;
544     case 'g':
545       out_uint (pformat, prefix_len, statbuf->st_gid);
546       break;
547     case 'G':
548       setgrent ();
549       gw_ent = getgrgid (statbuf->st_gid);
550       out_string (pformat, prefix_len,
551                   gw_ent ? gw_ent->gr_name : "UNKNOWN");
552       break;
553     case 't':
554       out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
555       break;
556     case 'T':
557       out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
558       break;
559     case 's':
560       out_uint (pformat, prefix_len, statbuf->st_size);
561       break;
562     case 'B':
563       out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
564       break;
565     case 'b':
566       out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
567       break;
568     case 'o':
569       out_uint (pformat, prefix_len, statbuf->st_blksize);
570       break;
571     case 'x':
572       out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
573       break;
574     case 'X':
575       if (TYPE_SIGNED (time_t))
576         out_int (pformat, prefix_len, statbuf->st_atime);
577       else
578         out_uint (pformat, prefix_len, statbuf->st_atime);
579       break;
580     case 'y':
581       out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
582       break;
583     case 'Y':
584       if (TYPE_SIGNED (time_t))
585         out_int (pformat, prefix_len, statbuf->st_mtime);
586       else
587         out_uint (pformat, prefix_len, statbuf->st_mtime);
588       break;
589     case 'z':
590       out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
591       break;
592     case 'Z':
593       if (TYPE_SIGNED (time_t))
594         out_int (pformat, prefix_len, statbuf->st_ctime);
595       else
596         out_uint (pformat, prefix_len, statbuf->st_ctime);
597       break;
598     default:
599       fputc ('?', stdout);
600       break;
601     }
602 }
603
604 /* Output a single-character \ escape.  */
605
606 static void
607 print_esc_char (char c)
608 {
609   switch (c)
610     {
611     case 'a':                   /* Alert. */
612       c ='\a';
613       break;
614     case 'b':                   /* Backspace. */
615       c ='\b';
616       break;
617     case 'f':                   /* Form feed. */
618       c ='\f';
619       break;
620     case 'n':                   /* New line. */
621       c ='\n';
622       break;
623     case 'r':                   /* Carriage return. */
624       c ='\r';
625       break;
626     case 't':                   /* Horizontal tab. */
627       c ='\t';
628       break;
629     case 'v':                   /* Vertical tab. */
630       c ='\v';
631       break;
632     case '"':
633     case '\\':
634       break;
635     default:
636       error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
637       break;
638     }
639   putchar (c);
640 }
641
642 static void
643 print_it (char const *format, char const *filename,
644           void (*print_func) (char *, size_t, char, char const *, void const *),
645           void const *data)
646 {
647   /* Add 2 to accommodate our conversion of the stat `%s' format string
648      to the longer printf `%llu' one.  */
649   enum
650     {
651       MAX_ADDITIONAL_BYTES =
652         (MAX (sizeof PRIdMAX,
653               MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
654          - 1)
655     };
656   size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
657   char *dest = xmalloc (n_alloc);
658   char const *b;
659   for (b = format; *b; b++)
660     {
661       switch (*b)
662         {
663         case '%':
664           {
665             size_t len = strspn (b + 1, "#-+.I 0123456789");
666             char const *fmt_char = b + len + 1;
667             memcpy (dest, b, len + 1);
668
669             b = fmt_char;
670             switch (*fmt_char)
671               {
672               case '\0':
673                 --b;
674                 /* fall through */
675               case '%':
676                 if (0 < len)
677                   {
678                     dest[len + 1] = *fmt_char;
679                     dest[len + 2] = '\0';
680                     error (EXIT_FAILURE, 0, _("%s: invalid directive"),
681                            quotearg_colon (dest));
682                   }
683                 putchar ('%');
684                 break;
685               default:
686                 print_func (dest, len + 1, *fmt_char, filename, data);
687                 break;
688               }
689             break;
690           }
691
692         case '\\':
693           if ( ! interpret_backslash_escapes)
694             {
695               putchar ('\\');
696               break;
697             }
698           ++b;
699           if (isodigit (*b))
700             {
701               int esc_value = octtobin (*b);
702               int esc_length = 1;       /* number of octal digits */
703               for (++b; esc_length < 3 && isodigit (*b);
704                    ++esc_length, ++b)
705                 {
706                   esc_value = esc_value * 8 + octtobin (*b);
707                 }
708               putchar (esc_value);
709               --b;
710             }
711           else if (*b == 'x' && isxdigit (to_uchar (b[1])))
712             {
713               int esc_value = hextobin (b[1]);  /* Value of \xhh escape. */
714               /* A hexadecimal \xhh escape sequence must have
715                  1 or 2 hex. digits.  */
716               ++b;
717               if (isxdigit (to_uchar (b[1])))
718                 {
719                   ++b;
720                   esc_value = esc_value * 16 + hextobin (*b);
721                 }
722               putchar (esc_value);
723             }
724           else if (*b == '\0')
725             {
726               error (0, 0, _("warning: backslash at end of format"));
727               putchar ('\\');
728               /* Arrange to exit the loop.  */
729               --b;
730             }
731           else
732             {
733               print_esc_char (*b);
734             }
735           break;
736
737         default:
738           putchar (*b);
739           break;
740         }
741     }
742   free (dest);
743
744   fputs (trailing_delim, stdout);
745 }
746
747 /* Stat the file system and print what we find.  */
748 static bool
749 do_statfs (char const *filename, bool terse, char const *format)
750 {
751   STRUCT_STATVFS statfsbuf;
752
753   if (STATFS (filename, &statfsbuf) != 0)
754     {
755       error (0, errno, _("cannot read file system information for %s"),
756              quote (filename));
757       return false;
758     }
759
760   if (format == NULL)
761     {
762       format = (terse
763                 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
764                 : "  File: \"%n\"\n"
765                 "    ID: %-8i Namelen: %-7l Type: %T\n"
766                 "Block size: %-10s Fundamental block size: %S\n"
767                 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
768                 "Inodes: Total: %-10c Free: %d\n");
769     }
770
771   print_it (format, filename, print_statfs, &statfsbuf);
772   return true;
773 }
774
775 /* stat the file and print what we find */
776 static bool
777 do_stat (char const *filename, bool follow_links, bool terse,
778          char const *format)
779 {
780   struct stat statbuf;
781
782   if ((follow_links ? stat : lstat) (filename, &statbuf) != 0)
783     {
784       error (0, errno, _("cannot stat %s"), quote (filename));
785       return false;
786     }
787
788   if (format == NULL)
789     {
790       if (terse)
791         {
792           format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
793         }
794       else
795         {
796           /* Temporary hack to match original output until conditional
797              implemented.  */
798           if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
799             {
800               format =
801                 "  File: %N\n"
802                 "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
803                 "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
804                 " Device type: %t,%T\n"
805                 "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
806                 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
807             }
808           else
809             {
810               format =
811                 "  File: %N\n"
812                 "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
813                 "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
814                 "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
815                 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
816             }
817         }
818     }
819   print_it (format, filename, print_stat, &statbuf);
820   return true;
821 }
822
823 void
824 usage (int status)
825 {
826   if (status != EXIT_SUCCESS)
827     fprintf (stderr, _("Try `%s --help' for more information.\n"),
828              program_name);
829   else
830     {
831       printf (_("Usage: %s [OPTION] FILE...\n"), program_name);
832       fputs (_("\
833 Display file or file system status.\n\
834 \n\
835   -L, --dereference     follow links\n\
836   -f, --file-system     display file system status instead of file status\n\
837 "), stdout);
838       fputs (_("\
839   -c  --format=FORMAT   use the specified FORMAT instead of the default;\n\
840                           output a newline after each use of FORMAT\n\
841       --printf=FORMAT   like --format, but interpret backslash escapes,\n\
842                           and do not output a mandatory trailing newline.\n\
843                           If you want a newline, include \\n in FORMAT.\n\
844   -t, --terse           print the information in terse form\n\
845 "), stdout);
846       fputs (HELP_OPTION_DESCRIPTION, stdout);
847       fputs (VERSION_OPTION_DESCRIPTION, stdout);
848
849       fputs (_("\n\
850 The valid format sequences for files (without --file-system):\n\
851 \n\
852   %a   Access rights in octal\n\
853   %A   Access rights in human readable form\n\
854   %b   Number of blocks allocated (see %B)\n\
855   %B   The size in bytes of each block reported by %b\n\
856 "), stdout);
857       fputs (_("\
858   %d   Device number in decimal\n\
859   %D   Device number in hex\n\
860   %f   Raw mode in hex\n\
861   %F   File type\n\
862   %g   Group ID of owner\n\
863   %G   Group name of owner\n\
864 "), stdout);
865       fputs (_("\
866   %h   Number of hard links\n\
867   %i   Inode number\n\
868   %n   File name\n\
869   %N   Quoted file name with dereference if symbolic link\n\
870   %o   I/O block size\n\
871   %s   Total size, in bytes\n\
872   %t   Major device type in hex\n\
873   %T   Minor device type in hex\n\
874 "), stdout);
875       fputs (_("\
876   %u   User ID of owner\n\
877   %U   User name of owner\n\
878   %x   Time of last access\n\
879   %X   Time of last access as seconds since Epoch\n\
880   %y   Time of last modification\n\
881   %Y   Time of last modification as seconds since Epoch\n\
882   %z   Time of last change\n\
883   %Z   Time of last change as seconds since Epoch\n\
884 \n\
885 "), stdout);
886
887       fputs (_("\
888 Valid format sequences for file systems:\n\
889 \n\
890   %a   Free blocks available to non-superuser\n\
891   %b   Total data blocks in file system\n\
892   %c   Total file nodes in file system\n\
893   %d   Free file nodes in file system\n\
894   %f   Free blocks in file system\n\
895 "), stdout);
896       fputs (_("\
897   %i   File System ID in hex\n\
898   %l   Maximum length of filenames\n\
899   %n   File name\n\
900   %s   Block size (for faster transfers)\n\
901   %S   Fundamental block size (for block counts)\n\
902   %t   Type in hex\n\
903   %T   Type in human readable form\n\
904 "), stdout);
905       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
906       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
907     }
908   exit (status);
909 }
910
911 int
912 main (int argc, char *argv[])
913 {
914   int c;
915   int i;
916   bool follow_links = false;
917   bool fs = false;
918   bool terse = false;
919   char *format = NULL;
920   bool ok = true;
921
922   initialize_main (&argc, &argv);
923   program_name = argv[0];
924   setlocale (LC_ALL, "");
925   bindtextdomain (PACKAGE, LOCALEDIR);
926   textdomain (PACKAGE);
927
928   atexit (close_stdout);
929
930   while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
931     {
932       switch (c)
933         {
934         case PRINTF_OPTION:
935           format = optarg;
936           interpret_backslash_escapes = true;
937           trailing_delim = "";
938           break;
939
940         case 'c':
941           format = optarg;
942           interpret_backslash_escapes = false;
943           trailing_delim = "\n";
944           break;
945
946         case 'L':
947           follow_links = true;
948           break;
949
950         case 'f':
951           fs = true;
952           break;
953
954         case 't':
955           terse = true;
956           break;
957
958         case_GETOPT_HELP_CHAR;
959
960         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
961
962         default:
963           usage (EXIT_FAILURE);
964         }
965     }
966
967   if (argc == optind)
968     {
969       error (0, 0, _("missing operand"));
970       usage (EXIT_FAILURE);
971     }
972
973   for (i = optind; i < argc; i++)
974     ok &= (fs
975            ? do_statfs (argv[i], terse, format)
976            : do_stat (argv[i], follow_links, terse, format));
977
978   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
979 }