stat: fix a small memory leak with %N
[coreutils.git] / src / stat.c
blob18a746f9703bb8b791c26f59025bc461b01d6a07
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 Written by Michael Meskes. */
19 #include <config.h>
21 /* Keep this conditional in sync with the similar conditional in
22 ../m4/stat-prog.m4. */
23 #if (STAT_STATVFS \
24 && (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
25 || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
26 # define USE_STATVFS 1
27 #else
28 # define USE_STATVFS 0
29 #endif
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #if USE_STATVFS
37 # include <sys/statvfs.h>
38 #elif HAVE_SYS_VFS_H
39 # include <sys/vfs.h>
40 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
41 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
42 It does have statvfs.h, but shouldn't use it, since it doesn't
43 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
44 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
45 # include <sys/param.h>
46 # include <sys/mount.h>
47 # if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
48 /* Ultrix 4.4 needs these for the declaration of struct statfs. */
49 # include <netinet/in.h>
50 # include <nfs/nfs_clnt.h>
51 # include <nfs/vfs.h>
52 # endif
53 #elif HAVE_OS_H /* BeOS */
54 # include <fs_info.h>
55 #endif
56 #include <selinux/selinux.h>
58 #include "system.h"
60 #include "alignof.h"
61 #include "areadlink.h"
62 #include "error.h"
63 #include "file-type.h"
64 #include "filemode.h"
65 #include "fs.h"
66 #include "getopt.h"
67 #include "quote.h"
68 #include "quotearg.h"
69 #include "stat-time.h"
70 #include "strftime.h"
72 #if USE_STATVFS
73 # define STRUCT_STATVFS struct statvfs
74 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
75 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
76 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
77 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
78 # endif
79 # define STATFS statvfs
80 # define STATFS_FRSIZE(S) ((S)->f_frsize)
81 #else
82 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
83 # if HAVE_STRUCT_STATFS_F_NAMELEN
84 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
85 # endif
86 # define STATFS statfs
87 # if HAVE_OS_H /* BeOS */
88 /* BeOS has a statvfs function, but it does not return sensible values
89 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
90 f_fstypename. Use 'struct fs_info' instead. */
91 static int ATTRIBUTE_WARN_UNUSED_RESULT
92 statfs (char const *filename, struct fs_info *buf)
94 dev_t device = dev_for_path (filename);
95 if (device < 0)
97 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
98 : device == B_BAD_VALUE ? EINVAL
99 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
100 : device == B_NO_MEMORY ? ENOMEM
101 : device == B_FILE_ERROR ? EIO
102 : 0);
103 return -1;
105 /* If successful, buf->dev will be == device. */
106 return fs_stat_dev (device, buf);
108 # define f_fsid dev
109 # define f_blocks total_blocks
110 # define f_bfree free_blocks
111 # define f_bavail free_blocks
112 # define f_bsize io_size
113 # define f_files total_nodes
114 # define f_ffree free_nodes
115 # define STRUCT_STATVFS struct fs_info
116 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
117 # define STATFS_FRSIZE(S) ((S)->block_size)
118 # else
119 # define STRUCT_STATVFS struct statfs
120 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
121 # define STATFS_FRSIZE(S) 0
122 # endif
123 #endif
125 #ifdef SB_F_NAMEMAX
126 # define OUT_NAMEMAX out_uint
127 #else
128 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
129 # define SB_F_NAMEMAX(S) "*"
130 # define OUT_NAMEMAX out_string
131 #endif
133 #if HAVE_STRUCT_STATVFS_F_BASETYPE
134 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
135 #else
136 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
137 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
138 # elif HAVE_OS_H /* BeOS */
139 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
140 # endif
141 #endif
143 /* FIXME: these are used by printf.c, too */
144 #define isodigit(c) ('0' <= (c) && (c) <= '7')
145 #define octtobin(c) ((c) - '0')
146 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
147 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
149 #define PROGRAM_NAME "stat"
151 #define AUTHORS proper_name ("Michael Meskes")
153 enum
155 PRINTF_OPTION = CHAR_MAX + 1
158 static struct option const long_options[] =
160 {"context", no_argument, 0, 'Z'},
161 {"dereference", no_argument, NULL, 'L'},
162 {"file-system", no_argument, NULL, 'f'},
163 {"format", required_argument, NULL, 'c'},
164 {"printf", required_argument, NULL, PRINTF_OPTION},
165 {"terse", no_argument, NULL, 't'},
166 {GETOPT_HELP_OPTION_DECL},
167 {GETOPT_VERSION_OPTION_DECL},
168 {NULL, 0, NULL, 0}
171 /* Whether to follow symbolic links; True for --dereference (-L). */
172 static bool follow_links;
174 /* Whether to interpret backslash-escape sequences.
175 True for --printf=FMT, not for --format=FMT (-c). */
176 static bool interpret_backslash_escapes;
178 /* The trailing delimiter string:
179 "" for --printf=FMT, "\n" for --format=FMT (-c). */
180 static char const *trailing_delim = "";
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 (GNU/Linux).
187 But f_type may only exist in statfs (Cygwin). */
188 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
189 human_fstype (STRUCT_STATVFS const *statfsbuf)
191 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
192 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
193 #else
194 switch (statfsbuf->f_type)
196 # if defined __linux__
198 /* Compare with what's in libc:
199 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
200 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
201 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
202 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
203 | sort > sym_libc
204 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
205 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
206 | sort > sym_stat
207 diff -u sym_stat sym_libc
210 /* Also compare with the list in "man 2 statfs" using the
211 fs-magic-compare make target. */
213 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
214 statements must be followed by a hexadecimal constant in
215 a comment. The S_MAGIC_... name and constant are automatically
216 combined to produce the #define directives in fs.h. */
218 case S_MAGIC_ADFS: /* 0xADF5 */
219 return "adfs";
220 case S_MAGIC_AFFS: /* 0xADFF */
221 return "affs";
222 case S_MAGIC_AFS: /* 0x5346414F */
223 return "afs";
224 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 */
225 return "anon-inode FS";
226 case S_MAGIC_AUTOFS: /* 0x0187 */
227 return "autofs";
228 case S_MAGIC_BEFS: /* 0x42465331 */
229 return "befs";
230 case S_MAGIC_BFS: /* 0x1BADFACE */
231 return "bfs";
232 case S_MAGIC_BINFMT_MISC: /* 0x42494E4D */
233 return "binfmt_misc";
234 case S_MAGIC_BTRFS: /* 0x9123683E */
235 return "btrfs";
236 case S_MAGIC_CGROUP: /* 0x0027E0EB */
237 return "cgroupfs";
238 case S_MAGIC_CIFS: /* 0xFF534D42 */
239 return "cifs";
240 case S_MAGIC_CODA: /* 0x73757245 */
241 return "coda";
242 case S_MAGIC_COH: /* 0x012FF7B7 */
243 return "coh";
244 case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
245 return "cramfs";
246 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 */
247 return "cramfs-wend";
248 case S_MAGIC_DEBUGFS: /* 0x64626720 */
249 return "debugfs";
250 case S_MAGIC_DEVFS: /* 0x1373 */
251 return "devfs";
252 case S_MAGIC_DEVPTS: /* 0x1CD1 */
253 return "devpts";
254 case S_MAGIC_EFS: /* 0x00414A53 */
255 return "efs";
256 case S_MAGIC_EXT: /* 0x137D */
257 return "ext";
258 case S_MAGIC_EXT2: /* 0xEF53 */
259 return "ext2/ext3";
260 case S_MAGIC_EXT2_OLD: /* 0xEF51 */
261 return "ext2";
262 case S_MAGIC_FAT: /* 0x4006 */
263 return "fat";
264 case S_MAGIC_FUSEBLK: /* 0x65735546 */
265 return "fuseblk";
266 case S_MAGIC_FUSECTL: /* 0x65735543 */
267 return "fusectl";
268 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA */
269 return "futexfs";
270 case S_MAGIC_GFS: /* 0x1161970 */
271 return "gfs/gfs2";
272 case S_MAGIC_HFS: /* 0x4244 */
273 return "hfs";
274 case S_MAGIC_HPFS: /* 0xF995E849 */
275 return "hpfs";
276 case S_MAGIC_HUGETLBFS: /* 0x958458F6 */
277 return "hugetlbfs";
278 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA */
279 return "inotifyfs";
280 case S_MAGIC_ISOFS: /* 0x9660 */
281 return "isofs";
282 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
283 return "isofs";
284 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
285 return "isofs";
286 case S_MAGIC_JFFS: /* 0x07C0 */
287 return "jffs";
288 case S_MAGIC_JFFS2: /* 0x72B6 */
289 return "jffs2";
290 case S_MAGIC_JFS: /* 0x3153464A */
291 return "jfs";
292 case S_MAGIC_KAFS: /* 0x6B414653 */
293 return "k-afs";
294 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 */
295 return "lustre";
296 case S_MAGIC_MINIX: /* 0x137F */
297 return "minix";
298 case S_MAGIC_MINIX_30: /* 0x138F */
299 return "minix (30 char.)";
300 case S_MAGIC_MINIX_V2: /* 0x2468 */
301 return "minix v2";
302 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
303 return "minix v2 (30 char.)";
304 case S_MAGIC_MINIX_V3: /* 0x4D5A */
305 return "minix3";
306 case S_MAGIC_MSDOS: /* 0x4D44 */
307 return "msdos";
308 case S_MAGIC_NCP: /* 0x564C */
309 return "novell";
310 case S_MAGIC_NFS: /* 0x6969 */
311 return "nfs";
312 case S_MAGIC_NFSD: /* 0x6E667364 */
313 return "nfsd";
314 case S_MAGIC_NILFS: /* 0x3434 */
315 return "nilfs";
316 case S_MAGIC_NTFS: /* 0x5346544E */
317 return "ntfs";
318 case S_MAGIC_OPENPROM: /* 0x9FA1 */
319 return "openprom";
320 case S_MAGIC_OCFS2: /* 0x7461636f */
321 return "ocfs2";
322 case S_MAGIC_PROC: /* 0x9FA0 */
323 return "proc";
324 case S_MAGIC_QNX4: /* 0x002F */
325 return "qnx4";
326 case S_MAGIC_RAMFS: /* 0x858458F6 */
327 return "ramfs";
328 case S_MAGIC_REISERFS: /* 0x52654973 */
329 return "reiserfs";
330 case S_MAGIC_ROMFS: /* 0x7275 */
331 return "romfs";
332 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 */
333 return "rpc_pipefs";
334 case S_MAGIC_SECURITYFS: /* 0x73636673 */
335 return "securityfs";
336 case S_MAGIC_SELINUX: /* 0xF97CFF8C */
337 return "selinux";
338 case S_MAGIC_SMB: /* 0x517B */
339 return "smb";
340 case S_MAGIC_SOCKFS: /* 0x534F434B */
341 return "sockfs";
342 case S_MAGIC_SQUASHFS: /* 0x73717368 */
343 return "squashfs";
344 case S_MAGIC_SYSFS: /* 0x62656572 */
345 return "sysfs";
346 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
347 return "sysv2";
348 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
349 return "sysv4";
350 case S_MAGIC_TMPFS: /* 0x01021994 */
351 return "tmpfs";
352 case S_MAGIC_UDF: /* 0x15013346 */
353 return "udf";
354 case S_MAGIC_UFS: /* 0x00011954 */
355 return "ufs";
356 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
357 return "ufs";
358 case S_MAGIC_USBDEVFS: /* 0x9FA2 */
359 return "usbdevfs";
360 case S_MAGIC_VXFS: /* 0xA501FCF5 */
361 return "vxfs";
362 case S_MAGIC_XENFS: /* 0xABBA1974 */
363 return "xenfs";
364 case S_MAGIC_XENIX: /* 0x012FF7B4 */
365 return "xenix";
366 case S_MAGIC_XFS: /* 0x58465342 */
367 return "xfs";
368 case S_MAGIC_XIAFS: /* 0x012FD16D */
369 return "xia";
371 # elif __GNU__
372 case FSTYPE_UFS:
373 return "ufs";
374 case FSTYPE_NFS:
375 return "nfs";
376 case FSTYPE_GFS:
377 return "gfs";
378 case FSTYPE_LFS:
379 return "lfs";
380 case FSTYPE_SYSV:
381 return "sysv";
382 case FSTYPE_FTP:
383 return "ftp";
384 case FSTYPE_TAR:
385 return "tar";
386 case FSTYPE_AR:
387 return "ar";
388 case FSTYPE_CPIO:
389 return "cpio";
390 case FSTYPE_MSLOSS:
391 return "msloss";
392 case FSTYPE_CPM:
393 return "cpm";
394 case FSTYPE_HFS:
395 return "hfs";
396 case FSTYPE_DTFS:
397 return "dtfs";
398 case FSTYPE_GRFS:
399 return "grfs";
400 case FSTYPE_TERM:
401 return "term";
402 case FSTYPE_DEV:
403 return "dev";
404 case FSTYPE_PROC:
405 return "proc";
406 case FSTYPE_IFSOCK:
407 return "ifsock";
408 case FSTYPE_AFS:
409 return "afs";
410 case FSTYPE_DFS:
411 return "dfs";
412 case FSTYPE_PROC9:
413 return "proc9";
414 case FSTYPE_SOCKET:
415 return "socket";
416 case FSTYPE_MISC:
417 return "misc";
418 case FSTYPE_EXT2FS:
419 return "ext2/ext3";
420 case FSTYPE_HTTP:
421 return "http";
422 case FSTYPE_MEMFS:
423 return "memfs";
424 case FSTYPE_ISO9660:
425 return "iso9660";
426 # endif
427 default:
429 unsigned long int type = statfsbuf->f_type;
430 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
431 + (sizeof type * CHAR_BIT + 3) / 4];
432 sprintf (buf, "UNKNOWN (0x%lx)", type);
433 return buf;
436 #endif
439 static char * ATTRIBUTE_WARN_UNUSED_RESULT
440 human_access (struct stat const *statbuf)
442 static char modebuf[12];
443 filemodestring (statbuf, modebuf);
444 modebuf[10] = 0;
445 return modebuf;
448 static char * ATTRIBUTE_WARN_UNUSED_RESULT
449 human_time (struct timespec t)
451 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
452 (INT_STRLEN_BOUND (int) /* YYYY */
453 + 1 /* because YYYY might equal INT_MAX + 1900 */
454 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
455 struct tm const *tm = localtime (&t.tv_sec);
456 if (tm == NULL)
457 return timetostr (t.tv_sec, str);
458 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
459 return str;
462 static void
463 out_string (char *pformat, size_t prefix_len, char const *arg)
465 strcpy (pformat + prefix_len, "s");
466 printf (pformat, arg);
468 static void
469 out_int (char *pformat, size_t prefix_len, intmax_t arg)
471 strcpy (pformat + prefix_len, PRIdMAX);
472 printf (pformat, arg);
474 static void
475 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
477 strcpy (pformat + prefix_len, PRIuMAX);
478 printf (pformat, arg);
480 static void
481 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
483 strcpy (pformat + prefix_len, PRIoMAX);
484 printf (pformat, arg);
486 static void
487 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
489 strcpy (pformat + prefix_len, PRIxMAX);
490 printf (pformat, arg);
493 /* Very specialized function (modifies FORMAT), just so as to avoid
494 duplicating this code between both print_statfs and print_stat.
495 Return zero upon success, nonzero upon failure. */
496 static bool ATTRIBUTE_WARN_UNUSED_RESULT
497 out_file_context (char const *filename, char *pformat, size_t prefix_len)
499 char *scontext;
500 bool fail = false;
502 if ((follow_links
503 ? getfilecon (filename, &scontext)
504 : lgetfilecon (filename, &scontext)) < 0)
506 error (0, errno, _("failed to get security context of %s"),
507 quote (filename));
508 scontext = NULL;
509 fail = true;
511 strcpy (pformat + prefix_len, "s");
512 printf (pformat, (scontext ? scontext : "?"));
513 if (scontext)
514 freecon (scontext);
515 return fail;
518 /* Print statfs info. Return zero upon success, nonzero upon failure. */
519 static bool ATTRIBUTE_WARN_UNUSED_RESULT
520 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
521 void const *data)
523 STRUCT_STATVFS const *statfsbuf = data;
524 bool fail = false;
526 switch (m)
528 case 'n':
529 out_string (pformat, prefix_len, filename);
530 break;
532 case 'i':
534 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
535 uintmax_t fsid = statfsbuf->f_fsid;
536 #else
537 typedef unsigned int fsid_word;
538 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
539 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
540 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
541 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
543 /* Assume a little-endian word order, as that is compatible
544 with glibc's statvfs implementation. */
545 uintmax_t fsid = 0;
546 int words = sizeof statfsbuf->f_fsid / sizeof *p;
547 int i;
548 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
550 uintmax_t u = p[words - 1 - i];
551 fsid |= u << (i * CHAR_BIT * sizeof *p);
553 #endif
554 out_uint_x (pformat, prefix_len, fsid);
556 break;
558 case 'l':
559 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
560 break;
561 case 't':
562 #if HAVE_STRUCT_STATXFS_F_TYPE
563 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
564 #else
565 fputc ('?', stdout);
566 #endif
567 break;
568 case 'T':
569 out_string (pformat, prefix_len, human_fstype (statfsbuf));
570 break;
571 case 'b':
572 out_int (pformat, prefix_len, statfsbuf->f_blocks);
573 break;
574 case 'f':
575 out_int (pformat, prefix_len, statfsbuf->f_bfree);
576 break;
577 case 'a':
578 out_int (pformat, prefix_len, statfsbuf->f_bavail);
579 break;
580 case 's':
581 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
582 break;
583 case 'S':
585 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
586 if (! frsize)
587 frsize = statfsbuf->f_bsize;
588 out_uint (pformat, prefix_len, frsize);
590 break;
591 case 'c':
592 out_uint (pformat, prefix_len, statfsbuf->f_files);
593 break;
594 case 'd':
595 out_int (pformat, prefix_len, statfsbuf->f_ffree);
596 break;
597 case 'C':
598 fail |= out_file_context (filename, pformat, prefix_len);
599 break;
600 default:
601 fputc ('?', stdout);
602 break;
604 return fail;
607 /* Print stat info. Return zero upon success, nonzero upon failure. */
608 static bool
609 print_stat (char *pformat, size_t prefix_len, char m,
610 char const *filename, void const *data)
612 struct stat *statbuf = (struct stat *) data;
613 struct passwd *pw_ent;
614 struct group *gw_ent;
615 bool fail = false;
617 switch (m)
619 case 'n':
620 out_string (pformat, prefix_len, filename);
621 break;
622 case 'N':
623 out_string (pformat, prefix_len, quote (filename));
624 if (S_ISLNK (statbuf->st_mode))
626 char *linkname = areadlink_with_size (filename, statbuf->st_size);
627 if (linkname == NULL)
629 error (0, errno, _("cannot read symbolic link %s"),
630 quote (filename));
631 return true;
633 printf (" -> ");
634 out_string (pformat, prefix_len, quote (linkname));
635 free (linkname);
637 break;
638 case 'd':
639 out_uint (pformat, prefix_len, statbuf->st_dev);
640 break;
641 case 'D':
642 out_uint_x (pformat, prefix_len, statbuf->st_dev);
643 break;
644 case 'i':
645 out_uint (pformat, prefix_len, statbuf->st_ino);
646 break;
647 case 'a':
648 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
649 break;
650 case 'A':
651 out_string (pformat, prefix_len, human_access (statbuf));
652 break;
653 case 'f':
654 out_uint_x (pformat, prefix_len, statbuf->st_mode);
655 break;
656 case 'F':
657 out_string (pformat, prefix_len, file_type (statbuf));
658 break;
659 case 'h':
660 out_uint (pformat, prefix_len, statbuf->st_nlink);
661 break;
662 case 'u':
663 out_uint (pformat, prefix_len, statbuf->st_uid);
664 break;
665 case 'U':
666 setpwent ();
667 pw_ent = getpwuid (statbuf->st_uid);
668 out_string (pformat, prefix_len,
669 pw_ent ? pw_ent->pw_name : "UNKNOWN");
670 break;
671 case 'g':
672 out_uint (pformat, prefix_len, statbuf->st_gid);
673 break;
674 case 'G':
675 setgrent ();
676 gw_ent = getgrgid (statbuf->st_gid);
677 out_string (pformat, prefix_len,
678 gw_ent ? gw_ent->gr_name : "UNKNOWN");
679 break;
680 case 't':
681 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
682 break;
683 case 'T':
684 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
685 break;
686 case 's':
687 out_uint (pformat, prefix_len, statbuf->st_size);
688 break;
689 case 'B':
690 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
691 break;
692 case 'b':
693 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
694 break;
695 case 'o':
696 out_uint (pformat, prefix_len, statbuf->st_blksize);
697 break;
698 case 'x':
699 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
700 break;
701 case 'X':
702 if (TYPE_SIGNED (time_t))
703 out_int (pformat, prefix_len, statbuf->st_atime);
704 else
705 out_uint (pformat, prefix_len, statbuf->st_atime);
706 break;
707 case 'y':
708 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
709 break;
710 case 'Y':
711 if (TYPE_SIGNED (time_t))
712 out_int (pformat, prefix_len, statbuf->st_mtime);
713 else
714 out_uint (pformat, prefix_len, statbuf->st_mtime);
715 break;
716 case 'z':
717 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
718 break;
719 case 'Z':
720 if (TYPE_SIGNED (time_t))
721 out_int (pformat, prefix_len, statbuf->st_ctime);
722 else
723 out_uint (pformat, prefix_len, statbuf->st_ctime);
724 break;
725 case 'C':
726 fail |= out_file_context (filename, pformat, prefix_len);
727 break;
728 default:
729 fputc ('?', stdout);
730 break;
732 return fail;
735 /* Output a single-character \ escape. */
737 static void
738 print_esc_char (char c)
740 switch (c)
742 case 'a': /* Alert. */
743 c ='\a';
744 break;
745 case 'b': /* Backspace. */
746 c ='\b';
747 break;
748 case 'e': /* Escape. */
749 c ='\x1B';
750 break;
751 case 'f': /* Form feed. */
752 c ='\f';
753 break;
754 case 'n': /* New line. */
755 c ='\n';
756 break;
757 case 'r': /* Carriage return. */
758 c ='\r';
759 break;
760 case 't': /* Horizontal tab. */
761 c ='\t';
762 break;
763 case 'v': /* Vertical tab. */
764 c ='\v';
765 break;
766 case '"':
767 case '\\':
768 break;
769 default:
770 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
771 break;
773 putchar (c);
776 /* Print the information specified by the format string, FORMAT,
777 calling PRINT_FUNC for each %-directive encountered.
778 Return zero upon success, nonzero upon failure. */
779 static bool ATTRIBUTE_WARN_UNUSED_RESULT
780 print_it (char const *format, char const *filename,
781 bool (*print_func) (char *, size_t, char, char const *, void const *),
782 void const *data)
784 bool fail = false;
786 /* Add 2 to accommodate our conversion of the stat `%s' format string
787 to the longer printf `%llu' one. */
788 enum
790 MAX_ADDITIONAL_BYTES =
791 (MAX (sizeof PRIdMAX,
792 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
793 - 1)
795 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
796 char *dest = xmalloc (n_alloc);
797 char const *b;
798 for (b = format; *b; b++)
800 switch (*b)
802 case '%':
804 size_t len = strspn (b + 1, "#-+.I 0123456789");
805 char const *fmt_char = b + len + 1;
806 memcpy (dest, b, len + 1);
808 b = fmt_char;
809 switch (*fmt_char)
811 case '\0':
812 --b;
813 /* fall through */
814 case '%':
815 if (0 < len)
817 dest[len + 1] = *fmt_char;
818 dest[len + 2] = '\0';
819 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
820 quotearg_colon (dest));
822 putchar ('%');
823 break;
824 default:
825 fail |= print_func (dest, len + 1, *fmt_char, filename, data);
826 break;
828 break;
831 case '\\':
832 if ( ! interpret_backslash_escapes)
834 putchar ('\\');
835 break;
837 ++b;
838 if (isodigit (*b))
840 int esc_value = octtobin (*b);
841 int esc_length = 1; /* number of octal digits */
842 for (++b; esc_length < 3 && isodigit (*b);
843 ++esc_length, ++b)
845 esc_value = esc_value * 8 + octtobin (*b);
847 putchar (esc_value);
848 --b;
850 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
852 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
853 /* A hexadecimal \xhh escape sequence must have
854 1 or 2 hex. digits. */
855 ++b;
856 if (isxdigit (to_uchar (b[1])))
858 ++b;
859 esc_value = esc_value * 16 + hextobin (*b);
861 putchar (esc_value);
863 else if (*b == '\0')
865 error (0, 0, _("warning: backslash at end of format"));
866 putchar ('\\');
867 /* Arrange to exit the loop. */
868 --b;
870 else
872 print_esc_char (*b);
874 break;
876 default:
877 putchar (*b);
878 break;
881 free (dest);
883 fputs (trailing_delim, stdout);
885 return fail;
888 /* Stat the file system and print what we find. */
889 static bool ATTRIBUTE_WARN_UNUSED_RESULT
890 do_statfs (char const *filename, bool terse, char const *format)
892 STRUCT_STATVFS statfsbuf;
894 if (STREQ (filename, "-"))
896 error (0, 0, _("using %s to denote standard input does not work"
897 " in file system mode"), quote (filename));
898 return false;
901 if (STATFS (filename, &statfsbuf) != 0)
903 error (0, errno, _("cannot read file system information for %s"),
904 quote (filename));
905 return false;
908 if (format == NULL)
910 format = (terse
911 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
912 : " File: \"%n\"\n"
913 " ID: %-8i Namelen: %-7l Type: %T\n"
914 "Block size: %-10s Fundamental block size: %S\n"
915 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
916 "Inodes: Total: %-10c Free: %d\n");
919 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
920 return ! fail;
923 /* stat the file and print what we find */
924 static bool ATTRIBUTE_WARN_UNUSED_RESULT
925 do_stat (char const *filename, bool terse, char const *format)
927 struct stat statbuf;
929 if (STREQ (filename, "-"))
931 if (fstat (STDIN_FILENO, &statbuf) != 0)
933 error (0, errno, _("cannot stat standard input"));
934 return false;
937 /* We can't use the shorter
938 (follow_links?stat:lstat) (filename, &statbug)
939 since stat might be a function-like macro. */
940 else if ((follow_links
941 ? stat (filename, &statbuf)
942 : lstat (filename, &statbuf)) != 0)
944 error (0, errno, _("cannot stat %s"), quote (filename));
945 return false;
948 if (format == NULL)
950 if (terse)
952 format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
954 else
956 /* Temporary hack to match original output until conditional
957 implemented. */
958 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
960 format =
961 " File: %N\n"
962 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
963 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
964 " Device type: %t,%T\n"
965 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
966 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
968 else
970 format =
971 " File: %N\n"
972 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
973 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
974 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
975 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
979 bool fail = print_it (format, filename, print_stat, &statbuf);
980 return ! fail;
983 void
984 usage (int status)
986 if (status != EXIT_SUCCESS)
987 fprintf (stderr, _("Try `%s --help' for more information.\n"),
988 program_name);
989 else
991 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
992 fputs (_("\
993 Display file or file system status.\n\
995 -L, --dereference follow links\n\
996 -f, --file-system display file system status instead of file status\n\
997 "), stdout);
998 fputs (_("\
999 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1000 output a newline after each use of FORMAT\n\
1001 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1002 and do not output a mandatory trailing newline.\n\
1003 If you want a newline, include \\n in FORMAT\n\
1004 -t, --terse print the information in terse form\n\
1005 "), stdout);
1006 fputs (HELP_OPTION_DESCRIPTION, stdout);
1007 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1009 fputs (_("\n\
1010 The valid format sequences for files (without --file-system):\n\
1012 %a Access rights in octal\n\
1013 %A Access rights in human readable form\n\
1014 %b Number of blocks allocated (see %B)\n\
1015 %B The size in bytes of each block reported by %b\n\
1016 %C SELinux security context string\n\
1017 "), stdout);
1018 fputs (_("\
1019 %d Device number in decimal\n\
1020 %D Device number in hex\n\
1021 %f Raw mode in hex\n\
1022 %F File type\n\
1023 %g Group ID of owner\n\
1024 %G Group name of owner\n\
1025 "), stdout);
1026 fputs (_("\
1027 %h Number of hard links\n\
1028 %i Inode number\n\
1029 %n File name\n\
1030 %N Quoted file name with dereference if symbolic link\n\
1031 %o I/O block size\n\
1032 %s Total size, in bytes\n\
1033 %t Major device type in hex\n\
1034 %T Minor device type in hex\n\
1035 "), stdout);
1036 fputs (_("\
1037 %u User ID of owner\n\
1038 %U User name of owner\n\
1039 %x Time of last access\n\
1040 %X Time of last access as seconds since Epoch\n\
1041 %y Time of last modification\n\
1042 %Y Time of last modification as seconds since Epoch\n\
1043 %z Time of last change\n\
1044 %Z Time of last change as seconds since Epoch\n\
1046 "), stdout);
1048 fputs (_("\
1049 Valid format sequences for file systems:\n\
1051 %a Free blocks available to non-superuser\n\
1052 %b Total data blocks in file system\n\
1053 %c Total file nodes in file system\n\
1054 %d Free file nodes in file system\n\
1055 %f Free blocks in file system\n\
1056 %C SELinux security context string\n\
1057 "), stdout);
1058 fputs (_("\
1059 %i File System ID in hex\n\
1060 %l Maximum length of filenames\n\
1061 %n File name\n\
1062 %s Block size (for faster transfers)\n\
1063 %S Fundamental block size (for block counts)\n\
1064 %t Type in hex\n\
1065 %T Type in human readable form\n\
1066 "), stdout);
1067 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1068 emit_ancillary_info ();
1070 exit (status);
1074 main (int argc, char *argv[])
1076 int c;
1077 int i;
1078 bool fs = false;
1079 bool terse = false;
1080 char *format = NULL;
1081 bool ok = true;
1083 initialize_main (&argc, &argv);
1084 set_program_name (argv[0]);
1085 setlocale (LC_ALL, "");
1086 bindtextdomain (PACKAGE, LOCALEDIR);
1087 textdomain (PACKAGE);
1089 atexit (close_stdout);
1091 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1093 switch (c)
1095 case PRINTF_OPTION:
1096 format = optarg;
1097 interpret_backslash_escapes = true;
1098 trailing_delim = "";
1099 break;
1101 case 'c':
1102 format = optarg;
1103 interpret_backslash_escapes = false;
1104 trailing_delim = "\n";
1105 break;
1107 case 'L':
1108 follow_links = true;
1109 break;
1111 case 'f':
1112 fs = true;
1113 break;
1115 case 't':
1116 terse = true;
1117 break;
1119 case_GETOPT_HELP_CHAR;
1121 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1123 default:
1124 usage (EXIT_FAILURE);
1128 if (argc == optind)
1130 error (0, 0, _("missing operand"));
1131 usage (EXIT_FAILURE);
1134 for (i = optind; i < argc; i++)
1135 ok &= (fs
1136 ? do_statfs (argv[i], terse, format)
1137 : do_stat (argv[i], terse, format));
1139 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);