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