19a63ce63SRobert Garrett /*
29a63ce63SRobert Garrett * Copyright (c) 2002 The NetBSD Foundation, Inc.
39a63ce63SRobert Garrett * All rights reserved.
49a63ce63SRobert Garrett *
59a63ce63SRobert Garrett * This code is derived from software contributed to The NetBSD Foundation
69a63ce63SRobert Garrett * by Andrew Brown.
79a63ce63SRobert Garrett *
89a63ce63SRobert Garrett * Redistribution and use in source and binary forms, with or without
99a63ce63SRobert Garrett * modification, are permitted provided that the following conditions
109a63ce63SRobert Garrett * are met:
119a63ce63SRobert Garrett * 1. Redistributions of source code must retain the above copyright
129a63ce63SRobert Garrett * notice, this list of conditions and the following disclaimer.
139a63ce63SRobert Garrett * 2. Redistributions in binary form must reproduce the above copyright
149a63ce63SRobert Garrett * notice, this list of conditions and the following disclaimer in the
159a63ce63SRobert Garrett * documentation and/or other materials provided with the distribution.
169a63ce63SRobert Garrett *
179a63ce63SRobert Garrett * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
189a63ce63SRobert Garrett * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
199a63ce63SRobert Garrett * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
209a63ce63SRobert Garrett * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
219a63ce63SRobert Garrett * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
229a63ce63SRobert Garrett * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
239a63ce63SRobert Garrett * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
249a63ce63SRobert Garrett * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
259a63ce63SRobert Garrett * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
269a63ce63SRobert Garrett * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
279a63ce63SRobert Garrett * POSSIBILITY OF SUCH DAMAGE.
2841250c5dSMatthew Dillon *
2946659ea9SSascha Wildner * $NetBSD: stat.c,v 1.33 2011/01/15 22:54:10 njoly Exp $
3046659ea9SSascha Wildner * $OpenBSD: stat.c,v 1.14 2009/06/24 09:44:25 sobrado Exp $
3146659ea9SSascha Wildner * $FreeBSD: head/usr.bin/stat/stat.c 265893 2014-05-11 18:49:18Z thomas $
329a63ce63SRobert Garrett */
339a63ce63SRobert Garrett
3446659ea9SSascha Wildner #include <sys/param.h>
359a63ce63SRobert Garrett #include <sys/types.h>
369a63ce63SRobert Garrett #include <sys/stat.h>
3746659ea9SSascha Wildner #include <sys/mount.h>
389a63ce63SRobert Garrett
399a63ce63SRobert Garrett #include <ctype.h>
409a63ce63SRobert Garrett #include <err.h>
4146659ea9SSascha Wildner #include <errno.h>
429a63ce63SRobert Garrett #include <grp.h>
439a63ce63SRobert Garrett #include <limits.h>
4446659ea9SSascha Wildner #include <paths.h>
459a63ce63SRobert Garrett #include <pwd.h>
469a63ce63SRobert Garrett #include <stdio.h>
479a63ce63SRobert Garrett #include <stdlib.h>
489a63ce63SRobert Garrett #include <string.h>
499a63ce63SRobert Garrett #include <time.h>
509a63ce63SRobert Garrett #include <unistd.h>
519a63ce63SRobert Garrett
52*e9445c5dSzrj #ifdef BOOTSTRAPPING
53*e9445c5dSzrj #define HAVE_DEVNAME 0
54*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_FLAGS 0
55*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_GEN 0
56*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_BIRTHTIME 0
57*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_ATIM 1
58*e9445c5dSzrj #else
59*e9445c5dSzrj #define HAVE_DEVNAME 1
60*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_FLAGS 1
61*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_GEN 1
62*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_BIRTHTIME 0
63*e9445c5dSzrj #define HAVE_STRUCT_STAT_ST_ATIM 1
64*e9445c5dSzrj #endif
65*e9445c5dSzrj
66d542b1f4SSimon Schubert #if HAVE_STRUCT_STAT_ST_FLAGS
67d542b1f4SSimon Schubert #define DEF_F "%#Xf "
68d542b1f4SSimon Schubert #define RAW_F "%f "
69d542b1f4SSimon Schubert #define SHELL_F " st_flags=%f"
70d542b1f4SSimon Schubert #else /* HAVE_STRUCT_STAT_ST_FLAGS */
71d542b1f4SSimon Schubert #define DEF_F
72d542b1f4SSimon Schubert #define RAW_F
73d542b1f4SSimon Schubert #define SHELL_F
74d542b1f4SSimon Schubert #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
75d542b1f4SSimon Schubert
76d542b1f4SSimon Schubert #if HAVE_STRUCT_STAT_ST_BIRTHTIME
77d542b1f4SSimon Schubert #define DEF_B "\"%SB\" "
78d542b1f4SSimon Schubert #define RAW_B "%B "
79d542b1f4SSimon Schubert #define SHELL_B "st_birthtime=%B "
80d542b1f4SSimon Schubert #else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
81d542b1f4SSimon Schubert #define DEF_B
82d542b1f4SSimon Schubert #define RAW_B
83d542b1f4SSimon Schubert #define SHELL_B
84d542b1f4SSimon Schubert #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
85d542b1f4SSimon Schubert
86d542b1f4SSimon Schubert #if HAVE_STRUCT_STAT_ST_ATIM
87d542b1f4SSimon Schubert #define st_atimespec st_atim
88d542b1f4SSimon Schubert #define st_ctimespec st_ctim
89d542b1f4SSimon Schubert #define st_mtimespec st_mtim
90d542b1f4SSimon Schubert #endif /* HAVE_STRUCT_STAT_ST_ATIM */
91d542b1f4SSimon Schubert
929a63ce63SRobert Garrett #define DEF_FORMAT \
93d542b1f4SSimon Schubert "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \
94d542b1f4SSimon Schubert "%k %b " DEF_F "%N"
95d542b1f4SSimon Schubert #define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \
96d542b1f4SSimon Schubert "%k %b " RAW_F "%N"
97d542b1f4SSimon Schubert #define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY"
98d542b1f4SSimon Schubert #define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY"
999a63ce63SRobert Garrett #define SHELL_FORMAT \
1009a63ce63SRobert Garrett "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \
1019a63ce63SRobert Garrett "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \
102d542b1f4SSimon Schubert "st_atime=%a st_mtime=%m st_ctime=%c " SHELL_B \
103d542b1f4SSimon Schubert "st_blksize=%k st_blocks=%b" SHELL_F
1049a63ce63SRobert Garrett #define LINUX_FORMAT \
1059a63ce63SRobert Garrett " File: \"%N\"%n" \
1069a63ce63SRobert Garrett " Size: %-11z FileType: %HT%n" \
10746659ea9SSascha Wildner " Mode: (%OMp%03OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \
1089a63ce63SRobert Garrett "Device: %Hd,%Ld Inode: %i Links: %l%n" \
109d542b1f4SSimon Schubert "Access: %Sa%n" \
110d542b1f4SSimon Schubert "Modify: %Sm%n" \
111d542b1f4SSimon Schubert "Change: %Sc"
1129a63ce63SRobert Garrett
1139a63ce63SRobert Garrett #define TIME_FORMAT "%b %e %T %Y"
1149a63ce63SRobert Garrett
1159a63ce63SRobert Garrett #define FLAG_POUND 0x01
1169a63ce63SRobert Garrett #define FLAG_SPACE 0x02
1179a63ce63SRobert Garrett #define FLAG_PLUS 0x04
1189a63ce63SRobert Garrett #define FLAG_ZERO 0x08
1199a63ce63SRobert Garrett #define FLAG_MINUS 0x10
1209a63ce63SRobert Garrett
1219a63ce63SRobert Garrett /*
1229a63ce63SRobert Garrett * These format characters must all be unique, except the magic one.
1239a63ce63SRobert Garrett */
1249a63ce63SRobert Garrett #define FMT_MAGIC '%'
1259a63ce63SRobert Garrett #define FMT_DOT '.'
1269a63ce63SRobert Garrett
1279a63ce63SRobert Garrett #define SIMPLE_NEWLINE 'n'
1289a63ce63SRobert Garrett #define SIMPLE_TAB 't'
1299a63ce63SRobert Garrett #define SIMPLE_PERCENT '%'
1309a63ce63SRobert Garrett #define SIMPLE_NUMBER '@'
1319a63ce63SRobert Garrett
1329a63ce63SRobert Garrett #define FMT_POUND '#'
1339a63ce63SRobert Garrett #define FMT_SPACE ' '
1349a63ce63SRobert Garrett #define FMT_PLUS '+'
1359a63ce63SRobert Garrett #define FMT_ZERO '0'
1369a63ce63SRobert Garrett #define FMT_MINUS '-'
1379a63ce63SRobert Garrett
1389a63ce63SRobert Garrett #define FMT_DECIMAL 'D'
1399a63ce63SRobert Garrett #define FMT_OCTAL 'O'
1409a63ce63SRobert Garrett #define FMT_UNSIGNED 'U'
1419a63ce63SRobert Garrett #define FMT_HEX 'X'
1429a63ce63SRobert Garrett #define FMT_FLOAT 'F'
1439a63ce63SRobert Garrett #define FMT_STRING 'S'
1449a63ce63SRobert Garrett
1459a63ce63SRobert Garrett #define FMTF_DECIMAL 0x01
1469a63ce63SRobert Garrett #define FMTF_OCTAL 0x02
1479a63ce63SRobert Garrett #define FMTF_UNSIGNED 0x04
1489a63ce63SRobert Garrett #define FMTF_HEX 0x08
1499a63ce63SRobert Garrett #define FMTF_FLOAT 0x10
1509a63ce63SRobert Garrett #define FMTF_STRING 0x20
1519a63ce63SRobert Garrett
1529a63ce63SRobert Garrett #define HIGH_PIECE 'H'
1539a63ce63SRobert Garrett #define MIDDLE_PIECE 'M'
1549a63ce63SRobert Garrett #define LOW_PIECE 'L'
1559a63ce63SRobert Garrett
15646659ea9SSascha Wildner #define SHOW_realpath 'R'
1579a63ce63SRobert Garrett #define SHOW_st_dev 'd'
1589a63ce63SRobert Garrett #define SHOW_st_ino 'i'
1599a63ce63SRobert Garrett #define SHOW_st_mode 'p'
1609a63ce63SRobert Garrett #define SHOW_st_nlink 'l'
1619a63ce63SRobert Garrett #define SHOW_st_uid 'u'
1629a63ce63SRobert Garrett #define SHOW_st_gid 'g'
1639a63ce63SRobert Garrett #define SHOW_st_rdev 'r'
1649a63ce63SRobert Garrett #define SHOW_st_atime 'a'
1659a63ce63SRobert Garrett #define SHOW_st_mtime 'm'
1669a63ce63SRobert Garrett #define SHOW_st_ctime 'c'
167d542b1f4SSimon Schubert #define SHOW_st_btime 'B'
1689a63ce63SRobert Garrett #define SHOW_st_size 'z'
1699a63ce63SRobert Garrett #define SHOW_st_blocks 'b'
1709a63ce63SRobert Garrett #define SHOW_st_blksize 'k'
1719a63ce63SRobert Garrett #define SHOW_st_flags 'f'
1729a63ce63SRobert Garrett #define SHOW_st_gen 'v'
1739a63ce63SRobert Garrett #define SHOW_symlink 'Y'
1749a63ce63SRobert Garrett #define SHOW_filetype 'T'
1759a63ce63SRobert Garrett #define SHOW_filename 'N'
1769a63ce63SRobert Garrett #define SHOW_sizerdev 'Z'
1779a63ce63SRobert Garrett
1786d08986dSSascha Wildner static void usage(const char *) __dead2;
17933437a44SSascha Wildner static void output(const struct stat *, const char *,
18046659ea9SSascha Wildner const char *, int, int);
18133437a44SSascha Wildner static int format1(const struct stat *, /* stat info */
1829a63ce63SRobert Garrett const char *, /* the file name */
1839a63ce63SRobert Garrett const char *, int, /* the format string itself */
1849a63ce63SRobert Garrett char *, size_t, /* a place to put the output */
1859a63ce63SRobert Garrett int, int, int, int, /* the parsed format */
1869a63ce63SRobert Garrett int, int);
187*e9445c5dSzrj #if HAVE_DEVNAME
18833437a44SSascha Wildner static int hex2byte(const char [2]);
189*e9445c5dSzrj #endif
19046659ea9SSascha Wildner #if HAVE_STRUCT_STAT_ST_FLAGS
19133437a44SSascha Wildner static char *xfflagstostr(unsigned long);
19246659ea9SSascha Wildner #endif
1939a63ce63SRobert Garrett
19446659ea9SSascha Wildner static const char *timefmt;
19546659ea9SSascha Wildner static int linkfail;
1969a63ce63SRobert Garrett
1979a63ce63SRobert Garrett #define addchar(s, c, nl) \
1989a63ce63SRobert Garrett do { \
19946659ea9SSascha Wildner fputc((c), (s)); \
2009a63ce63SRobert Garrett (*nl) = ((c) == '\n'); \
2019a63ce63SRobert Garrett } while (0/*CONSTCOND*/)
2029a63ce63SRobert Garrett
2039a63ce63SRobert Garrett int
main(int argc,char * argv[])2049a63ce63SRobert Garrett main(int argc, char *argv[])
2059a63ce63SRobert Garrett {
2069a63ce63SRobert Garrett struct stat st;
2079a63ce63SRobert Garrett int ch, rc, errs, am_readlink;
20846659ea9SSascha Wildner int lsF, fmtchar, usestat, nfs_handle, fn, nonl, quiet;
20946659ea9SSascha Wildner const char *statfmt, *options, *synopsis;
210*e9445c5dSzrj #if HAVE_DEVNAME
21146659ea9SSascha Wildner char dname[sizeof _PATH_DEV + MNAMELEN] = _PATH_DEV;
21246659ea9SSascha Wildner fhandle_t fhnd;
213*e9445c5dSzrj #endif
21446659ea9SSascha Wildner const char *file;
2159a63ce63SRobert Garrett
2169a63ce63SRobert Garrett am_readlink = 0;
2179a63ce63SRobert Garrett lsF = 0;
2189a63ce63SRobert Garrett fmtchar = '\0';
2199a63ce63SRobert Garrett usestat = 0;
22046659ea9SSascha Wildner nfs_handle = 0;
2219a63ce63SRobert Garrett nonl = 0;
2229a63ce63SRobert Garrett quiet = 0;
2239a63ce63SRobert Garrett linkfail = 0;
2249a63ce63SRobert Garrett statfmt = NULL;
2259a63ce63SRobert Garrett timefmt = NULL;
2269a63ce63SRobert Garrett
2279a63ce63SRobert Garrett if (strcmp(getprogname(), "readlink") == 0) {
2289a63ce63SRobert Garrett am_readlink = 1;
22946659ea9SSascha Wildner options = "fn";
23046659ea9SSascha Wildner synopsis = "[-fn] [file ...]";
2319a63ce63SRobert Garrett statfmt = "%Y";
2329a63ce63SRobert Garrett fmtchar = 'f';
2339a63ce63SRobert Garrett quiet = 1;
2349a63ce63SRobert Garrett } else {
23546659ea9SSascha Wildner options = "f:FHlLnqrst:x";
23646659ea9SSascha Wildner synopsis = "[-FLnq] [-f format | -l | -r | -s | -x] "
23746659ea9SSascha Wildner "[-t timefmt] [file|handle ...]";
2389a63ce63SRobert Garrett }
2399a63ce63SRobert Garrett
2409a63ce63SRobert Garrett while ((ch = getopt(argc, argv, options)) != -1)
2419a63ce63SRobert Garrett switch (ch) {
2429a63ce63SRobert Garrett case 'F':
2439a63ce63SRobert Garrett lsF = 1;
2449a63ce63SRobert Garrett break;
24546659ea9SSascha Wildner case 'H':
24646659ea9SSascha Wildner nfs_handle = 1;
24746659ea9SSascha Wildner break;
2489a63ce63SRobert Garrett case 'L':
2499a63ce63SRobert Garrett usestat = 1;
2509a63ce63SRobert Garrett break;
2519a63ce63SRobert Garrett case 'n':
2529a63ce63SRobert Garrett nonl = 1;
2539a63ce63SRobert Garrett break;
2549a63ce63SRobert Garrett case 'q':
2559a63ce63SRobert Garrett quiet = 1;
2569a63ce63SRobert Garrett break;
2579a63ce63SRobert Garrett case 'f':
25846659ea9SSascha Wildner if (am_readlink) {
25946659ea9SSascha Wildner statfmt = "%R";
26046659ea9SSascha Wildner break;
26146659ea9SSascha Wildner }
2629a63ce63SRobert Garrett statfmt = optarg;
2639a63ce63SRobert Garrett /* FALLTHROUGH */
2649a63ce63SRobert Garrett case 'l':
2659a63ce63SRobert Garrett case 'r':
2669a63ce63SRobert Garrett case 's':
2679a63ce63SRobert Garrett case 'x':
2689a63ce63SRobert Garrett if (fmtchar != 0)
2699a63ce63SRobert Garrett errx(1, "can't use format '%c' with '%c'",
2709a63ce63SRobert Garrett fmtchar, ch);
2719a63ce63SRobert Garrett fmtchar = ch;
2729a63ce63SRobert Garrett break;
2739a63ce63SRobert Garrett case 't':
2749a63ce63SRobert Garrett timefmt = optarg;
2759a63ce63SRobert Garrett break;
2769a63ce63SRobert Garrett default:
2779a63ce63SRobert Garrett usage(synopsis);
2789a63ce63SRobert Garrett }
2799a63ce63SRobert Garrett
2809a63ce63SRobert Garrett argc -= optind;
2819a63ce63SRobert Garrett argv += optind;
2829a63ce63SRobert Garrett fn = 1;
2839a63ce63SRobert Garrett
2849a63ce63SRobert Garrett if (fmtchar == '\0') {
2859a63ce63SRobert Garrett if (lsF)
2869a63ce63SRobert Garrett fmtchar = 'l';
2879a63ce63SRobert Garrett else {
2889a63ce63SRobert Garrett fmtchar = 'f';
2899a63ce63SRobert Garrett statfmt = DEF_FORMAT;
2909a63ce63SRobert Garrett }
2919a63ce63SRobert Garrett }
2929a63ce63SRobert Garrett
2939a63ce63SRobert Garrett if (lsF && fmtchar != 'l')
2949a63ce63SRobert Garrett errx(1, "can't use format '%c' with -F", fmtchar);
2959a63ce63SRobert Garrett
2969a63ce63SRobert Garrett switch (fmtchar) {
2979a63ce63SRobert Garrett case 'f':
2989a63ce63SRobert Garrett /* statfmt already set */
2999a63ce63SRobert Garrett break;
3009a63ce63SRobert Garrett case 'l':
3019a63ce63SRobert Garrett statfmt = lsF ? LSF_FORMAT : LS_FORMAT;
3029a63ce63SRobert Garrett break;
3039a63ce63SRobert Garrett case 'r':
3049a63ce63SRobert Garrett statfmt = RAW_FORMAT;
3059a63ce63SRobert Garrett break;
3069a63ce63SRobert Garrett case 's':
3079a63ce63SRobert Garrett statfmt = SHELL_FORMAT;
3089a63ce63SRobert Garrett break;
3099a63ce63SRobert Garrett case 'x':
3109a63ce63SRobert Garrett statfmt = LINUX_FORMAT;
3119a63ce63SRobert Garrett if (timefmt == NULL)
3129a63ce63SRobert Garrett timefmt = "%c";
3139a63ce63SRobert Garrett break;
3149a63ce63SRobert Garrett default:
3159a63ce63SRobert Garrett usage(synopsis);
3169a63ce63SRobert Garrett /*NOTREACHED*/
3179a63ce63SRobert Garrett }
3189a63ce63SRobert Garrett
3199a63ce63SRobert Garrett if (timefmt == NULL)
3209a63ce63SRobert Garrett timefmt = TIME_FORMAT;
3219a63ce63SRobert Garrett
3229a63ce63SRobert Garrett errs = 0;
3239a63ce63SRobert Garrett do {
32446659ea9SSascha Wildner if (argc == 0) {
325*e9445c5dSzrj #if HAVE_DEVNAME
32646659ea9SSascha Wildner if (fdevname_r(STDIN_FILENO, dname +
32746659ea9SSascha Wildner sizeof _PATH_DEV - 1, MNAMELEN) == 0)
32846659ea9SSascha Wildner file = dname;
3299a63ce63SRobert Garrett else
330*e9445c5dSzrj #endif
33146659ea9SSascha Wildner file = "(stdin)";
33246659ea9SSascha Wildner rc = fstat(STDIN_FILENO, &st);
33346659ea9SSascha Wildner } else {
33446659ea9SSascha Wildner file = argv[0];
33546659ea9SSascha Wildner if (nfs_handle) {
336*e9445c5dSzrj #if HAVE_DEVNAME
337*e9445c5dSzrj int j;
338*e9445c5dSzrj
33946659ea9SSascha Wildner rc = 0;
34046659ea9SSascha Wildner bzero(&fhnd, sizeof(fhnd));
34146659ea9SSascha Wildner j = MIN(2 * sizeof(fhnd), strlen(file));
34246659ea9SSascha Wildner if ((j & 1) != 0) {
34346659ea9SSascha Wildner rc = -1;
34446659ea9SSascha Wildner } else {
34546659ea9SSascha Wildner while (j) {
34646659ea9SSascha Wildner rc = hex2byte(&file[j - 2]);
34746659ea9SSascha Wildner if (rc == -1)
34846659ea9SSascha Wildner break;
34946659ea9SSascha Wildner ((char*) &fhnd)[j / 2 - 1] = rc;
35046659ea9SSascha Wildner j -= 2;
35146659ea9SSascha Wildner }
35246659ea9SSascha Wildner }
35346659ea9SSascha Wildner if (rc == -1)
35446659ea9SSascha Wildner errno = EINVAL;
35546659ea9SSascha Wildner else
35646659ea9SSascha Wildner rc = fhstat(&fhnd, &st);
357*e9445c5dSzrj #else
358*e9445c5dSzrj err(1, "-H is disabled in btools");
359*e9445c5dSzrj #endif
36046659ea9SSascha Wildner } else if (usestat) {
36146659ea9SSascha Wildner /*
36246659ea9SSascha Wildner * Try stat() and if it fails, fall back to
36346659ea9SSascha Wildner * lstat() just in case we're examining a
36446659ea9SSascha Wildner * broken symlink.
36546659ea9SSascha Wildner */
36646659ea9SSascha Wildner if ((rc = stat(file, &st)) == -1 &&
36746659ea9SSascha Wildner errno == ENOENT &&
36846659ea9SSascha Wildner (rc = lstat(file, &st)) == -1)
36946659ea9SSascha Wildner errno = ENOENT;
37046659ea9SSascha Wildner }
37146659ea9SSascha Wildner else
37246659ea9SSascha Wildner rc = lstat(file, &st);
37346659ea9SSascha Wildner }
3749a63ce63SRobert Garrett
3759a63ce63SRobert Garrett if (rc == -1) {
3769a63ce63SRobert Garrett errs = 1;
3779a63ce63SRobert Garrett linkfail = 1;
3789a63ce63SRobert Garrett if (!quiet)
37946659ea9SSascha Wildner warn("%s: stat", file);
3809a63ce63SRobert Garrett }
3819a63ce63SRobert Garrett else
38246659ea9SSascha Wildner output(&st, file, statfmt, fn, nonl);
3839a63ce63SRobert Garrett
3849a63ce63SRobert Garrett argv++;
3859a63ce63SRobert Garrett argc--;
3869a63ce63SRobert Garrett fn++;
3879a63ce63SRobert Garrett } while (argc > 0);
3889a63ce63SRobert Garrett
3899a63ce63SRobert Garrett return (am_readlink ? linkfail : errs);
3909a63ce63SRobert Garrett }
3919a63ce63SRobert Garrett
39246659ea9SSascha Wildner #if HAVE_STRUCT_STAT_ST_FLAGS
39346659ea9SSascha Wildner /*
39446659ea9SSascha Wildner * fflagstostr() wrapper that leaks only once
39546659ea9SSascha Wildner */
39633437a44SSascha Wildner static char *
xfflagstostr(unsigned long fflags)39746659ea9SSascha Wildner xfflagstostr(unsigned long fflags)
39846659ea9SSascha Wildner {
39946659ea9SSascha Wildner static char *str = NULL;
40046659ea9SSascha Wildner
40146659ea9SSascha Wildner if (str != NULL)
40246659ea9SSascha Wildner free(str);
40346659ea9SSascha Wildner
40446659ea9SSascha Wildner str = fflagstostr(fflags);
40546659ea9SSascha Wildner if (str == NULL)
40646659ea9SSascha Wildner err(1, "fflagstostr");
40746659ea9SSascha Wildner return (str);
40846659ea9SSascha Wildner }
40946659ea9SSascha Wildner #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
41046659ea9SSascha Wildner
41133437a44SSascha Wildner static void
usage(const char * synopsis)4129a63ce63SRobert Garrett usage(const char *synopsis)
4139a63ce63SRobert Garrett {
4149a63ce63SRobert Garrett
41546659ea9SSascha Wildner fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis);
4169a63ce63SRobert Garrett exit(1);
4179a63ce63SRobert Garrett }
4189a63ce63SRobert Garrett
4199a63ce63SRobert Garrett /*
4209a63ce63SRobert Garrett * Parses a format string.
4219a63ce63SRobert Garrett */
42233437a44SSascha Wildner static void
output(const struct stat * st,const char * file,const char * statfmt,int fn,int nonl)4239a63ce63SRobert Garrett output(const struct stat *st, const char *file,
42446659ea9SSascha Wildner const char *statfmt, int fn, int nonl)
4259a63ce63SRobert Garrett {
4269a63ce63SRobert Garrett int flags, size, prec, ofmt, hilo, what;
42746659ea9SSascha Wildner char buf[PATH_MAX + 4 + 1];
4289a63ce63SRobert Garrett const char *subfmt;
4299a63ce63SRobert Garrett int nl, t, i;
4309a63ce63SRobert Garrett
4319a63ce63SRobert Garrett nl = 1;
4329a63ce63SRobert Garrett while (*statfmt != '\0') {
4339a63ce63SRobert Garrett
4349a63ce63SRobert Garrett /*
4359a63ce63SRobert Garrett * Non-format characters go straight out.
4369a63ce63SRobert Garrett */
4379a63ce63SRobert Garrett if (*statfmt != FMT_MAGIC) {
4389a63ce63SRobert Garrett addchar(stdout, *statfmt, &nl);
4399a63ce63SRobert Garrett statfmt++;
4409a63ce63SRobert Garrett continue;
4419a63ce63SRobert Garrett }
4429a63ce63SRobert Garrett
4439a63ce63SRobert Garrett /*
4449a63ce63SRobert Garrett * The current format "substring" starts here,
4459a63ce63SRobert Garrett * and then we skip the magic.
4469a63ce63SRobert Garrett */
4479a63ce63SRobert Garrett subfmt = statfmt;
4489a63ce63SRobert Garrett statfmt++;
4499a63ce63SRobert Garrett
4509a63ce63SRobert Garrett /*
4519a63ce63SRobert Garrett * Some simple one-character "formats".
4529a63ce63SRobert Garrett */
4539a63ce63SRobert Garrett switch (*statfmt) {
4549a63ce63SRobert Garrett case SIMPLE_NEWLINE:
4559a63ce63SRobert Garrett addchar(stdout, '\n', &nl);
4569a63ce63SRobert Garrett statfmt++;
4579a63ce63SRobert Garrett continue;
4589a63ce63SRobert Garrett case SIMPLE_TAB:
4599a63ce63SRobert Garrett addchar(stdout, '\t', &nl);
4609a63ce63SRobert Garrett statfmt++;
4619a63ce63SRobert Garrett continue;
4629a63ce63SRobert Garrett case SIMPLE_PERCENT:
4639a63ce63SRobert Garrett addchar(stdout, '%', &nl);
4649a63ce63SRobert Garrett statfmt++;
4659a63ce63SRobert Garrett continue;
4669a63ce63SRobert Garrett case SIMPLE_NUMBER: {
4679a63ce63SRobert Garrett char num[12], *p;
4689a63ce63SRobert Garrett
4699a63ce63SRobert Garrett snprintf(num, sizeof(num), "%d", fn);
4709a63ce63SRobert Garrett for (p = &num[0]; *p; p++)
4719a63ce63SRobert Garrett addchar(stdout, *p, &nl);
4729a63ce63SRobert Garrett statfmt++;
4739a63ce63SRobert Garrett continue;
4749a63ce63SRobert Garrett }
4759a63ce63SRobert Garrett }
4769a63ce63SRobert Garrett
4779a63ce63SRobert Garrett /*
4789a63ce63SRobert Garrett * This must be an actual format string. Format strings are
4799a63ce63SRobert Garrett * similar to printf(3) formats up to a point, and are of
4809a63ce63SRobert Garrett * the form:
4819a63ce63SRobert Garrett *
4829a63ce63SRobert Garrett * % required start of format
4839a63ce63SRobert Garrett * [-# +0] opt. format characters
4849a63ce63SRobert Garrett * size opt. field width
4859a63ce63SRobert Garrett * . opt. decimal separator, followed by
4869a63ce63SRobert Garrett * prec opt. precision
4879a63ce63SRobert Garrett * fmt opt. output specifier (string, numeric, etc.)
4889a63ce63SRobert Garrett * sub opt. sub field specifier (high, middle, low)
4899a63ce63SRobert Garrett * datum required field specifier (size, mode, etc)
4909a63ce63SRobert Garrett *
4919a63ce63SRobert Garrett * Only the % and the datum selector are required. All data
4929a63ce63SRobert Garrett * have reasonable default output forms. The "sub" specifier
4939a63ce63SRobert Garrett * only applies to certain data (mode, dev, rdev, filetype).
4949a63ce63SRobert Garrett * The symlink output defaults to STRING, yet will only emit
4959a63ce63SRobert Garrett * the leading " -> " if STRING is explicitly specified. The
4969a63ce63SRobert Garrett * sizerdev datum will generate rdev output for character or
4979a63ce63SRobert Garrett * block devices, and size output for all others.
4989a63ce63SRobert Garrett */
4999a63ce63SRobert Garrett flags = 0;
5009a63ce63SRobert Garrett do {
5019a63ce63SRobert Garrett if (*statfmt == FMT_POUND)
5029a63ce63SRobert Garrett flags |= FLAG_POUND;
5039a63ce63SRobert Garrett else if (*statfmt == FMT_SPACE)
5049a63ce63SRobert Garrett flags |= FLAG_SPACE;
5059a63ce63SRobert Garrett else if (*statfmt == FMT_PLUS)
5069a63ce63SRobert Garrett flags |= FLAG_PLUS;
5079a63ce63SRobert Garrett else if (*statfmt == FMT_ZERO)
5089a63ce63SRobert Garrett flags |= FLAG_ZERO;
5099a63ce63SRobert Garrett else if (*statfmt == FMT_MINUS)
5109a63ce63SRobert Garrett flags |= FLAG_MINUS;
5119a63ce63SRobert Garrett else
5129a63ce63SRobert Garrett break;
5139a63ce63SRobert Garrett statfmt++;
5149a63ce63SRobert Garrett } while (1/*CONSTCOND*/);
5159a63ce63SRobert Garrett
5169a63ce63SRobert Garrett size = -1;
5179a63ce63SRobert Garrett if (isdigit((unsigned)*statfmt)) {
5189a63ce63SRobert Garrett size = 0;
5199a63ce63SRobert Garrett while (isdigit((unsigned)*statfmt)) {
5209a63ce63SRobert Garrett size = (size * 10) + (*statfmt - '0');
5219a63ce63SRobert Garrett statfmt++;
5229a63ce63SRobert Garrett if (size < 0)
5239a63ce63SRobert Garrett goto badfmt;
5249a63ce63SRobert Garrett }
5259a63ce63SRobert Garrett }
5269a63ce63SRobert Garrett
5279a63ce63SRobert Garrett prec = -1;
5289a63ce63SRobert Garrett if (*statfmt == FMT_DOT) {
5299a63ce63SRobert Garrett statfmt++;
5309a63ce63SRobert Garrett
5319a63ce63SRobert Garrett prec = 0;
5329a63ce63SRobert Garrett while (isdigit((unsigned)*statfmt)) {
5339a63ce63SRobert Garrett prec = (prec * 10) + (*statfmt - '0');
5349a63ce63SRobert Garrett statfmt++;
5359a63ce63SRobert Garrett if (prec < 0)
5369a63ce63SRobert Garrett goto badfmt;
5379a63ce63SRobert Garrett }
5389a63ce63SRobert Garrett }
5399a63ce63SRobert Garrett
5409a63ce63SRobert Garrett #define fmtcase(x, y) case (y): (x) = (y); statfmt++; break
5419a63ce63SRobert Garrett #define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break
5429a63ce63SRobert Garrett switch (*statfmt) {
5439a63ce63SRobert Garrett fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL);
5449a63ce63SRobert Garrett fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL);
5459a63ce63SRobert Garrett fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED);
5469a63ce63SRobert Garrett fmtcasef(ofmt, FMT_HEX, FMTF_HEX);
5479a63ce63SRobert Garrett fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT);
5489a63ce63SRobert Garrett fmtcasef(ofmt, FMT_STRING, FMTF_STRING);
5499a63ce63SRobert Garrett default:
5509a63ce63SRobert Garrett ofmt = 0;
5519a63ce63SRobert Garrett break;
5529a63ce63SRobert Garrett }
5539a63ce63SRobert Garrett
5549a63ce63SRobert Garrett switch (*statfmt) {
5559a63ce63SRobert Garrett fmtcase(hilo, HIGH_PIECE);
5569a63ce63SRobert Garrett fmtcase(hilo, MIDDLE_PIECE);
5579a63ce63SRobert Garrett fmtcase(hilo, LOW_PIECE);
5589a63ce63SRobert Garrett default:
5599a63ce63SRobert Garrett hilo = 0;
5609a63ce63SRobert Garrett break;
5619a63ce63SRobert Garrett }
5629a63ce63SRobert Garrett
5639a63ce63SRobert Garrett switch (*statfmt) {
56446659ea9SSascha Wildner fmtcase(what, SHOW_realpath);
5659a63ce63SRobert Garrett fmtcase(what, SHOW_st_dev);
5669a63ce63SRobert Garrett fmtcase(what, SHOW_st_ino);
5679a63ce63SRobert Garrett fmtcase(what, SHOW_st_mode);
5689a63ce63SRobert Garrett fmtcase(what, SHOW_st_nlink);
5699a63ce63SRobert Garrett fmtcase(what, SHOW_st_uid);
5709a63ce63SRobert Garrett fmtcase(what, SHOW_st_gid);
5719a63ce63SRobert Garrett fmtcase(what, SHOW_st_rdev);
5729a63ce63SRobert Garrett fmtcase(what, SHOW_st_atime);
5739a63ce63SRobert Garrett fmtcase(what, SHOW_st_mtime);
5749a63ce63SRobert Garrett fmtcase(what, SHOW_st_ctime);
575d542b1f4SSimon Schubert fmtcase(what, SHOW_st_btime);
5769a63ce63SRobert Garrett fmtcase(what, SHOW_st_size);
5779a63ce63SRobert Garrett fmtcase(what, SHOW_st_blocks);
5789a63ce63SRobert Garrett fmtcase(what, SHOW_st_blksize);
5799a63ce63SRobert Garrett fmtcase(what, SHOW_st_flags);
5809a63ce63SRobert Garrett fmtcase(what, SHOW_st_gen);
5819a63ce63SRobert Garrett fmtcase(what, SHOW_symlink);
5829a63ce63SRobert Garrett fmtcase(what, SHOW_filetype);
5839a63ce63SRobert Garrett fmtcase(what, SHOW_filename);
5849a63ce63SRobert Garrett fmtcase(what, SHOW_sizerdev);
5859a63ce63SRobert Garrett default:
5869a63ce63SRobert Garrett goto badfmt;
5879a63ce63SRobert Garrett }
5889a63ce63SRobert Garrett #undef fmtcasef
5899a63ce63SRobert Garrett #undef fmtcase
5909a63ce63SRobert Garrett
5919a63ce63SRobert Garrett t = format1(st,
5929a63ce63SRobert Garrett file,
5939a63ce63SRobert Garrett subfmt, statfmt - subfmt,
5949a63ce63SRobert Garrett buf, sizeof(buf),
5959a63ce63SRobert Garrett flags, size, prec, ofmt, hilo, what);
5969a63ce63SRobert Garrett
59746659ea9SSascha Wildner for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++)
5989a63ce63SRobert Garrett addchar(stdout, buf[i], &nl);
5999a63ce63SRobert Garrett
6009a63ce63SRobert Garrett continue;
6019a63ce63SRobert Garrett
6029a63ce63SRobert Garrett badfmt:
6039a63ce63SRobert Garrett errx(1, "%.*s: bad format",
6049a63ce63SRobert Garrett (int)(statfmt - subfmt + 1), subfmt);
6059a63ce63SRobert Garrett }
6069a63ce63SRobert Garrett
6079a63ce63SRobert Garrett if (!nl && !nonl)
60846659ea9SSascha Wildner fputc('\n', stdout);
60946659ea9SSascha Wildner fflush(stdout);
6109a63ce63SRobert Garrett }
6119a63ce63SRobert Garrett
6129a63ce63SRobert Garrett /*
6139a63ce63SRobert Garrett * Arranges output according to a single parsed format substring.
6149a63ce63SRobert Garrett */
61533437a44SSascha Wildner static int
format1(const struct stat * st,const char * file,const char * fmt,int flen,char * buf,size_t blen,int flags,int size,int prec,int ofmt,int hilo,int what)6169a63ce63SRobert Garrett format1(const struct stat *st,
6179a63ce63SRobert Garrett const char *file,
6189a63ce63SRobert Garrett const char *fmt, int flen,
6199a63ce63SRobert Garrett char *buf, size_t blen,
6209a63ce63SRobert Garrett int flags, int size, int prec, int ofmt,
6219a63ce63SRobert Garrett int hilo, int what)
6229a63ce63SRobert Garrett {
6239a63ce63SRobert Garrett u_int64_t data;
62446659ea9SSascha Wildner char *stmp, lfmt[24], tmp[20];
62546659ea9SSascha Wildner const char *sdata;
6269a63ce63SRobert Garrett char smode[12], sid[12], path[PATH_MAX + 4];
6279a63ce63SRobert Garrett struct passwd *pw;
6289a63ce63SRobert Garrett struct group *gr;
6299a63ce63SRobert Garrett const struct timespec *tsp;
6309a63ce63SRobert Garrett struct timespec ts;
631d542b1f4SSimon Schubert struct tm *tm;
6329a63ce63SRobert Garrett int l, small, formats;
6339a63ce63SRobert Garrett
6349a63ce63SRobert Garrett tsp = NULL;
6359a63ce63SRobert Garrett formats = 0;
6369a63ce63SRobert Garrett small = 0;
6379a63ce63SRobert Garrett
6389a63ce63SRobert Garrett /*
6399a63ce63SRobert Garrett * First, pick out the data and tweak it based on hilo or
6409a63ce63SRobert Garrett * specified output format (symlink output only).
6419a63ce63SRobert Garrett */
6429a63ce63SRobert Garrett switch (what) {
6439a63ce63SRobert Garrett case SHOW_st_dev:
6449a63ce63SRobert Garrett case SHOW_st_rdev:
6459a63ce63SRobert Garrett small = (sizeof(st->st_dev) == 4);
6469a63ce63SRobert Garrett data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev;
647d542b1f4SSimon Schubert #if HAVE_DEVNAME
6489a63ce63SRobert Garrett sdata = (what == SHOW_st_dev) ?
6499a63ce63SRobert Garrett devname(st->st_dev, S_IFBLK) :
6509a63ce63SRobert Garrett devname(st->st_rdev,
6519a63ce63SRobert Garrett S_ISCHR(st->st_mode) ? S_IFCHR :
6529a63ce63SRobert Garrett S_ISBLK(st->st_mode) ? S_IFBLK :
6539a63ce63SRobert Garrett 0U);
654*e9445c5dSzrj #else
655*e9445c5dSzrj sdata = NULL;
656*e9445c5dSzrj #endif /* HAVE_DEVNAME */
6579a63ce63SRobert Garrett if (sdata == NULL)
6589a63ce63SRobert Garrett sdata = "???";
6599a63ce63SRobert Garrett if (hilo == HIGH_PIECE) {
6609a63ce63SRobert Garrett data = major(data);
6619a63ce63SRobert Garrett hilo = 0;
6629a63ce63SRobert Garrett }
6639a63ce63SRobert Garrett else if (hilo == LOW_PIECE) {
6649a63ce63SRobert Garrett data = minor((unsigned)data);
6659a63ce63SRobert Garrett hilo = 0;
6669a63ce63SRobert Garrett }
6679a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
668d542b1f4SSimon Schubert #if HAVE_DEVNAME
6699a63ce63SRobert Garrett FMTF_STRING;
670d542b1f4SSimon Schubert #else /* HAVE_DEVNAME */
671d542b1f4SSimon Schubert 0;
672d542b1f4SSimon Schubert #endif /* HAVE_DEVNAME */
6739a63ce63SRobert Garrett if (ofmt == 0)
6749a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
6759a63ce63SRobert Garrett break;
6769a63ce63SRobert Garrett case SHOW_st_ino:
6779a63ce63SRobert Garrett small = (sizeof(st->st_ino) == 4);
6789a63ce63SRobert Garrett data = st->st_ino;
6799a63ce63SRobert Garrett sdata = NULL;
6809a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
6819a63ce63SRobert Garrett if (ofmt == 0)
6829a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
6839a63ce63SRobert Garrett break;
6849a63ce63SRobert Garrett case SHOW_st_mode:
6859a63ce63SRobert Garrett small = (sizeof(st->st_mode) == 4);
6869a63ce63SRobert Garrett data = st->st_mode;
6879a63ce63SRobert Garrett strmode(st->st_mode, smode);
68846659ea9SSascha Wildner stmp = smode;
68946659ea9SSascha Wildner l = strlen(stmp);
69046659ea9SSascha Wildner if (stmp[l - 1] == ' ')
69146659ea9SSascha Wildner stmp[--l] = '\0';
6929a63ce63SRobert Garrett if (hilo == HIGH_PIECE) {
6939a63ce63SRobert Garrett data >>= 12;
69446659ea9SSascha Wildner stmp += 1;
69546659ea9SSascha Wildner stmp[3] = '\0';
6969a63ce63SRobert Garrett hilo = 0;
6979a63ce63SRobert Garrett }
6989a63ce63SRobert Garrett else if (hilo == MIDDLE_PIECE) {
6999a63ce63SRobert Garrett data = (data >> 9) & 07;
70046659ea9SSascha Wildner stmp += 4;
70146659ea9SSascha Wildner stmp[3] = '\0';
7029a63ce63SRobert Garrett hilo = 0;
7039a63ce63SRobert Garrett }
7049a63ce63SRobert Garrett else if (hilo == LOW_PIECE) {
7059a63ce63SRobert Garrett data &= 0777;
70646659ea9SSascha Wildner stmp += 7;
70746659ea9SSascha Wildner stmp[3] = '\0';
7089a63ce63SRobert Garrett hilo = 0;
7099a63ce63SRobert Garrett }
71046659ea9SSascha Wildner sdata = stmp;
7119a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
7129a63ce63SRobert Garrett FMTF_STRING;
7139a63ce63SRobert Garrett if (ofmt == 0)
7149a63ce63SRobert Garrett ofmt = FMTF_OCTAL;
7159a63ce63SRobert Garrett break;
7169a63ce63SRobert Garrett case SHOW_st_nlink:
7179a63ce63SRobert Garrett small = (sizeof(st->st_dev) == 4);
7189a63ce63SRobert Garrett data = st->st_nlink;
7199a63ce63SRobert Garrett sdata = NULL;
7209a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
7219a63ce63SRobert Garrett if (ofmt == 0)
7229a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
7239a63ce63SRobert Garrett break;
7249a63ce63SRobert Garrett case SHOW_st_uid:
7259a63ce63SRobert Garrett small = (sizeof(st->st_uid) == 4);
7269a63ce63SRobert Garrett data = st->st_uid;
7279a63ce63SRobert Garrett if ((pw = getpwuid(st->st_uid)) != NULL)
7289a63ce63SRobert Garrett sdata = pw->pw_name;
7299a63ce63SRobert Garrett else {
7309a63ce63SRobert Garrett snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid);
7319a63ce63SRobert Garrett sdata = sid;
7329a63ce63SRobert Garrett }
7339a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
7349a63ce63SRobert Garrett FMTF_STRING;
7359a63ce63SRobert Garrett if (ofmt == 0)
7369a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
7379a63ce63SRobert Garrett break;
7389a63ce63SRobert Garrett case SHOW_st_gid:
7399a63ce63SRobert Garrett small = (sizeof(st->st_gid) == 4);
7409a63ce63SRobert Garrett data = st->st_gid;
7419a63ce63SRobert Garrett if ((gr = getgrgid(st->st_gid)) != NULL)
7429a63ce63SRobert Garrett sdata = gr->gr_name;
7439a63ce63SRobert Garrett else {
7449a63ce63SRobert Garrett snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid);
7459a63ce63SRobert Garrett sdata = sid;
7469a63ce63SRobert Garrett }
7479a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
7489a63ce63SRobert Garrett FMTF_STRING;
7499a63ce63SRobert Garrett if (ofmt == 0)
7509a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
7519a63ce63SRobert Garrett break;
7529a63ce63SRobert Garrett case SHOW_st_atime:
7539a63ce63SRobert Garrett tsp = &st->st_atimespec;
7549a63ce63SRobert Garrett /* FALLTHROUGH */
7559a63ce63SRobert Garrett case SHOW_st_mtime:
7569a63ce63SRobert Garrett if (tsp == NULL)
7579a63ce63SRobert Garrett tsp = &st->st_mtimespec;
7589a63ce63SRobert Garrett /* FALLTHROUGH */
7599a63ce63SRobert Garrett case SHOW_st_ctime:
7609a63ce63SRobert Garrett if (tsp == NULL)
7619a63ce63SRobert Garrett tsp = &st->st_ctimespec;
7629a63ce63SRobert Garrett /* FALLTHROUGH */
763d542b1f4SSimon Schubert #if HAVE_STRUCT_STAT_ST_BIRTHTIME
764d542b1f4SSimon Schubert case SHOW_st_btime:
765d542b1f4SSimon Schubert if (tsp == NULL)
766d542b1f4SSimon Schubert tsp = &st->st_birthtimespec;
767d542b1f4SSimon Schubert #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
768d542b1f4SSimon Schubert ts = *tsp; /* copy so we can muck with it */
769d542b1f4SSimon Schubert small = (sizeof(ts.tv_sec) == 4);
770d542b1f4SSimon Schubert data = ts.tv_sec;
771d542b1f4SSimon Schubert tm = localtime(&ts.tv_sec);
77246659ea9SSascha Wildner if (tm == NULL) {
77346659ea9SSascha Wildner ts.tv_sec = 0;
77446659ea9SSascha Wildner tm = localtime(&ts.tv_sec);
77546659ea9SSascha Wildner }
776d542b1f4SSimon Schubert strftime(path, sizeof(path), timefmt, tm);
777d542b1f4SSimon Schubert sdata = path;
778d542b1f4SSimon Schubert formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
779d542b1f4SSimon Schubert FMTF_FLOAT | FMTF_STRING;
780d542b1f4SSimon Schubert if (ofmt == 0)
781d542b1f4SSimon Schubert ofmt = FMTF_DECIMAL;
782d542b1f4SSimon Schubert break;
7839a63ce63SRobert Garrett case SHOW_st_size:
7849a63ce63SRobert Garrett small = (sizeof(st->st_size) == 4);
7859a63ce63SRobert Garrett data = st->st_size;
7869a63ce63SRobert Garrett sdata = NULL;
7879a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
7889a63ce63SRobert Garrett if (ofmt == 0)
7899a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
7909a63ce63SRobert Garrett break;
7919a63ce63SRobert Garrett case SHOW_st_blocks:
7929a63ce63SRobert Garrett small = (sizeof(st->st_blocks) == 4);
7939a63ce63SRobert Garrett data = st->st_blocks;
7949a63ce63SRobert Garrett sdata = NULL;
7959a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
7969a63ce63SRobert Garrett if (ofmt == 0)
7979a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
7989a63ce63SRobert Garrett break;
7999a63ce63SRobert Garrett case SHOW_st_blksize:
8009a63ce63SRobert Garrett small = (sizeof(st->st_blksize) == 4);
8019a63ce63SRobert Garrett data = st->st_blksize;
8029a63ce63SRobert Garrett sdata = NULL;
8039a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
8049a63ce63SRobert Garrett if (ofmt == 0)
8059a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
8069a63ce63SRobert Garrett break;
807d542b1f4SSimon Schubert #if HAVE_STRUCT_STAT_ST_FLAGS
8089a63ce63SRobert Garrett case SHOW_st_flags:
8099a63ce63SRobert Garrett small = (sizeof(st->st_flags) == 4);
8109a63ce63SRobert Garrett data = st->st_flags;
81146659ea9SSascha Wildner sdata = xfflagstostr(st->st_flags);
81246659ea9SSascha Wildner if (*sdata == '\0')
81346659ea9SSascha Wildner sdata = "-";
81446659ea9SSascha Wildner formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
81546659ea9SSascha Wildner FMTF_STRING;
8169a63ce63SRobert Garrett if (ofmt == 0)
8179a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
8189a63ce63SRobert Garrett break;
819d542b1f4SSimon Schubert #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
820d542b1f4SSimon Schubert #if HAVE_STRUCT_STAT_ST_GEN
8219a63ce63SRobert Garrett case SHOW_st_gen:
8229a63ce63SRobert Garrett small = (sizeof(st->st_gen) == 4);
8239a63ce63SRobert Garrett data = st->st_gen;
8249a63ce63SRobert Garrett sdata = NULL;
8259a63ce63SRobert Garrett formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
8269a63ce63SRobert Garrett if (ofmt == 0)
8279a63ce63SRobert Garrett ofmt = FMTF_UNSIGNED;
8289a63ce63SRobert Garrett break;
829d542b1f4SSimon Schubert #endif /* HAVE_STRUCT_STAT_ST_GEN */
83046659ea9SSascha Wildner case SHOW_realpath:
83146659ea9SSascha Wildner small = 0;
83246659ea9SSascha Wildner data = 0;
83346659ea9SSascha Wildner if (file == NULL) {
83446659ea9SSascha Wildner strlcpy(path, "(stdin)", sizeof(path));
83546659ea9SSascha Wildner sdata = path;
83646659ea9SSascha Wildner } else {
83746659ea9SSascha Wildner snprintf(path, sizeof(path), " -> ");
83846659ea9SSascha Wildner if (realpath(file, path + 4) == NULL) {
83946659ea9SSascha Wildner linkfail = 1;
84046659ea9SSascha Wildner l = 0;
84146659ea9SSascha Wildner path[0] = '\0';
84246659ea9SSascha Wildner }
84346659ea9SSascha Wildner sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
84446659ea9SSascha Wildner }
84546659ea9SSascha Wildner
84646659ea9SSascha Wildner formats = FMTF_STRING;
84746659ea9SSascha Wildner if (ofmt == 0)
84846659ea9SSascha Wildner ofmt = FMTF_STRING;
84946659ea9SSascha Wildner break;
8509a63ce63SRobert Garrett case SHOW_symlink:
8519a63ce63SRobert Garrett small = 0;
8529a63ce63SRobert Garrett data = 0;
8539a63ce63SRobert Garrett if (S_ISLNK(st->st_mode)) {
8549a63ce63SRobert Garrett snprintf(path, sizeof(path), " -> ");
8559a63ce63SRobert Garrett l = readlink(file, path + 4, sizeof(path) - 4 - 1);
8569a63ce63SRobert Garrett if (l == -1) {
8579a63ce63SRobert Garrett linkfail = 1;
8589a63ce63SRobert Garrett l = 0;
8599a63ce63SRobert Garrett path[0] = '\0';
8609a63ce63SRobert Garrett }
8619a63ce63SRobert Garrett path[l + 4] = '\0';
8629a63ce63SRobert Garrett sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
8639a63ce63SRobert Garrett }
8649a63ce63SRobert Garrett else {
8659a63ce63SRobert Garrett linkfail = 1;
8669a63ce63SRobert Garrett sdata = "";
8679a63ce63SRobert Garrett }
8689a63ce63SRobert Garrett formats = FMTF_STRING;
8699a63ce63SRobert Garrett if (ofmt == 0)
8709a63ce63SRobert Garrett ofmt = FMTF_STRING;
8719a63ce63SRobert Garrett break;
8729a63ce63SRobert Garrett case SHOW_filetype:
8739a63ce63SRobert Garrett small = 0;
8749a63ce63SRobert Garrett data = 0;
87546659ea9SSascha Wildner sdata = "";
8769a63ce63SRobert Garrett if (hilo == 0 || hilo == LOW_PIECE) {
8779a63ce63SRobert Garrett switch (st->st_mode & S_IFMT) {
87846659ea9SSascha Wildner case S_IFIFO: sdata = "|"; break;
87946659ea9SSascha Wildner case S_IFDIR: sdata = "/"; break;
8809a63ce63SRobert Garrett case S_IFREG:
8819a63ce63SRobert Garrett if (st->st_mode &
8829a63ce63SRobert Garrett (S_IXUSR | S_IXGRP | S_IXOTH))
88346659ea9SSascha Wildner sdata = "*";
8849a63ce63SRobert Garrett break;
88546659ea9SSascha Wildner case S_IFLNK: sdata = "@"; break;
88646659ea9SSascha Wildner case S_IFSOCK: sdata = "="; break;
887d542b1f4SSimon Schubert #ifdef S_IFWHT
88846659ea9SSascha Wildner case S_IFWHT: sdata = "%"; break;
889d542b1f4SSimon Schubert #endif /* S_IFWHT */
890d542b1f4SSimon Schubert #ifdef S_IFDOOR
89146659ea9SSascha Wildner case S_IFDOOR: sdata = ">"; break;
892d542b1f4SSimon Schubert #endif /* S_IFDOOR */
8939a63ce63SRobert Garrett }
8949a63ce63SRobert Garrett hilo = 0;
8959a63ce63SRobert Garrett }
8969a63ce63SRobert Garrett else if (hilo == HIGH_PIECE) {
8979a63ce63SRobert Garrett switch (st->st_mode & S_IFMT) {
8989a63ce63SRobert Garrett case S_IFIFO: sdata = "Fifo File"; break;
8999a63ce63SRobert Garrett case S_IFCHR: sdata = "Character Device"; break;
9009a63ce63SRobert Garrett case S_IFDIR: sdata = "Directory"; break;
9019a63ce63SRobert Garrett case S_IFBLK: sdata = "Block Device"; break;
9029a63ce63SRobert Garrett case S_IFREG: sdata = "Regular File"; break;
9039a63ce63SRobert Garrett case S_IFLNK: sdata = "Symbolic Link"; break;
9049a63ce63SRobert Garrett case S_IFSOCK: sdata = "Socket"; break;
905d542b1f4SSimon Schubert #ifdef S_IFWHT
9069a63ce63SRobert Garrett case S_IFWHT: sdata = "Whiteout File"; break;
907d542b1f4SSimon Schubert #endif /* S_IFWHT */
908d542b1f4SSimon Schubert #ifdef S_IFDOOR
909d542b1f4SSimon Schubert case S_IFDOOR: sdata = "Door"; break;
910d542b1f4SSimon Schubert #endif /* S_IFDOOR */
9119a63ce63SRobert Garrett default: sdata = "???"; break;
9129a63ce63SRobert Garrett }
9139a63ce63SRobert Garrett hilo = 0;
9149a63ce63SRobert Garrett }
9159a63ce63SRobert Garrett formats = FMTF_STRING;
9169a63ce63SRobert Garrett if (ofmt == 0)
9179a63ce63SRobert Garrett ofmt = FMTF_STRING;
9189a63ce63SRobert Garrett break;
9199a63ce63SRobert Garrett case SHOW_filename:
9209a63ce63SRobert Garrett small = 0;
9219a63ce63SRobert Garrett data = 0;
92246659ea9SSascha Wildner strlcpy(path, file, sizeof(path));
9239a63ce63SRobert Garrett sdata = path;
9249a63ce63SRobert Garrett formats = FMTF_STRING;
9259a63ce63SRobert Garrett if (ofmt == 0)
9269a63ce63SRobert Garrett ofmt = FMTF_STRING;
9279a63ce63SRobert Garrett break;
9289a63ce63SRobert Garrett case SHOW_sizerdev:
9299a63ce63SRobert Garrett if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
9309a63ce63SRobert Garrett char majdev[20], mindev[20];
9319a63ce63SRobert Garrett int l1, l2;
9329a63ce63SRobert Garrett
9339a63ce63SRobert Garrett l1 = format1(st,
9349a63ce63SRobert Garrett file,
9359a63ce63SRobert Garrett fmt, flen,
9369a63ce63SRobert Garrett majdev, sizeof(majdev),
9379a63ce63SRobert Garrett flags, size, prec,
9389a63ce63SRobert Garrett ofmt, HIGH_PIECE, SHOW_st_rdev);
9399a63ce63SRobert Garrett l2 = format1(st,
9409a63ce63SRobert Garrett file,
9419a63ce63SRobert Garrett fmt, flen,
9429a63ce63SRobert Garrett mindev, sizeof(mindev),
9439a63ce63SRobert Garrett flags, size, prec,
9449a63ce63SRobert Garrett ofmt, LOW_PIECE, SHOW_st_rdev);
9459a63ce63SRobert Garrett return (snprintf(buf, blen, "%.*s,%.*s",
9469a63ce63SRobert Garrett l1, majdev, l2, mindev));
9479a63ce63SRobert Garrett }
9489a63ce63SRobert Garrett else {
9499a63ce63SRobert Garrett return (format1(st,
9509a63ce63SRobert Garrett file,
9519a63ce63SRobert Garrett fmt, flen,
9529a63ce63SRobert Garrett buf, blen,
9539a63ce63SRobert Garrett flags, size, prec,
9549a63ce63SRobert Garrett ofmt, 0, SHOW_st_size));
9559a63ce63SRobert Garrett }
9569a63ce63SRobert Garrett /*NOTREACHED*/
9579a63ce63SRobert Garrett default:
9589a63ce63SRobert Garrett errx(1, "%.*s: bad format", (int)flen, fmt);
9599a63ce63SRobert Garrett }
9609a63ce63SRobert Garrett
9619a63ce63SRobert Garrett /*
9629a63ce63SRobert Garrett * If a subdatum was specified but not supported, or an output
9639a63ce63SRobert Garrett * format was selected that is not supported, that's an error.
9649a63ce63SRobert Garrett */
9659a63ce63SRobert Garrett if (hilo != 0 || (ofmt & formats) == 0)
9669a63ce63SRobert Garrett errx(1, "%.*s: bad format", (int)flen, fmt);
9679a63ce63SRobert Garrett
9689a63ce63SRobert Garrett /*
9699a63ce63SRobert Garrett * Assemble the format string for passing to printf(3).
9709a63ce63SRobert Garrett */
9719a63ce63SRobert Garrett lfmt[0] = '\0';
97246659ea9SSascha Wildner strcat(lfmt, "%");
9739a63ce63SRobert Garrett if (flags & FLAG_POUND)
97446659ea9SSascha Wildner strcat(lfmt, "#");
9759a63ce63SRobert Garrett if (flags & FLAG_SPACE)
97646659ea9SSascha Wildner strcat(lfmt, " ");
9779a63ce63SRobert Garrett if (flags & FLAG_PLUS)
97846659ea9SSascha Wildner strcat(lfmt, "+");
9799a63ce63SRobert Garrett if (flags & FLAG_MINUS)
98046659ea9SSascha Wildner strcat(lfmt, "-");
9819a63ce63SRobert Garrett if (flags & FLAG_ZERO)
98246659ea9SSascha Wildner strcat(lfmt, "0");
9839a63ce63SRobert Garrett
9849a63ce63SRobert Garrett /*
9859a63ce63SRobert Garrett * Only the timespecs support the FLOAT output format, and that
9869a63ce63SRobert Garrett * requires work that differs from the other formats.
9879a63ce63SRobert Garrett */
9889a63ce63SRobert Garrett if (ofmt == FMTF_FLOAT) {
9899a63ce63SRobert Garrett /*
9909a63ce63SRobert Garrett * Nothing after the decimal point, so just print seconds.
9919a63ce63SRobert Garrett */
9929a63ce63SRobert Garrett if (prec == 0) {
9939a63ce63SRobert Garrett if (size != -1) {
99446659ea9SSascha Wildner snprintf(tmp, sizeof(tmp), "%d", size);
99546659ea9SSascha Wildner strcat(lfmt, tmp);
9969a63ce63SRobert Garrett }
99746659ea9SSascha Wildner strcat(lfmt, "lld");
99846659ea9SSascha Wildner return (snprintf(buf, blen, lfmt,
99946659ea9SSascha Wildner (long long)ts.tv_sec));
10009a63ce63SRobert Garrett }
10019a63ce63SRobert Garrett
10029a63ce63SRobert Garrett /*
10039a63ce63SRobert Garrett * Unspecified precision gets all the precision we have:
10049a63ce63SRobert Garrett * 9 digits.
10059a63ce63SRobert Garrett */
10069a63ce63SRobert Garrett if (prec == -1)
10079a63ce63SRobert Garrett prec = 9;
10089a63ce63SRobert Garrett
10099a63ce63SRobert Garrett /*
10109a63ce63SRobert Garrett * Adjust the size for the decimal point and the digits
10119a63ce63SRobert Garrett * that will follow.
10129a63ce63SRobert Garrett */
10139a63ce63SRobert Garrett size -= prec + 1;
10149a63ce63SRobert Garrett
10159a63ce63SRobert Garrett /*
10169a63ce63SRobert Garrett * Any leftover size that's legitimate will be used.
10179a63ce63SRobert Garrett */
10189a63ce63SRobert Garrett if (size > 0) {
101946659ea9SSascha Wildner snprintf(tmp, sizeof(tmp), "%d", size);
102046659ea9SSascha Wildner strcat(lfmt, tmp);
10219a63ce63SRobert Garrett }
102246659ea9SSascha Wildner /* Seconds: time_t cast to long long. */
102346659ea9SSascha Wildner strcat(lfmt, "lld");
10249a63ce63SRobert Garrett
10259a63ce63SRobert Garrett /*
10269a63ce63SRobert Garrett * The stuff after the decimal point always needs zero
10279a63ce63SRobert Garrett * filling.
10289a63ce63SRobert Garrett */
102946659ea9SSascha Wildner strcat(lfmt, ".%0");
10309a63ce63SRobert Garrett
10319a63ce63SRobert Garrett /*
10329a63ce63SRobert Garrett * We can "print" at most nine digits of precision. The
10339a63ce63SRobert Garrett * rest we will pad on at the end.
103446659ea9SSascha Wildner *
103546659ea9SSascha Wildner * Nanoseconds: long.
10369a63ce63SRobert Garrett */
103746659ea9SSascha Wildner snprintf(tmp, sizeof(tmp), "%dld", prec > 9 ? 9 : prec);
103846659ea9SSascha Wildner strcat(lfmt, tmp);
10399a63ce63SRobert Garrett
10409a63ce63SRobert Garrett /*
10419a63ce63SRobert Garrett * For precision of less that nine digits, trim off the
10429a63ce63SRobert Garrett * less significant figures.
10439a63ce63SRobert Garrett */
10449a63ce63SRobert Garrett for (; prec < 9; prec++)
10459a63ce63SRobert Garrett ts.tv_nsec /= 10;
10469a63ce63SRobert Garrett
10479a63ce63SRobert Garrett /*
10489a63ce63SRobert Garrett * Use the format, and then tack on any zeroes that
10499a63ce63SRobert Garrett * might be required to make up the requested precision.
10509a63ce63SRobert Garrett */
105146659ea9SSascha Wildner l = snprintf(buf, blen, lfmt, (long long)ts.tv_sec, ts.tv_nsec);
105246659ea9SSascha Wildner for (; prec > 9 && l < (int)blen; prec--, l++)
105346659ea9SSascha Wildner strcat(buf, "0");
10549a63ce63SRobert Garrett return (l);
10559a63ce63SRobert Garrett }
10569a63ce63SRobert Garrett
10579a63ce63SRobert Garrett /*
10589a63ce63SRobert Garrett * Add on size and precision, if specified, to the format.
10599a63ce63SRobert Garrett */
10609a63ce63SRobert Garrett if (size != -1) {
106146659ea9SSascha Wildner snprintf(tmp, sizeof(tmp), "%d", size);
106246659ea9SSascha Wildner strcat(lfmt, tmp);
10639a63ce63SRobert Garrett }
10649a63ce63SRobert Garrett if (prec != -1) {
106546659ea9SSascha Wildner snprintf(tmp, sizeof(tmp), ".%d", prec);
106646659ea9SSascha Wildner strcat(lfmt, tmp);
10679a63ce63SRobert Garrett }
10689a63ce63SRobert Garrett
10699a63ce63SRobert Garrett /*
10709a63ce63SRobert Garrett * String output uses the temporary sdata.
10719a63ce63SRobert Garrett */
10729a63ce63SRobert Garrett if (ofmt == FMTF_STRING) {
10739a63ce63SRobert Garrett if (sdata == NULL)
10749a63ce63SRobert Garrett errx(1, "%.*s: bad format", (int)flen, fmt);
107546659ea9SSascha Wildner strcat(lfmt, "s");
10769a63ce63SRobert Garrett return (snprintf(buf, blen, lfmt, sdata));
10779a63ce63SRobert Garrett }
10789a63ce63SRobert Garrett
10799a63ce63SRobert Garrett /*
10809a63ce63SRobert Garrett * Ensure that sign extension does not cause bad looking output
10819a63ce63SRobert Garrett * for some forms.
10829a63ce63SRobert Garrett */
10839a63ce63SRobert Garrett if (small && ofmt != FMTF_DECIMAL)
10849a63ce63SRobert Garrett data = (u_int32_t)data;
10859a63ce63SRobert Garrett
10869a63ce63SRobert Garrett /*
10879a63ce63SRobert Garrett * The four "numeric" output forms.
10889a63ce63SRobert Garrett */
108946659ea9SSascha Wildner strcat(lfmt, "ll");
10909a63ce63SRobert Garrett switch (ofmt) {
109146659ea9SSascha Wildner case FMTF_DECIMAL: strcat(lfmt, "d"); break;
109246659ea9SSascha Wildner case FMTF_OCTAL: strcat(lfmt, "o"); break;
109346659ea9SSascha Wildner case FMTF_UNSIGNED: strcat(lfmt, "u"); break;
109446659ea9SSascha Wildner case FMTF_HEX: strcat(lfmt, "x"); break;
10959a63ce63SRobert Garrett }
10969a63ce63SRobert Garrett
10979a63ce63SRobert Garrett return (snprintf(buf, blen, lfmt, data));
10989a63ce63SRobert Garrett }
109946659ea9SSascha Wildner
1100*e9445c5dSzrj #if HAVE_DEVNAME
110146659ea9SSascha Wildner #define hex2nibble(c) (c <= '9' ? c - '0' : toupper(c) - 'A' + 10)
110233437a44SSascha Wildner static int
hex2byte(const char c[2])110346659ea9SSascha Wildner hex2byte(const char c[2]) {
110446659ea9SSascha Wildner if (!(ishexnumber(c[0]) && ishexnumber(c[1])))
110546659ea9SSascha Wildner return -1;
110646659ea9SSascha Wildner return (hex2nibble(c[0]) << 4) + hex2nibble(c[1]);
110746659ea9SSascha Wildner }
1108*e9445c5dSzrj #endif
1109