11de7b4b8SPedro F. Giffuni /*-
2b61a5730SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
406b6d7d8SDoug Barton * Copyright (c) 2002 The NetBSD Foundation, Inc.
506b6d7d8SDoug Barton * All rights reserved.
606b6d7d8SDoug Barton *
706b6d7d8SDoug Barton * This code is derived from software contributed to The NetBSD Foundation
806b6d7d8SDoug Barton * by Andrew Brown.
906b6d7d8SDoug Barton *
1006b6d7d8SDoug Barton * Redistribution and use in source and binary forms, with or without
1106b6d7d8SDoug Barton * modification, are permitted provided that the following conditions
1206b6d7d8SDoug Barton * are met:
1306b6d7d8SDoug Barton * 1. Redistributions of source code must retain the above copyright
1406b6d7d8SDoug Barton * notice, this list of conditions and the following disclaimer.
1506b6d7d8SDoug Barton * 2. Redistributions in binary form must reproduce the above copyright
1606b6d7d8SDoug Barton * notice, this list of conditions and the following disclaimer in the
1706b6d7d8SDoug Barton * documentation and/or other materials provided with the distribution.
1806b6d7d8SDoug Barton *
1906b6d7d8SDoug Barton * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2006b6d7d8SDoug Barton * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2106b6d7d8SDoug Barton * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2206b6d7d8SDoug Barton * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2306b6d7d8SDoug Barton * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2406b6d7d8SDoug Barton * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2506b6d7d8SDoug Barton * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2606b6d7d8SDoug Barton * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2706b6d7d8SDoug Barton * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2806b6d7d8SDoug Barton * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2906b6d7d8SDoug Barton * POSSIBILITY OF SUCH DAMAGE.
3006b6d7d8SDoug Barton */
3106b6d7d8SDoug Barton
321cee9d8dSDoug Barton #include <sys/cdefs.h>
335b812a14SDoug Barton #if 0
3406b6d7d8SDoug Barton #ifndef lint
3549399277SDoug Barton __RCSID("$NetBSD: stat.c,v 1.33 2011/01/15 22:54:10 njoly Exp $"
36de28c1d6SDoug Barton "$OpenBSD: stat.c,v 1.14 2009/06/24 09:44:25 sobrado Exp $");
375b812a14SDoug Barton #endif
3806b6d7d8SDoug Barton #endif
39dad5d4e0SDoug Barton #if HAVE_CONFIG_H
40dad5d4e0SDoug Barton #include "config.h"
41dad5d4e0SDoug Barton #else /* HAVE_CONFIG_H */
42dad5d4e0SDoug Barton #define HAVE_STRUCT_STAT_ST_FLAGS 1
43dad5d4e0SDoug Barton #define HAVE_STRUCT_STAT_ST_GEN 1
44dad5d4e0SDoug Barton #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
45dad5d4e0SDoug Barton #define HAVE_STRUCT_STAT_ST_MTIMENSEC 1
46dad5d4e0SDoug Barton #define HAVE_DEVNAME 1
47dad5d4e0SDoug Barton #endif /* HAVE_CONFIG_H */
48dad5d4e0SDoug Barton
4942dc9c5aSEd Schouten #include <sys/param.h>
50782ce287SDoug Barton #include <sys/types.h>
5106b6d7d8SDoug Barton #include <sys/stat.h>
524d6bab26SThomas Quinot #include <sys/mount.h>
535b812a14SDoug Barton
5406b6d7d8SDoug Barton #include <ctype.h>
555b812a14SDoug Barton #include <err.h>
56ccdbe8ceSDoug Barton #include <errno.h>
5706b6d7d8SDoug Barton #include <grp.h>
58782ce287SDoug Barton #include <limits.h>
5920f8331aSStefan Eßer #include <locale.h>
6042dc9c5aSEd Schouten #include <paths.h>
615b812a14SDoug Barton #include <pwd.h>
625b812a14SDoug Barton #include <stdio.h>
635b812a14SDoug Barton #include <stdlib.h>
645b812a14SDoug Barton #include <string.h>
655b812a14SDoug Barton #include <time.h>
665b812a14SDoug Barton #include <unistd.h>
6706b6d7d8SDoug Barton
68dad5d4e0SDoug Barton #if HAVE_STRUCT_STAT_ST_FLAGS
69dad5d4e0SDoug Barton #define DEF_F "%#Xf "
70dad5d4e0SDoug Barton #define RAW_F "%f "
71dad5d4e0SDoug Barton #define SHELL_F " st_flags=%f"
72dad5d4e0SDoug Barton #else /* HAVE_STRUCT_STAT_ST_FLAGS */
73dad5d4e0SDoug Barton #define DEF_F
74dad5d4e0SDoug Barton #define RAW_F
75dad5d4e0SDoug Barton #define SHELL_F
76dad5d4e0SDoug Barton #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
77dad5d4e0SDoug Barton
78dad5d4e0SDoug Barton #if HAVE_STRUCT_STAT_ST_BIRTHTIME
79dad5d4e0SDoug Barton #define DEF_B "\"%SB\" "
80dad5d4e0SDoug Barton #define RAW_B "%B "
81dad5d4e0SDoug Barton #define SHELL_B "st_birthtime=%B "
82dad5d4e0SDoug Barton #else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
83dad5d4e0SDoug Barton #define DEF_B
84dad5d4e0SDoug Barton #define RAW_B
85dad5d4e0SDoug Barton #define SHELL_B
86dad5d4e0SDoug Barton #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
87dad5d4e0SDoug Barton
88dad5d4e0SDoug Barton #if HAVE_STRUCT_STAT_ST_ATIM
89dad5d4e0SDoug Barton #define st_atimespec st_atim
90dad5d4e0SDoug Barton #define st_ctimespec st_ctim
91dad5d4e0SDoug Barton #define st_mtimespec st_mtim
92dad5d4e0SDoug Barton #endif /* HAVE_STRUCT_STAT_ST_ATIM */
93dad5d4e0SDoug Barton
9406b6d7d8SDoug Barton #define DEF_FORMAT \
95dad5d4e0SDoug Barton "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \
96dad5d4e0SDoug Barton "%k %b " DEF_F "%N"
97dad5d4e0SDoug Barton #define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \
98dad5d4e0SDoug Barton "%k %b " RAW_F "%N"
9906b6d7d8SDoug Barton #define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY"
10006b6d7d8SDoug Barton #define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY"
10106b6d7d8SDoug Barton #define SHELL_FORMAT \
10206b6d7d8SDoug Barton "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \
10306b6d7d8SDoug Barton "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \
104dad5d4e0SDoug Barton "st_atime=%a st_mtime=%m st_ctime=%c " SHELL_B \
105dad5d4e0SDoug Barton "st_blksize=%k st_blocks=%b" SHELL_F
10606b6d7d8SDoug Barton #define LINUX_FORMAT \
10706b6d7d8SDoug Barton " File: \"%N\"%n" \
10806b6d7d8SDoug Barton " Size: %-11z FileType: %HT%n" \
109df8a2f1aSMaxim Konovalov " Mode: (%OMp%03OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \
11006b6d7d8SDoug Barton "Device: %Hd,%Ld Inode: %i Links: %l%n" \
11106b6d7d8SDoug Barton "Access: %Sa%n" \
11206b6d7d8SDoug Barton "Modify: %Sm%n" \
113f35f34b1SEdward Tomasz Napierala "Change: %Sc%n" \
114f35f34b1SEdward Tomasz Napierala " Birth: %SB"
11506b6d7d8SDoug Barton
11606b6d7d8SDoug Barton #define TIME_FORMAT "%b %e %T %Y"
11706b6d7d8SDoug Barton
11806b6d7d8SDoug Barton #define FLAG_POUND 0x01
11906b6d7d8SDoug Barton #define FLAG_SPACE 0x02
12006b6d7d8SDoug Barton #define FLAG_PLUS 0x04
12106b6d7d8SDoug Barton #define FLAG_ZERO 0x08
12206b6d7d8SDoug Barton #define FLAG_MINUS 0x10
12306b6d7d8SDoug Barton
12406b6d7d8SDoug Barton /*
12506b6d7d8SDoug Barton * These format characters must all be unique, except the magic one.
12606b6d7d8SDoug Barton */
12706b6d7d8SDoug Barton #define FMT_MAGIC '%'
12806b6d7d8SDoug Barton #define FMT_DOT '.'
12906b6d7d8SDoug Barton
13006b6d7d8SDoug Barton #define SIMPLE_NEWLINE 'n'
13106b6d7d8SDoug Barton #define SIMPLE_TAB 't'
13206b6d7d8SDoug Barton #define SIMPLE_PERCENT '%'
13306b6d7d8SDoug Barton #define SIMPLE_NUMBER '@'
13406b6d7d8SDoug Barton
13506b6d7d8SDoug Barton #define FMT_POUND '#'
13606b6d7d8SDoug Barton #define FMT_SPACE ' '
13706b6d7d8SDoug Barton #define FMT_PLUS '+'
13806b6d7d8SDoug Barton #define FMT_ZERO '0'
13906b6d7d8SDoug Barton #define FMT_MINUS '-'
14006b6d7d8SDoug Barton
14106b6d7d8SDoug Barton #define FMT_DECIMAL 'D'
14206b6d7d8SDoug Barton #define FMT_OCTAL 'O'
14306b6d7d8SDoug Barton #define FMT_UNSIGNED 'U'
14406b6d7d8SDoug Barton #define FMT_HEX 'X'
14506b6d7d8SDoug Barton #define FMT_FLOAT 'F'
14606b6d7d8SDoug Barton #define FMT_STRING 'S'
14706b6d7d8SDoug Barton
1485b812a14SDoug Barton #define FMTF_DECIMAL 0x01
1495b812a14SDoug Barton #define FMTF_OCTAL 0x02
1505b812a14SDoug Barton #define FMTF_UNSIGNED 0x04
1515b812a14SDoug Barton #define FMTF_HEX 0x08
1525b812a14SDoug Barton #define FMTF_FLOAT 0x10
1535b812a14SDoug Barton #define FMTF_STRING 0x20
1545b812a14SDoug Barton
15506b6d7d8SDoug Barton #define HIGH_PIECE 'H'
15606b6d7d8SDoug Barton #define MIDDLE_PIECE 'M'
15706b6d7d8SDoug Barton #define LOW_PIECE 'L'
15806b6d7d8SDoug Barton
159b9f9338eSDoug Barton #define SHOW_realpath 'R'
16006b6d7d8SDoug Barton #define SHOW_st_dev 'd'
16106b6d7d8SDoug Barton #define SHOW_st_ino 'i'
16206b6d7d8SDoug Barton #define SHOW_st_mode 'p'
16306b6d7d8SDoug Barton #define SHOW_st_nlink 'l'
16406b6d7d8SDoug Barton #define SHOW_st_uid 'u'
16506b6d7d8SDoug Barton #define SHOW_st_gid 'g'
16606b6d7d8SDoug Barton #define SHOW_st_rdev 'r'
16706b6d7d8SDoug Barton #define SHOW_st_atime 'a'
16806b6d7d8SDoug Barton #define SHOW_st_mtime 'm'
16906b6d7d8SDoug Barton #define SHOW_st_ctime 'c'
1701cee9d8dSDoug Barton #define SHOW_st_btime 'B'
17106b6d7d8SDoug Barton #define SHOW_st_size 'z'
17206b6d7d8SDoug Barton #define SHOW_st_blocks 'b'
17306b6d7d8SDoug Barton #define SHOW_st_blksize 'k'
17406b6d7d8SDoug Barton #define SHOW_st_flags 'f'
17506b6d7d8SDoug Barton #define SHOW_st_gen 'v'
17606b6d7d8SDoug Barton #define SHOW_symlink 'Y'
17706b6d7d8SDoug Barton #define SHOW_filetype 'T'
17806b6d7d8SDoug Barton #define SHOW_filename 'N'
17906b6d7d8SDoug Barton #define SHOW_sizerdev 'Z'
18006b6d7d8SDoug Barton
1815b812a14SDoug Barton void usage(const char *);
18206b6d7d8SDoug Barton void output(const struct stat *, const char *,
183d7233fd6SDoug Barton const char *, int, int);
18406b6d7d8SDoug Barton int format1(const struct stat *, /* stat info */
18506b6d7d8SDoug Barton const char *, /* the file name */
18606b6d7d8SDoug Barton const char *, int, /* the format string itself */
18706b6d7d8SDoug Barton char *, size_t, /* a place to put the output */
18806b6d7d8SDoug Barton int, int, int, int, /* the parsed format */
18906b6d7d8SDoug Barton int, int);
19055298f03SThomas Quinot int hex2byte(const char [2]);
19124d9be57SJilles Tjoelker #if HAVE_STRUCT_STAT_ST_FLAGS
19224d9be57SJilles Tjoelker char *xfflagstostr(unsigned long);
19324d9be57SJilles Tjoelker #endif
19406b6d7d8SDoug Barton
195bf70beceSEd Schouten static const char *timefmt;
196bf70beceSEd Schouten static int linkfail;
19706b6d7d8SDoug Barton
1985b812a14SDoug Barton #define addchar(s, c, nl) \
19906b6d7d8SDoug Barton do { \
2005b812a14SDoug Barton (void)fputc((c), (s)); \
20106b6d7d8SDoug Barton (*nl) = ((c) == '\n'); \
20206b6d7d8SDoug Barton } while (0/*CONSTCOND*/)
20306b6d7d8SDoug Barton
20406b6d7d8SDoug Barton int
main(int argc,char * argv[])20506b6d7d8SDoug Barton main(int argc, char *argv[])
20606b6d7d8SDoug Barton {
20706b6d7d8SDoug Barton struct stat st;
2085b812a14SDoug Barton int ch, rc, errs, am_readlink;
2094d6bab26SThomas Quinot int lsF, fmtchar, usestat, nfs_handle, fn, nonl, quiet;
2109b3f4394SDoug Barton const char *statfmt, *options, *synopsis;
21142dc9c5aSEd Schouten char dname[sizeof _PATH_DEV + SPECNAMELEN] = _PATH_DEV;
2124d6bab26SThomas Quinot fhandle_t fhnd;
21342dc9c5aSEd Schouten const char *file;
21406b6d7d8SDoug Barton
2155b812a14SDoug Barton am_readlink = 0;
21606b6d7d8SDoug Barton lsF = 0;
21706b6d7d8SDoug Barton fmtchar = '\0';
21806b6d7d8SDoug Barton usestat = 0;
2194d6bab26SThomas Quinot nfs_handle = 0;
22006b6d7d8SDoug Barton nonl = 0;
2215b812a14SDoug Barton quiet = 0;
2225b812a14SDoug Barton linkfail = 0;
22306b6d7d8SDoug Barton statfmt = NULL;
22406b6d7d8SDoug Barton timefmt = NULL;
22506b6d7d8SDoug Barton
2265b812a14SDoug Barton if (strcmp(getprogname(), "readlink") == 0) {
2275b812a14SDoug Barton am_readlink = 1;
228b9f9338eSDoug Barton options = "fn";
229b9f9338eSDoug Barton synopsis = "[-fn] [file ...]";
2305b812a14SDoug Barton statfmt = "%Y";
2315b812a14SDoug Barton fmtchar = 'f';
2325b812a14SDoug Barton quiet = 1;
2335b812a14SDoug Barton } else {
2344d6bab26SThomas Quinot options = "f:FHlLnqrst:x";
235de28c1d6SDoug Barton synopsis = "[-FLnq] [-f format | -l | -r | -s | -x] "
2364d6bab26SThomas Quinot "[-t timefmt] [file|handle ...]";
2375b812a14SDoug Barton }
2385b812a14SDoug Barton
2395b812a14SDoug Barton while ((ch = getopt(argc, argv, options)) != -1)
24006b6d7d8SDoug Barton switch (ch) {
24106b6d7d8SDoug Barton case 'F':
24206b6d7d8SDoug Barton lsF = 1;
24306b6d7d8SDoug Barton break;
2444d6bab26SThomas Quinot case 'H':
2454d6bab26SThomas Quinot nfs_handle = 1;
2464d6bab26SThomas Quinot break;
24706b6d7d8SDoug Barton case 'L':
24806b6d7d8SDoug Barton usestat = 1;
24906b6d7d8SDoug Barton break;
25006b6d7d8SDoug Barton case 'n':
25106b6d7d8SDoug Barton nonl = 1;
25206b6d7d8SDoug Barton break;
2535b812a14SDoug Barton case 'q':
2545b812a14SDoug Barton quiet = 1;
2555b812a14SDoug Barton break;
25606b6d7d8SDoug Barton case 'f':
257b9f9338eSDoug Barton if (am_readlink) {
258b9f9338eSDoug Barton statfmt = "%R";
259b9f9338eSDoug Barton break;
260b9f9338eSDoug Barton }
26106b6d7d8SDoug Barton statfmt = optarg;
26206b6d7d8SDoug Barton /* FALLTHROUGH */
26306b6d7d8SDoug Barton case 'l':
26406b6d7d8SDoug Barton case 'r':
26506b6d7d8SDoug Barton case 's':
26606b6d7d8SDoug Barton case 'x':
26706b6d7d8SDoug Barton if (fmtchar != 0)
26806b6d7d8SDoug Barton errx(1, "can't use format '%c' with '%c'",
26906b6d7d8SDoug Barton fmtchar, ch);
27006b6d7d8SDoug Barton fmtchar = ch;
27106b6d7d8SDoug Barton break;
27206b6d7d8SDoug Barton case 't':
27306b6d7d8SDoug Barton timefmt = optarg;
27406b6d7d8SDoug Barton break;
27506b6d7d8SDoug Barton default:
2765b812a14SDoug Barton usage(synopsis);
27706b6d7d8SDoug Barton }
27806b6d7d8SDoug Barton
27906b6d7d8SDoug Barton argc -= optind;
28006b6d7d8SDoug Barton argv += optind;
28106b6d7d8SDoug Barton fn = 1;
28206b6d7d8SDoug Barton
28306b6d7d8SDoug Barton if (fmtchar == '\0') {
28406b6d7d8SDoug Barton if (lsF)
28506b6d7d8SDoug Barton fmtchar = 'l';
28606b6d7d8SDoug Barton else {
28706b6d7d8SDoug Barton fmtchar = 'f';
28806b6d7d8SDoug Barton statfmt = DEF_FORMAT;
28906b6d7d8SDoug Barton }
29006b6d7d8SDoug Barton }
29106b6d7d8SDoug Barton
29206b6d7d8SDoug Barton if (lsF && fmtchar != 'l')
29306b6d7d8SDoug Barton errx(1, "can't use format '%c' with -F", fmtchar);
29406b6d7d8SDoug Barton
29506b6d7d8SDoug Barton switch (fmtchar) {
29606b6d7d8SDoug Barton case 'f':
29706b6d7d8SDoug Barton /* statfmt already set */
29806b6d7d8SDoug Barton break;
29906b6d7d8SDoug Barton case 'l':
30006b6d7d8SDoug Barton statfmt = lsF ? LSF_FORMAT : LS_FORMAT;
30106b6d7d8SDoug Barton break;
30206b6d7d8SDoug Barton case 'r':
30306b6d7d8SDoug Barton statfmt = RAW_FORMAT;
30406b6d7d8SDoug Barton break;
30506b6d7d8SDoug Barton case 's':
30606b6d7d8SDoug Barton statfmt = SHELL_FORMAT;
30706b6d7d8SDoug Barton break;
30806b6d7d8SDoug Barton case 'x':
30906b6d7d8SDoug Barton statfmt = LINUX_FORMAT;
31006b6d7d8SDoug Barton if (timefmt == NULL)
31106b6d7d8SDoug Barton timefmt = "%c";
31206b6d7d8SDoug Barton break;
31306b6d7d8SDoug Barton default:
3145b812a14SDoug Barton usage(synopsis);
31506b6d7d8SDoug Barton /*NOTREACHED*/
31606b6d7d8SDoug Barton }
31706b6d7d8SDoug Barton
31806b6d7d8SDoug Barton if (timefmt == NULL)
31906b6d7d8SDoug Barton timefmt = TIME_FORMAT;
32006b6d7d8SDoug Barton
32106b6d7d8SDoug Barton errs = 0;
32206b6d7d8SDoug Barton do {
32342dc9c5aSEd Schouten if (argc == 0) {
32442dc9c5aSEd Schouten if (fdevname_r(STDIN_FILENO, dname +
32542dc9c5aSEd Schouten sizeof _PATH_DEV - 1, SPECNAMELEN) != NULL)
32642dc9c5aSEd Schouten file = dname;
32706b6d7d8SDoug Barton else
32842dc9c5aSEd Schouten file = "(stdin)";
32942dc9c5aSEd Schouten rc = fstat(STDIN_FILENO, &st);
33042dc9c5aSEd Schouten } else {
3314d6bab26SThomas Quinot int j;
3324d6bab26SThomas Quinot
33342dc9c5aSEd Schouten file = argv[0];
3344d6bab26SThomas Quinot if (nfs_handle) {
3354d6bab26SThomas Quinot rc = 0;
33655298f03SThomas Quinot bzero(&fhnd, sizeof(fhnd));
33755298f03SThomas Quinot j = MIN(2 * sizeof(fhnd), strlen(file));
33855298f03SThomas Quinot if ((j & 1) != 0) {
3394d6bab26SThomas Quinot rc = -1;
3404d6bab26SThomas Quinot } else {
3414d6bab26SThomas Quinot while (j) {
34255298f03SThomas Quinot rc = hex2byte(&file[j - 2]);
34355298f03SThomas Quinot if (rc == -1)
3444d6bab26SThomas Quinot break;
34555298f03SThomas Quinot ((char*) &fhnd)[j / 2 - 1] = rc;
3464d6bab26SThomas Quinot j -= 2;
3474d6bab26SThomas Quinot }
3484d6bab26SThomas Quinot }
34955298f03SThomas Quinot if (rc == -1)
35055298f03SThomas Quinot errno = EINVAL;
35155298f03SThomas Quinot else
35255298f03SThomas Quinot rc = fhstat(&fhnd, &st);
3534d6bab26SThomas Quinot
3544d6bab26SThomas Quinot } else if (usestat) {
355ccdbe8ceSDoug Barton /*
356ccdbe8ceSDoug Barton * Try stat() and if it fails, fall back to
357ccdbe8ceSDoug Barton * lstat() just in case we're examining a
358ccdbe8ceSDoug Barton * broken symlink.
359ccdbe8ceSDoug Barton */
360ccdbe8ceSDoug Barton if ((rc = stat(file, &st)) == -1 &&
361ccdbe8ceSDoug Barton errno == ENOENT &&
362ccdbe8ceSDoug Barton (rc = lstat(file, &st)) == -1)
363ccdbe8ceSDoug Barton errno = ENOENT;
364ccdbe8ceSDoug Barton }
36542dc9c5aSEd Schouten else
36642dc9c5aSEd Schouten rc = lstat(file, &st);
36742dc9c5aSEd Schouten }
36806b6d7d8SDoug Barton
36906b6d7d8SDoug Barton if (rc == -1) {
37006b6d7d8SDoug Barton errs = 1;
3715b812a14SDoug Barton linkfail = 1;
3725b812a14SDoug Barton if (!quiet)
37337399d5bSJamie Landeg-Jones warn("%s", file);
37406b6d7d8SDoug Barton }
37506b6d7d8SDoug Barton else
376d7233fd6SDoug Barton output(&st, file, statfmt, fn, nonl);
37706b6d7d8SDoug Barton
37806b6d7d8SDoug Barton argv++;
37906b6d7d8SDoug Barton argc--;
38006b6d7d8SDoug Barton fn++;
38106b6d7d8SDoug Barton } while (argc > 0);
38206b6d7d8SDoug Barton
3835b812a14SDoug Barton return (am_readlink ? linkfail : errs);
38406b6d7d8SDoug Barton }
38506b6d7d8SDoug Barton
38624d9be57SJilles Tjoelker #if HAVE_STRUCT_STAT_ST_FLAGS
38724d9be57SJilles Tjoelker /*
38824d9be57SJilles Tjoelker * fflagstostr() wrapper that leaks only once
38924d9be57SJilles Tjoelker */
39024d9be57SJilles Tjoelker char *
xfflagstostr(unsigned long fflags)39124d9be57SJilles Tjoelker xfflagstostr(unsigned long fflags)
39224d9be57SJilles Tjoelker {
39324d9be57SJilles Tjoelker static char *str = NULL;
39424d9be57SJilles Tjoelker
39524d9be57SJilles Tjoelker if (str != NULL)
39624d9be57SJilles Tjoelker free(str);
39724d9be57SJilles Tjoelker
39824d9be57SJilles Tjoelker str = fflagstostr(fflags);
39924d9be57SJilles Tjoelker if (str == NULL)
40024d9be57SJilles Tjoelker err(1, "fflagstostr");
40124d9be57SJilles Tjoelker return (str);
40224d9be57SJilles Tjoelker }
40324d9be57SJilles Tjoelker #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
40424d9be57SJilles Tjoelker
40506b6d7d8SDoug Barton void
usage(const char * synopsis)4065b812a14SDoug Barton usage(const char *synopsis)
40706b6d7d8SDoug Barton {
40806b6d7d8SDoug Barton
4095b812a14SDoug Barton (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis);
41006b6d7d8SDoug Barton exit(1);
41106b6d7d8SDoug Barton }
41206b6d7d8SDoug Barton
41306b6d7d8SDoug Barton /*
41406b6d7d8SDoug Barton * Parses a format string.
41506b6d7d8SDoug Barton */
41606b6d7d8SDoug Barton void
output(const struct stat * st,const char * file,const char * statfmt,int fn,int nonl)41706b6d7d8SDoug Barton output(const struct stat *st, const char *file,
418d7233fd6SDoug Barton const char *statfmt, int fn, int nonl)
41906b6d7d8SDoug Barton {
42006b6d7d8SDoug Barton int flags, size, prec, ofmt, hilo, what;
421578800b5SDoug Barton char buf[PATH_MAX + 4 + 1];
42206b6d7d8SDoug Barton const char *subfmt;
42306b6d7d8SDoug Barton int nl, t, i;
42406b6d7d8SDoug Barton
4255b812a14SDoug Barton nl = 1;
42606b6d7d8SDoug Barton while (*statfmt != '\0') {
42706b6d7d8SDoug Barton
42806b6d7d8SDoug Barton /*
42906b6d7d8SDoug Barton * Non-format characters go straight out.
43006b6d7d8SDoug Barton */
43106b6d7d8SDoug Barton if (*statfmt != FMT_MAGIC) {
4325b812a14SDoug Barton addchar(stdout, *statfmt, &nl);
43306b6d7d8SDoug Barton statfmt++;
43406b6d7d8SDoug Barton continue;
43506b6d7d8SDoug Barton }
43606b6d7d8SDoug Barton
43706b6d7d8SDoug Barton /*
43806b6d7d8SDoug Barton * The current format "substring" starts here,
43906b6d7d8SDoug Barton * and then we skip the magic.
44006b6d7d8SDoug Barton */
44106b6d7d8SDoug Barton subfmt = statfmt;
44206b6d7d8SDoug Barton statfmt++;
44306b6d7d8SDoug Barton
44406b6d7d8SDoug Barton /*
44506b6d7d8SDoug Barton * Some simple one-character "formats".
44606b6d7d8SDoug Barton */
44706b6d7d8SDoug Barton switch (*statfmt) {
44806b6d7d8SDoug Barton case SIMPLE_NEWLINE:
4495b812a14SDoug Barton addchar(stdout, '\n', &nl);
45006b6d7d8SDoug Barton statfmt++;
45106b6d7d8SDoug Barton continue;
45206b6d7d8SDoug Barton case SIMPLE_TAB:
4535b812a14SDoug Barton addchar(stdout, '\t', &nl);
45406b6d7d8SDoug Barton statfmt++;
45506b6d7d8SDoug Barton continue;
45606b6d7d8SDoug Barton case SIMPLE_PERCENT:
4575b812a14SDoug Barton addchar(stdout, '%', &nl);
45806b6d7d8SDoug Barton statfmt++;
45906b6d7d8SDoug Barton continue;
46006b6d7d8SDoug Barton case SIMPLE_NUMBER: {
46106b6d7d8SDoug Barton char num[12], *p;
46206b6d7d8SDoug Barton
46306b6d7d8SDoug Barton snprintf(num, sizeof(num), "%d", fn);
46406b6d7d8SDoug Barton for (p = &num[0]; *p; p++)
4655b812a14SDoug Barton addchar(stdout, *p, &nl);
46606b6d7d8SDoug Barton statfmt++;
46706b6d7d8SDoug Barton continue;
46806b6d7d8SDoug Barton }
46906b6d7d8SDoug Barton }
47006b6d7d8SDoug Barton
47106b6d7d8SDoug Barton /*
47206b6d7d8SDoug Barton * This must be an actual format string. Format strings are
47306b6d7d8SDoug Barton * similar to printf(3) formats up to a point, and are of
47406b6d7d8SDoug Barton * the form:
47506b6d7d8SDoug Barton *
47606b6d7d8SDoug Barton * % required start of format
47706b6d7d8SDoug Barton * [-# +0] opt. format characters
47806b6d7d8SDoug Barton * size opt. field width
47906b6d7d8SDoug Barton * . opt. decimal separator, followed by
48006b6d7d8SDoug Barton * prec opt. precision
48106b6d7d8SDoug Barton * fmt opt. output specifier (string, numeric, etc.)
48206b6d7d8SDoug Barton * sub opt. sub field specifier (high, middle, low)
48306b6d7d8SDoug Barton * datum required field specifier (size, mode, etc)
48406b6d7d8SDoug Barton *
48506b6d7d8SDoug Barton * Only the % and the datum selector are required. All data
48606b6d7d8SDoug Barton * have reasonable default output forms. The "sub" specifier
48706b6d7d8SDoug Barton * only applies to certain data (mode, dev, rdev, filetype).
48806b6d7d8SDoug Barton * The symlink output defaults to STRING, yet will only emit
48906b6d7d8SDoug Barton * the leading " -> " if STRING is explicitly specified. The
49006b6d7d8SDoug Barton * sizerdev datum will generate rdev output for character or
49106b6d7d8SDoug Barton * block devices, and size output for all others.
49206b6d7d8SDoug Barton */
49306b6d7d8SDoug Barton flags = 0;
49406b6d7d8SDoug Barton do {
49506b6d7d8SDoug Barton if (*statfmt == FMT_POUND)
49606b6d7d8SDoug Barton flags |= FLAG_POUND;
49706b6d7d8SDoug Barton else if (*statfmt == FMT_SPACE)
49806b6d7d8SDoug Barton flags |= FLAG_SPACE;
49906b6d7d8SDoug Barton else if (*statfmt == FMT_PLUS)
50006b6d7d8SDoug Barton flags |= FLAG_PLUS;
50106b6d7d8SDoug Barton else if (*statfmt == FMT_ZERO)
50206b6d7d8SDoug Barton flags |= FLAG_ZERO;
50306b6d7d8SDoug Barton else if (*statfmt == FMT_MINUS)
50406b6d7d8SDoug Barton flags |= FLAG_MINUS;
50506b6d7d8SDoug Barton else
50606b6d7d8SDoug Barton break;
50706b6d7d8SDoug Barton statfmt++;
50806b6d7d8SDoug Barton } while (1/*CONSTCOND*/);
50906b6d7d8SDoug Barton
51006b6d7d8SDoug Barton size = -1;
51106b6d7d8SDoug Barton if (isdigit((unsigned)*statfmt)) {
51206b6d7d8SDoug Barton size = 0;
51306b6d7d8SDoug Barton while (isdigit((unsigned)*statfmt)) {
51406b6d7d8SDoug Barton size = (size * 10) + (*statfmt - '0');
51506b6d7d8SDoug Barton statfmt++;
51606b6d7d8SDoug Barton if (size < 0)
51706b6d7d8SDoug Barton goto badfmt;
51806b6d7d8SDoug Barton }
51906b6d7d8SDoug Barton }
52006b6d7d8SDoug Barton
52106b6d7d8SDoug Barton prec = -1;
52206b6d7d8SDoug Barton if (*statfmt == FMT_DOT) {
52306b6d7d8SDoug Barton statfmt++;
52406b6d7d8SDoug Barton
52506b6d7d8SDoug Barton prec = 0;
52606b6d7d8SDoug Barton while (isdigit((unsigned)*statfmt)) {
52706b6d7d8SDoug Barton prec = (prec * 10) + (*statfmt - '0');
52806b6d7d8SDoug Barton statfmt++;
52906b6d7d8SDoug Barton if (prec < 0)
53006b6d7d8SDoug Barton goto badfmt;
53106b6d7d8SDoug Barton }
53206b6d7d8SDoug Barton }
53306b6d7d8SDoug Barton
53406b6d7d8SDoug Barton #define fmtcase(x, y) case (y): (x) = (y); statfmt++; break
5355b812a14SDoug Barton #define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break
53606b6d7d8SDoug Barton switch (*statfmt) {
5375b812a14SDoug Barton fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL);
5385b812a14SDoug Barton fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL);
5395b812a14SDoug Barton fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED);
5405b812a14SDoug Barton fmtcasef(ofmt, FMT_HEX, FMTF_HEX);
5415b812a14SDoug Barton fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT);
5425b812a14SDoug Barton fmtcasef(ofmt, FMT_STRING, FMTF_STRING);
54306b6d7d8SDoug Barton default:
54406b6d7d8SDoug Barton ofmt = 0;
54506b6d7d8SDoug Barton break;
54606b6d7d8SDoug Barton }
54706b6d7d8SDoug Barton
54806b6d7d8SDoug Barton switch (*statfmt) {
54906b6d7d8SDoug Barton fmtcase(hilo, HIGH_PIECE);
55006b6d7d8SDoug Barton fmtcase(hilo, MIDDLE_PIECE);
55106b6d7d8SDoug Barton fmtcase(hilo, LOW_PIECE);
55206b6d7d8SDoug Barton default:
55306b6d7d8SDoug Barton hilo = 0;
55406b6d7d8SDoug Barton break;
55506b6d7d8SDoug Barton }
55606b6d7d8SDoug Barton
55706b6d7d8SDoug Barton switch (*statfmt) {
558b9f9338eSDoug Barton fmtcase(what, SHOW_realpath);
55906b6d7d8SDoug Barton fmtcase(what, SHOW_st_dev);
56006b6d7d8SDoug Barton fmtcase(what, SHOW_st_ino);
56106b6d7d8SDoug Barton fmtcase(what, SHOW_st_mode);
56206b6d7d8SDoug Barton fmtcase(what, SHOW_st_nlink);
56306b6d7d8SDoug Barton fmtcase(what, SHOW_st_uid);
56406b6d7d8SDoug Barton fmtcase(what, SHOW_st_gid);
56506b6d7d8SDoug Barton fmtcase(what, SHOW_st_rdev);
56606b6d7d8SDoug Barton fmtcase(what, SHOW_st_atime);
56706b6d7d8SDoug Barton fmtcase(what, SHOW_st_mtime);
56806b6d7d8SDoug Barton fmtcase(what, SHOW_st_ctime);
5691cee9d8dSDoug Barton fmtcase(what, SHOW_st_btime);
57006b6d7d8SDoug Barton fmtcase(what, SHOW_st_size);
57106b6d7d8SDoug Barton fmtcase(what, SHOW_st_blocks);
57206b6d7d8SDoug Barton fmtcase(what, SHOW_st_blksize);
57306b6d7d8SDoug Barton fmtcase(what, SHOW_st_flags);
57406b6d7d8SDoug Barton fmtcase(what, SHOW_st_gen);
57506b6d7d8SDoug Barton fmtcase(what, SHOW_symlink);
57606b6d7d8SDoug Barton fmtcase(what, SHOW_filetype);
57706b6d7d8SDoug Barton fmtcase(what, SHOW_filename);
57806b6d7d8SDoug Barton fmtcase(what, SHOW_sizerdev);
57906b6d7d8SDoug Barton default:
58006b6d7d8SDoug Barton goto badfmt;
58106b6d7d8SDoug Barton }
5825b812a14SDoug Barton #undef fmtcasef
58306b6d7d8SDoug Barton #undef fmtcase
58406b6d7d8SDoug Barton
58506b6d7d8SDoug Barton t = format1(st,
58606b6d7d8SDoug Barton file,
58706b6d7d8SDoug Barton subfmt, statfmt - subfmt,
5885b812a14SDoug Barton buf, sizeof(buf),
58906b6d7d8SDoug Barton flags, size, prec, ofmt, hilo, what);
59006b6d7d8SDoug Barton
5919b3f4394SDoug Barton for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++)
5925b812a14SDoug Barton addchar(stdout, buf[i], &nl);
59306b6d7d8SDoug Barton
59406b6d7d8SDoug Barton continue;
59506b6d7d8SDoug Barton
59606b6d7d8SDoug Barton badfmt:
59706b6d7d8SDoug Barton errx(1, "%.*s: bad format",
59806b6d7d8SDoug Barton (int)(statfmt - subfmt + 1), subfmt);
59906b6d7d8SDoug Barton }
60006b6d7d8SDoug Barton
60106b6d7d8SDoug Barton if (!nl && !nonl)
6025b812a14SDoug Barton (void)fputc('\n', stdout);
6035b812a14SDoug Barton (void)fflush(stdout);
60406b6d7d8SDoug Barton }
60506b6d7d8SDoug Barton
60606b6d7d8SDoug Barton /*
60706b6d7d8SDoug Barton * Arranges output according to a single parsed format substring.
60806b6d7d8SDoug Barton */
60906b6d7d8SDoug Barton 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)61006b6d7d8SDoug Barton format1(const struct stat *st,
61106b6d7d8SDoug Barton const char *file,
61206b6d7d8SDoug Barton const char *fmt, int flen,
61306b6d7d8SDoug Barton char *buf, size_t blen,
61406b6d7d8SDoug Barton int flags, int size, int prec, int ofmt,
61506b6d7d8SDoug Barton int hilo, int what)
61606b6d7d8SDoug Barton {
61706b6d7d8SDoug Barton u_int64_t data;
6189b3f4394SDoug Barton char *stmp, lfmt[24], tmp[20];
6199b3f4394SDoug Barton const char *sdata;
6205b812a14SDoug Barton char smode[12], sid[12], path[PATH_MAX + 4];
62106b6d7d8SDoug Barton const struct timespec *tsp;
62206b6d7d8SDoug Barton struct timespec ts;
62306b6d7d8SDoug Barton struct tm *tm;
62406b6d7d8SDoug Barton int l, small, formats;
62506b6d7d8SDoug Barton
62606b6d7d8SDoug Barton tsp = NULL;
62706b6d7d8SDoug Barton formats = 0;
62806b6d7d8SDoug Barton small = 0;
62906b6d7d8SDoug Barton
63006b6d7d8SDoug Barton /*
63106b6d7d8SDoug Barton * First, pick out the data and tweak it based on hilo or
63206b6d7d8SDoug Barton * specified output format (symlink output only).
63306b6d7d8SDoug Barton */
63406b6d7d8SDoug Barton switch (what) {
63506b6d7d8SDoug Barton case SHOW_st_dev:
63606b6d7d8SDoug Barton case SHOW_st_rdev:
63706b6d7d8SDoug Barton small = (sizeof(st->st_dev) == 4);
63806b6d7d8SDoug Barton data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev;
639dad5d4e0SDoug Barton #if HAVE_DEVNAME
640050e4bcaSAlexander Naumochkin sdata = devname(what == SHOW_st_dev ? st->st_dev :
641050e4bcaSAlexander Naumochkin st->st_rdev, S_ISCHR(st->st_mode) ? S_IFCHR :
642050e4bcaSAlexander Naumochkin (S_ISBLK(st->st_mode) ? S_IFBLK : 0));
643dad5d4e0SDoug Barton #endif /* HAVE_DEVNAME */
64406b6d7d8SDoug Barton if (hilo == HIGH_PIECE) {
64506b6d7d8SDoug Barton data = major(data);
64606b6d7d8SDoug Barton hilo = 0;
64706b6d7d8SDoug Barton }
64806b6d7d8SDoug Barton else if (hilo == LOW_PIECE) {
64906b6d7d8SDoug Barton data = minor((unsigned)data);
65006b6d7d8SDoug Barton hilo = 0;
65106b6d7d8SDoug Barton }
6525b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
653dad5d4e0SDoug Barton #if HAVE_DEVNAME
6545b812a14SDoug Barton FMTF_STRING;
655dad5d4e0SDoug Barton #else /* HAVE_DEVNAME */
656dad5d4e0SDoug Barton 0;
657dad5d4e0SDoug Barton #endif /* HAVE_DEVNAME */
65806b6d7d8SDoug Barton if (ofmt == 0)
6595b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
66006b6d7d8SDoug Barton break;
66106b6d7d8SDoug Barton case SHOW_st_ino:
66206b6d7d8SDoug Barton small = (sizeof(st->st_ino) == 4);
66306b6d7d8SDoug Barton data = st->st_ino;
66406b6d7d8SDoug Barton sdata = NULL;
6655b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
66606b6d7d8SDoug Barton if (ofmt == 0)
6675b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
66806b6d7d8SDoug Barton break;
66906b6d7d8SDoug Barton case SHOW_st_mode:
67006b6d7d8SDoug Barton small = (sizeof(st->st_mode) == 4);
67106b6d7d8SDoug Barton data = st->st_mode;
67206b6d7d8SDoug Barton strmode(st->st_mode, smode);
6739b3f4394SDoug Barton stmp = smode;
6749b3f4394SDoug Barton l = strlen(stmp);
6759b3f4394SDoug Barton if (stmp[l - 1] == ' ')
6769b3f4394SDoug Barton stmp[--l] = '\0';
67706b6d7d8SDoug Barton if (hilo == HIGH_PIECE) {
67806b6d7d8SDoug Barton data >>= 12;
6799b3f4394SDoug Barton stmp += 1;
6809b3f4394SDoug Barton stmp[3] = '\0';
68106b6d7d8SDoug Barton hilo = 0;
68206b6d7d8SDoug Barton }
68306b6d7d8SDoug Barton else if (hilo == MIDDLE_PIECE) {
68406b6d7d8SDoug Barton data = (data >> 9) & 07;
6859b3f4394SDoug Barton stmp += 4;
6869b3f4394SDoug Barton stmp[3] = '\0';
68706b6d7d8SDoug Barton hilo = 0;
68806b6d7d8SDoug Barton }
68906b6d7d8SDoug Barton else if (hilo == LOW_PIECE) {
69006b6d7d8SDoug Barton data &= 0777;
6919b3f4394SDoug Barton stmp += 7;
6929b3f4394SDoug Barton stmp[3] = '\0';
69306b6d7d8SDoug Barton hilo = 0;
69406b6d7d8SDoug Barton }
6959b3f4394SDoug Barton sdata = stmp;
6965b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
6975b812a14SDoug Barton FMTF_STRING;
69806b6d7d8SDoug Barton if (ofmt == 0)
6995b812a14SDoug Barton ofmt = FMTF_OCTAL;
70006b6d7d8SDoug Barton break;
70106b6d7d8SDoug Barton case SHOW_st_nlink:
70206b6d7d8SDoug Barton small = (sizeof(st->st_dev) == 4);
70306b6d7d8SDoug Barton data = st->st_nlink;
70406b6d7d8SDoug Barton sdata = NULL;
7055b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
70606b6d7d8SDoug Barton if (ofmt == 0)
7075b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
70806b6d7d8SDoug Barton break;
70906b6d7d8SDoug Barton case SHOW_st_uid:
71006b6d7d8SDoug Barton small = (sizeof(st->st_uid) == 4);
71106b6d7d8SDoug Barton data = st->st_uid;
712a04ed761SConrad Meyer sdata = user_from_uid(st->st_uid, 1);
713a04ed761SConrad Meyer if (sdata == NULL) {
71406b6d7d8SDoug Barton snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid);
71506b6d7d8SDoug Barton sdata = sid;
71606b6d7d8SDoug Barton }
7175b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
7185b812a14SDoug Barton FMTF_STRING;
71906b6d7d8SDoug Barton if (ofmt == 0)
7205b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
72106b6d7d8SDoug Barton break;
72206b6d7d8SDoug Barton case SHOW_st_gid:
72306b6d7d8SDoug Barton small = (sizeof(st->st_gid) == 4);
72406b6d7d8SDoug Barton data = st->st_gid;
725a04ed761SConrad Meyer sdata = group_from_gid(st->st_gid, 1);
726a04ed761SConrad Meyer if (sdata == NULL) {
72706b6d7d8SDoug Barton snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid);
72806b6d7d8SDoug Barton sdata = sid;
72906b6d7d8SDoug Barton }
7305b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
7315b812a14SDoug Barton FMTF_STRING;
73206b6d7d8SDoug Barton if (ofmt == 0)
7335b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
73406b6d7d8SDoug Barton break;
73506b6d7d8SDoug Barton case SHOW_st_atime:
73606b6d7d8SDoug Barton tsp = &st->st_atimespec;
73706b6d7d8SDoug Barton /* FALLTHROUGH */
73806b6d7d8SDoug Barton case SHOW_st_mtime:
73906b6d7d8SDoug Barton if (tsp == NULL)
74006b6d7d8SDoug Barton tsp = &st->st_mtimespec;
74106b6d7d8SDoug Barton /* FALLTHROUGH */
74206b6d7d8SDoug Barton case SHOW_st_ctime:
74306b6d7d8SDoug Barton if (tsp == NULL)
74406b6d7d8SDoug Barton tsp = &st->st_ctimespec;
7451cee9d8dSDoug Barton /* FALLTHROUGH */
746dad5d4e0SDoug Barton #if HAVE_STRUCT_STAT_ST_BIRTHTIME
7471cee9d8dSDoug Barton case SHOW_st_btime:
7481cee9d8dSDoug Barton if (tsp == NULL)
7491cee9d8dSDoug Barton tsp = &st->st_birthtimespec;
750dad5d4e0SDoug Barton #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
75106b6d7d8SDoug Barton ts = *tsp; /* copy so we can muck with it */
75206b6d7d8SDoug Barton small = (sizeof(ts.tv_sec) == 4);
75306b6d7d8SDoug Barton data = ts.tv_sec;
75406b6d7d8SDoug Barton tm = localtime(&ts.tv_sec);
75549399277SDoug Barton if (tm == NULL) {
75649399277SDoug Barton ts.tv_sec = 0;
75749399277SDoug Barton tm = localtime(&ts.tv_sec);
75849399277SDoug Barton }
75920f8331aSStefan Eßer (void)setlocale(LC_TIME, "");
76006b6d7d8SDoug Barton (void)strftime(path, sizeof(path), timefmt, tm);
76106b6d7d8SDoug Barton sdata = path;
7625b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
7635b812a14SDoug Barton FMTF_FLOAT | FMTF_STRING;
76406b6d7d8SDoug Barton if (ofmt == 0)
7655b812a14SDoug Barton ofmt = FMTF_DECIMAL;
76606b6d7d8SDoug Barton break;
76706b6d7d8SDoug Barton case SHOW_st_size:
76806b6d7d8SDoug Barton small = (sizeof(st->st_size) == 4);
76906b6d7d8SDoug Barton data = st->st_size;
77006b6d7d8SDoug Barton sdata = NULL;
7715b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
77206b6d7d8SDoug Barton if (ofmt == 0)
7735b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
77406b6d7d8SDoug Barton break;
77506b6d7d8SDoug Barton case SHOW_st_blocks:
77606b6d7d8SDoug Barton small = (sizeof(st->st_blocks) == 4);
77706b6d7d8SDoug Barton data = st->st_blocks;
77806b6d7d8SDoug Barton sdata = NULL;
7795b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
78006b6d7d8SDoug Barton if (ofmt == 0)
7815b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
78206b6d7d8SDoug Barton break;
78306b6d7d8SDoug Barton case SHOW_st_blksize:
78406b6d7d8SDoug Barton small = (sizeof(st->st_blksize) == 4);
78506b6d7d8SDoug Barton data = st->st_blksize;
78606b6d7d8SDoug Barton sdata = NULL;
7875b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
78806b6d7d8SDoug Barton if (ofmt == 0)
7895b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
79006b6d7d8SDoug Barton break;
791dad5d4e0SDoug Barton #if HAVE_STRUCT_STAT_ST_FLAGS
79206b6d7d8SDoug Barton case SHOW_st_flags:
79306b6d7d8SDoug Barton small = (sizeof(st->st_flags) == 4);
79406b6d7d8SDoug Barton data = st->st_flags;
79524d9be57SJilles Tjoelker sdata = xfflagstostr(st->st_flags);
79624d9be57SJilles Tjoelker if (*sdata == '\0')
79724d9be57SJilles Tjoelker sdata = "-";
79824d9be57SJilles Tjoelker formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
79924d9be57SJilles Tjoelker FMTF_STRING;
80006b6d7d8SDoug Barton if (ofmt == 0)
8015b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
80206b6d7d8SDoug Barton break;
803dad5d4e0SDoug Barton #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
804dad5d4e0SDoug Barton #if HAVE_STRUCT_STAT_ST_GEN
80506b6d7d8SDoug Barton case SHOW_st_gen:
80606b6d7d8SDoug Barton small = (sizeof(st->st_gen) == 4);
80706b6d7d8SDoug Barton data = st->st_gen;
80806b6d7d8SDoug Barton sdata = NULL;
8095b812a14SDoug Barton formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
81006b6d7d8SDoug Barton if (ofmt == 0)
8115b812a14SDoug Barton ofmt = FMTF_UNSIGNED;
81206b6d7d8SDoug Barton break;
813dad5d4e0SDoug Barton #endif /* HAVE_STRUCT_STAT_ST_GEN */
814b9f9338eSDoug Barton case SHOW_realpath:
815b9f9338eSDoug Barton small = 0;
816b9f9338eSDoug Barton data = 0;
817b9f9338eSDoug Barton if (file == NULL) {
81834aba78bSDoug Barton (void)strlcpy(path, "(stdin)", sizeof(path));
819b9f9338eSDoug Barton sdata = path;
820b9f9338eSDoug Barton } else {
821b9f9338eSDoug Barton snprintf(path, sizeof(path), " -> ");
822b9f9338eSDoug Barton if (realpath(file, path + 4) == NULL) {
823b9f9338eSDoug Barton linkfail = 1;
824b9f9338eSDoug Barton l = 0;
825b9f9338eSDoug Barton path[0] = '\0';
826b9f9338eSDoug Barton }
827b9f9338eSDoug Barton sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
828b9f9338eSDoug Barton }
829b9f9338eSDoug Barton
830b9f9338eSDoug Barton formats = FMTF_STRING;
831b9f9338eSDoug Barton if (ofmt == 0)
832b9f9338eSDoug Barton ofmt = FMTF_STRING;
833b9f9338eSDoug Barton break;
83406b6d7d8SDoug Barton case SHOW_symlink:
83506b6d7d8SDoug Barton small = 0;
83606b6d7d8SDoug Barton data = 0;
83706b6d7d8SDoug Barton if (S_ISLNK(st->st_mode)) {
83806b6d7d8SDoug Barton snprintf(path, sizeof(path), " -> ");
8397d5b77efSDoug Barton l = readlink(file, path + 4, sizeof(path) - 4 - 1);
84006b6d7d8SDoug Barton if (l == -1) {
8415b812a14SDoug Barton linkfail = 1;
84206b6d7d8SDoug Barton l = 0;
84306b6d7d8SDoug Barton path[0] = '\0';
84406b6d7d8SDoug Barton }
84506b6d7d8SDoug Barton path[l + 4] = '\0';
8465b812a14SDoug Barton sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
84706b6d7d8SDoug Barton }
8485b812a14SDoug Barton else {
8495b812a14SDoug Barton linkfail = 1;
85006b6d7d8SDoug Barton sdata = "";
8515b812a14SDoug Barton }
8525b812a14SDoug Barton formats = FMTF_STRING;
85306b6d7d8SDoug Barton if (ofmt == 0)
8545b812a14SDoug Barton ofmt = FMTF_STRING;
85506b6d7d8SDoug Barton break;
85606b6d7d8SDoug Barton case SHOW_filetype:
85706b6d7d8SDoug Barton small = 0;
85806b6d7d8SDoug Barton data = 0;
8599b3f4394SDoug Barton sdata = "";
86006b6d7d8SDoug Barton if (hilo == 0 || hilo == LOW_PIECE) {
86106b6d7d8SDoug Barton switch (st->st_mode & S_IFMT) {
8629b3f4394SDoug Barton case S_IFIFO: sdata = "|"; break;
8639b3f4394SDoug Barton case S_IFDIR: sdata = "/"; break;
86406b6d7d8SDoug Barton case S_IFREG:
86506b6d7d8SDoug Barton if (st->st_mode &
86606b6d7d8SDoug Barton (S_IXUSR | S_IXGRP | S_IXOTH))
8679b3f4394SDoug Barton sdata = "*";
86806b6d7d8SDoug Barton break;
8699b3f4394SDoug Barton case S_IFLNK: sdata = "@"; break;
8709b3f4394SDoug Barton case S_IFSOCK: sdata = "="; break;
871dad5d4e0SDoug Barton #ifdef S_IFWHT
8729b3f4394SDoug Barton case S_IFWHT: sdata = "%"; break;
873dad5d4e0SDoug Barton #endif /* S_IFWHT */
874dad5d4e0SDoug Barton #ifdef S_IFDOOR
8759b3f4394SDoug Barton case S_IFDOOR: sdata = ">"; break;
876dad5d4e0SDoug Barton #endif /* S_IFDOOR */
87706b6d7d8SDoug Barton }
87806b6d7d8SDoug Barton hilo = 0;
87906b6d7d8SDoug Barton }
88006b6d7d8SDoug Barton else if (hilo == HIGH_PIECE) {
88106b6d7d8SDoug Barton switch (st->st_mode & S_IFMT) {
88206b6d7d8SDoug Barton case S_IFIFO: sdata = "Fifo File"; break;
88306b6d7d8SDoug Barton case S_IFCHR: sdata = "Character Device"; break;
88406b6d7d8SDoug Barton case S_IFDIR: sdata = "Directory"; break;
88506b6d7d8SDoug Barton case S_IFBLK: sdata = "Block Device"; break;
88606b6d7d8SDoug Barton case S_IFREG: sdata = "Regular File"; break;
88706b6d7d8SDoug Barton case S_IFLNK: sdata = "Symbolic Link"; break;
88806b6d7d8SDoug Barton case S_IFSOCK: sdata = "Socket"; break;
889dad5d4e0SDoug Barton #ifdef S_IFWHT
89006b6d7d8SDoug Barton case S_IFWHT: sdata = "Whiteout File"; break;
891dad5d4e0SDoug Barton #endif /* S_IFWHT */
892dad5d4e0SDoug Barton #ifdef S_IFDOOR
893dad5d4e0SDoug Barton case S_IFDOOR: sdata = "Door"; break;
894dad5d4e0SDoug Barton #endif /* S_IFDOOR */
89506b6d7d8SDoug Barton default: sdata = "???"; break;
89606b6d7d8SDoug Barton }
89706b6d7d8SDoug Barton hilo = 0;
89806b6d7d8SDoug Barton }
8995b812a14SDoug Barton formats = FMTF_STRING;
90006b6d7d8SDoug Barton if (ofmt == 0)
9015b812a14SDoug Barton ofmt = FMTF_STRING;
90206b6d7d8SDoug Barton break;
90306b6d7d8SDoug Barton case SHOW_filename:
90406b6d7d8SDoug Barton small = 0;
90506b6d7d8SDoug Barton data = 0;
90634aba78bSDoug Barton (void)strlcpy(path, file, sizeof(path));
90706b6d7d8SDoug Barton sdata = path;
9085b812a14SDoug Barton formats = FMTF_STRING;
90906b6d7d8SDoug Barton if (ofmt == 0)
9105b812a14SDoug Barton ofmt = FMTF_STRING;
91106b6d7d8SDoug Barton break;
91206b6d7d8SDoug Barton case SHOW_sizerdev:
91306b6d7d8SDoug Barton if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
91406b6d7d8SDoug Barton char majdev[20], mindev[20];
91506b6d7d8SDoug Barton int l1, l2;
91606b6d7d8SDoug Barton
91706b6d7d8SDoug Barton l1 = format1(st,
91806b6d7d8SDoug Barton file,
91906b6d7d8SDoug Barton fmt, flen,
92006b6d7d8SDoug Barton majdev, sizeof(majdev),
92106b6d7d8SDoug Barton flags, size, prec,
92206b6d7d8SDoug Barton ofmt, HIGH_PIECE, SHOW_st_rdev);
92306b6d7d8SDoug Barton l2 = format1(st,
92406b6d7d8SDoug Barton file,
92506b6d7d8SDoug Barton fmt, flen,
92606b6d7d8SDoug Barton mindev, sizeof(mindev),
92706b6d7d8SDoug Barton flags, size, prec,
92806b6d7d8SDoug Barton ofmt, LOW_PIECE, SHOW_st_rdev);
92906b6d7d8SDoug Barton return (snprintf(buf, blen, "%.*s,%.*s",
93006b6d7d8SDoug Barton l1, majdev, l2, mindev));
93106b6d7d8SDoug Barton }
93206b6d7d8SDoug Barton else {
93306b6d7d8SDoug Barton return (format1(st,
93406b6d7d8SDoug Barton file,
93506b6d7d8SDoug Barton fmt, flen,
93606b6d7d8SDoug Barton buf, blen,
93706b6d7d8SDoug Barton flags, size, prec,
93806b6d7d8SDoug Barton ofmt, 0, SHOW_st_size));
93906b6d7d8SDoug Barton }
94006b6d7d8SDoug Barton /*NOTREACHED*/
94106b6d7d8SDoug Barton default:
94206b6d7d8SDoug Barton errx(1, "%.*s: bad format", (int)flen, fmt);
94306b6d7d8SDoug Barton }
94406b6d7d8SDoug Barton
94506b6d7d8SDoug Barton /*
94606b6d7d8SDoug Barton * If a subdatum was specified but not supported, or an output
94706b6d7d8SDoug Barton * format was selected that is not supported, that's an error.
94806b6d7d8SDoug Barton */
94906b6d7d8SDoug Barton if (hilo != 0 || (ofmt & formats) == 0)
95006b6d7d8SDoug Barton errx(1, "%.*s: bad format", (int)flen, fmt);
95106b6d7d8SDoug Barton
95206b6d7d8SDoug Barton /*
95306b6d7d8SDoug Barton * Assemble the format string for passing to printf(3).
95406b6d7d8SDoug Barton */
95506b6d7d8SDoug Barton lfmt[0] = '\0';
95606b6d7d8SDoug Barton (void)strcat(lfmt, "%");
95706b6d7d8SDoug Barton if (flags & FLAG_POUND)
95806b6d7d8SDoug Barton (void)strcat(lfmt, "#");
95906b6d7d8SDoug Barton if (flags & FLAG_SPACE)
96006b6d7d8SDoug Barton (void)strcat(lfmt, " ");
96106b6d7d8SDoug Barton if (flags & FLAG_PLUS)
96206b6d7d8SDoug Barton (void)strcat(lfmt, "+");
96306b6d7d8SDoug Barton if (flags & FLAG_MINUS)
96406b6d7d8SDoug Barton (void)strcat(lfmt, "-");
96506b6d7d8SDoug Barton if (flags & FLAG_ZERO)
96606b6d7d8SDoug Barton (void)strcat(lfmt, "0");
96706b6d7d8SDoug Barton
96806b6d7d8SDoug Barton /*
96906b6d7d8SDoug Barton * Only the timespecs support the FLOAT output format, and that
97006b6d7d8SDoug Barton * requires work that differs from the other formats.
97106b6d7d8SDoug Barton */
9725b812a14SDoug Barton if (ofmt == FMTF_FLOAT) {
97306b6d7d8SDoug Barton /*
97406b6d7d8SDoug Barton * Nothing after the decimal point, so just print seconds.
97506b6d7d8SDoug Barton */
97606b6d7d8SDoug Barton if (prec == 0) {
97706b6d7d8SDoug Barton if (size != -1) {
97806b6d7d8SDoug Barton (void)snprintf(tmp, sizeof(tmp), "%d", size);
97906b6d7d8SDoug Barton (void)strcat(lfmt, tmp);
98006b6d7d8SDoug Barton }
981da78faccSDoug Barton (void)strcat(lfmt, "lld");
982da78faccSDoug Barton return (snprintf(buf, blen, lfmt,
983da78faccSDoug Barton (long long)ts.tv_sec));
98406b6d7d8SDoug Barton }
98506b6d7d8SDoug Barton
98606b6d7d8SDoug Barton /*
98706b6d7d8SDoug Barton * Unspecified precision gets all the precision we have:
98806b6d7d8SDoug Barton * 9 digits.
98906b6d7d8SDoug Barton */
99006b6d7d8SDoug Barton if (prec == -1)
99106b6d7d8SDoug Barton prec = 9;
99206b6d7d8SDoug Barton
99306b6d7d8SDoug Barton /*
99406b6d7d8SDoug Barton * Adjust the size for the decimal point and the digits
99506b6d7d8SDoug Barton * that will follow.
99606b6d7d8SDoug Barton */
99706b6d7d8SDoug Barton size -= prec + 1;
99806b6d7d8SDoug Barton
99906b6d7d8SDoug Barton /*
100006b6d7d8SDoug Barton * Any leftover size that's legitimate will be used.
100106b6d7d8SDoug Barton */
100206b6d7d8SDoug Barton if (size > 0) {
100306b6d7d8SDoug Barton (void)snprintf(tmp, sizeof(tmp), "%d", size);
100406b6d7d8SDoug Barton (void)strcat(lfmt, tmp);
100506b6d7d8SDoug Barton }
1006da78faccSDoug Barton /* Seconds: time_t cast to long long. */
1007da78faccSDoug Barton (void)strcat(lfmt, "lld");
100806b6d7d8SDoug Barton
100906b6d7d8SDoug Barton /*
101006b6d7d8SDoug Barton * The stuff after the decimal point always needs zero
101106b6d7d8SDoug Barton * filling.
101206b6d7d8SDoug Barton */
101306b6d7d8SDoug Barton (void)strcat(lfmt, ".%0");
101406b6d7d8SDoug Barton
101506b6d7d8SDoug Barton /*
101606b6d7d8SDoug Barton * We can "print" at most nine digits of precision. The
101706b6d7d8SDoug Barton * rest we will pad on at the end.
1018da78faccSDoug Barton *
1019da78faccSDoug Barton * Nanoseconds: long.
102006b6d7d8SDoug Barton */
10215d49c30cSMarcelo Araujo (void)snprintf(tmp, sizeof(tmp), "%dld", MIN(prec, 9));
102206b6d7d8SDoug Barton (void)strcat(lfmt, tmp);
102306b6d7d8SDoug Barton
102406b6d7d8SDoug Barton /*
1025*49eeca74SLO WEN-CHIEN * For precision of less than nine digits, trim off the
102606b6d7d8SDoug Barton * less significant figures.
102706b6d7d8SDoug Barton */
102806b6d7d8SDoug Barton for (; prec < 9; prec++)
102906b6d7d8SDoug Barton ts.tv_nsec /= 10;
103006b6d7d8SDoug Barton
103106b6d7d8SDoug Barton /*
103206b6d7d8SDoug Barton * Use the format, and then tack on any zeroes that
103306b6d7d8SDoug Barton * might be required to make up the requested precision.
103406b6d7d8SDoug Barton */
1035da78faccSDoug Barton l = snprintf(buf, blen, lfmt, (long long)ts.tv_sec, ts.tv_nsec);
10369b3f4394SDoug Barton for (; prec > 9 && l < (int)blen; prec--, l++)
103706b6d7d8SDoug Barton (void)strcat(buf, "0");
103806b6d7d8SDoug Barton return (l);
103906b6d7d8SDoug Barton }
104006b6d7d8SDoug Barton
104106b6d7d8SDoug Barton /*
104206b6d7d8SDoug Barton * Add on size and precision, if specified, to the format.
104306b6d7d8SDoug Barton */
104406b6d7d8SDoug Barton if (size != -1) {
104506b6d7d8SDoug Barton (void)snprintf(tmp, sizeof(tmp), "%d", size);
104606b6d7d8SDoug Barton (void)strcat(lfmt, tmp);
104706b6d7d8SDoug Barton }
104806b6d7d8SDoug Barton if (prec != -1) {
104906b6d7d8SDoug Barton (void)snprintf(tmp, sizeof(tmp), ".%d", prec);
105006b6d7d8SDoug Barton (void)strcat(lfmt, tmp);
105106b6d7d8SDoug Barton }
105206b6d7d8SDoug Barton
105306b6d7d8SDoug Barton /*
105406b6d7d8SDoug Barton * String output uses the temporary sdata.
105506b6d7d8SDoug Barton */
10565b812a14SDoug Barton if (ofmt == FMTF_STRING) {
105706b6d7d8SDoug Barton if (sdata == NULL)
105806b6d7d8SDoug Barton errx(1, "%.*s: bad format", (int)flen, fmt);
105906b6d7d8SDoug Barton (void)strcat(lfmt, "s");
106006b6d7d8SDoug Barton return (snprintf(buf, blen, lfmt, sdata));
106106b6d7d8SDoug Barton }
106206b6d7d8SDoug Barton
106306b6d7d8SDoug Barton /*
106406b6d7d8SDoug Barton * Ensure that sign extension does not cause bad looking output
106506b6d7d8SDoug Barton * for some forms.
106606b6d7d8SDoug Barton */
10675b812a14SDoug Barton if (small && ofmt != FMTF_DECIMAL)
106806b6d7d8SDoug Barton data = (u_int32_t)data;
106906b6d7d8SDoug Barton
107006b6d7d8SDoug Barton /*
107106b6d7d8SDoug Barton * The four "numeric" output forms.
107206b6d7d8SDoug Barton */
107306b6d7d8SDoug Barton (void)strcat(lfmt, "ll");
107406b6d7d8SDoug Barton switch (ofmt) {
10755b812a14SDoug Barton case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break;
10765b812a14SDoug Barton case FMTF_OCTAL: (void)strcat(lfmt, "o"); break;
10775b812a14SDoug Barton case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break;
10785b812a14SDoug Barton case FMTF_HEX: (void)strcat(lfmt, "x"); break;
107906b6d7d8SDoug Barton }
108006b6d7d8SDoug Barton
108106b6d7d8SDoug Barton return (snprintf(buf, blen, lfmt, data));
108206b6d7d8SDoug Barton }
108355298f03SThomas Quinot
108455298f03SThomas Quinot
108555298f03SThomas Quinot #define hex2nibble(c) (c <= '9' ? c - '0' : toupper(c) - 'A' + 10)
108655298f03SThomas Quinot int
hex2byte(const char c[2])108755298f03SThomas Quinot hex2byte(const char c[2]) {
108855298f03SThomas Quinot if (!(ishexnumber(c[0]) && ishexnumber(c[1])))
108955298f03SThomas Quinot return -1;
109055298f03SThomas Quinot return (hex2nibble(c[0]) << 4) + hex2nibble(c[1]);
109155298f03SThomas Quinot }
1092