xref: /freebsd-src/usr.bin/stat/stat.c (revision 49eeca743b70d13883ed1db73b6e04fbab791681)
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