stat.c revision 1.4 1 1.4 atatat /* $NetBSD: stat.c,v 1.4 2002/07/08 18:48:42 atatat Exp $ */
2 1.1 atatat
3 1.1 atatat /*
4 1.1 atatat * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 1.1 atatat * All rights reserved.
6 1.1 atatat *
7 1.1 atatat * This code is derived from software contributed to The NetBSD Foundation
8 1.1 atatat * by Andrew Brown.
9 1.1 atatat *
10 1.1 atatat * Redistribution and use in source and binary forms, with or without
11 1.1 atatat * modification, are permitted provided that the following conditions
12 1.1 atatat * are met:
13 1.1 atatat * 1. Redistributions of source code must retain the above copyright
14 1.1 atatat * notice, this list of conditions and the following disclaimer.
15 1.1 atatat * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 atatat * notice, this list of conditions and the following disclaimer in the
17 1.1 atatat * documentation and/or other materials provided with the distribution.
18 1.1 atatat * 3. All advertising materials mentioning features or use of this software
19 1.1 atatat * must display the following acknowledgement:
20 1.1 atatat * This product includes software developed by the NetBSD
21 1.1 atatat * Foundation, Inc. and its contributors.
22 1.1 atatat * 4. Neither the name of The NetBSD Foundation nor the names of its
23 1.1 atatat * contributors may be used to endorse or promote products derived
24 1.1 atatat * from this software without specific prior written permission.
25 1.1 atatat *
26 1.1 atatat * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 1.1 atatat * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 1.1 atatat * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 1.1 atatat * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 1.1 atatat * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 1.1 atatat * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 1.1 atatat * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 1.1 atatat * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 1.1 atatat * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 1.1 atatat * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 1.1 atatat * POSSIBILITY OF SUCH DAMAGE.
37 1.1 atatat */
38 1.1 atatat
39 1.1 atatat #include <sys/cdefs.h>
40 1.1 atatat #ifndef lint
41 1.4 atatat __RCSID("$NetBSD: stat.c,v 1.4 2002/07/08 18:48:42 atatat Exp $");
42 1.1 atatat #endif
43 1.1 atatat
44 1.1 atatat #include <sys/types.h>
45 1.1 atatat #include <sys/param.h>
46 1.1 atatat #include <sys/stat.h>
47 1.1 atatat #include <unistd.h>
48 1.4 atatat #include <fcntl.h>
49 1.1 atatat #include <err.h>
50 1.1 atatat #include <string.h>
51 1.1 atatat #include <stdio.h>
52 1.1 atatat #include <ctype.h>
53 1.1 atatat #include <stddef.h>
54 1.1 atatat #include <stdlib.h>
55 1.1 atatat #include <pwd.h>
56 1.1 atatat #include <grp.h>
57 1.1 atatat
58 1.1 atatat #define DEF_FORMAT \
59 1.1 atatat "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" %k %b %N"
60 1.1 atatat #define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c %k %b %N"
61 1.1 atatat #define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY"
62 1.1 atatat #define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY"
63 1.1 atatat #define SHELL_FORMAT \
64 1.1 atatat "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \
65 1.1 atatat "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \
66 1.1 atatat "st_atimespec=%a st_mtimespec=%m st_ctimespec=%c " \
67 1.1 atatat "st_blksize=%k st_blocks=%b"
68 1.1 atatat #define LINUX_FORMAT \
69 1.1 atatat " File: \"%N\"%n" \
70 1.1 atatat " Size: %-11z FileType: %HT%n" \
71 1.1 atatat " Mode: (%04OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \
72 1.1 atatat "Device: %Hd,%Ld Inode: %i Links: %l%n" \
73 1.1 atatat "Access: %Sa%n" \
74 1.1 atatat "Modify: %Sm%n" \
75 1.1 atatat "Change: %Sc"
76 1.1 atatat
77 1.1 atatat #define TIME_FORMAT "%b %e %T %Y"
78 1.1 atatat
79 1.2 atatat #define FLAG_POUND 0x01
80 1.2 atatat #define FLAG_SPACE 0x02
81 1.2 atatat #define FLAG_PLUS 0x04
82 1.2 atatat #define FLAG_ZERO 0x08
83 1.2 atatat #define FLAG_MINUS 0x10
84 1.1 atatat
85 1.1 atatat /*
86 1.2 atatat * These format characters must all be unique, except the magic one.
87 1.1 atatat */
88 1.2 atatat #define FMT_MAGIC '%'
89 1.2 atatat #define FMT_DOT '.'
90 1.2 atatat
91 1.1 atatat #define SIMPLE_NEWLINE 'n'
92 1.1 atatat #define SIMPLE_TAB 't'
93 1.1 atatat #define SIMPLE_PERCENT '%'
94 1.2 atatat #define SIMPLE_NUMBER '@'
95 1.2 atatat
96 1.2 atatat #define FMT_POUND '#'
97 1.2 atatat #define FMT_SPACE ' '
98 1.2 atatat #define FMT_PLUS '+'
99 1.2 atatat #define FMT_ZERO '0'
100 1.2 atatat #define FMT_MINUS '-'
101 1.1 atatat
102 1.1 atatat #define FMT_DECIMAL 'D'
103 1.1 atatat #define FMT_OCTAL 'O'
104 1.1 atatat #define FMT_UNSIGNED 'U'
105 1.1 atatat #define FMT_HEX 'X'
106 1.1 atatat #define FMT_FLOAT 'F'
107 1.1 atatat #define FMT_STRING 'S'
108 1.1 atatat
109 1.1 atatat #define HIGH_PIECE 'H'
110 1.1 atatat #define MIDDLE_PIECE 'M'
111 1.1 atatat #define LOW_PIECE 'L'
112 1.1 atatat
113 1.1 atatat #define SHOW_st_dev 'd'
114 1.1 atatat #define SHOW_st_ino 'i'
115 1.1 atatat #define SHOW_st_mode 'p'
116 1.1 atatat #define SHOW_st_nlink 'l'
117 1.1 atatat #define SHOW_st_uid 'u'
118 1.1 atatat #define SHOW_st_gid 'g'
119 1.1 atatat #define SHOW_st_rdev 'r'
120 1.1 atatat #define SHOW_st_atime 'a'
121 1.1 atatat #define SHOW_st_mtime 'm'
122 1.1 atatat #define SHOW_st_ctime 'c'
123 1.1 atatat #define SHOW_st_size 'z'
124 1.1 atatat #define SHOW_st_blocks 'b'
125 1.1 atatat #define SHOW_st_blksize 'k'
126 1.1 atatat #define SHOW_st_flags 'f'
127 1.1 atatat #define SHOW_st_gen 'v'
128 1.1 atatat #define SHOW_symlink 'Y'
129 1.1 atatat #define SHOW_filetype 'T'
130 1.1 atatat #define SHOW_filename 'N'
131 1.1 atatat #define SHOW_sizerdev 'Z'
132 1.1 atatat
133 1.4 atatat void usage(const char *);
134 1.1 atatat void output(const struct stat *, const char *,
135 1.4 atatat const char *, int, int, int);
136 1.1 atatat int format1(const struct stat *, /* stat info */
137 1.1 atatat const char *, /* the file name */
138 1.1 atatat const char *, int, /* the format string itself */
139 1.1 atatat char *, size_t, /* a place to put the output */
140 1.1 atatat int, int, int, int, /* the parsed format */
141 1.1 atatat int, int);
142 1.1 atatat
143 1.1 atatat char *timefmt;
144 1.4 atatat int linkfail;
145 1.1 atatat
146 1.4 atatat #define addchar(s, c, nl) \
147 1.1 atatat do { \
148 1.4 atatat (void)fputc((c), (s)); \
149 1.4 atatat (*nl) = ((c) == '\n'); \
150 1.1 atatat } while (0/*CONSTCOND*/)
151 1.1 atatat
152 1.1 atatat int
153 1.1 atatat main(int argc, char *argv[])
154 1.1 atatat {
155 1.1 atatat struct stat st;
156 1.4 atatat int ch, rc, errs, am_readlink;
157 1.4 atatat int lsF, fmtchar, usestat, fn, nonl, quiet;
158 1.4 atatat char *statfmt, *options, *synopsis;
159 1.1 atatat
160 1.4 atatat am_readlink = 0;
161 1.1 atatat lsF = 0;
162 1.1 atatat fmtchar = '\0';
163 1.1 atatat usestat = 0;
164 1.1 atatat nonl = 0;
165 1.4 atatat quiet = 0;
166 1.4 atatat linkfail = 0;
167 1.1 atatat statfmt = NULL;
168 1.1 atatat timefmt = NULL;
169 1.1 atatat
170 1.4 atatat if (strcmp(getprogname(), "readlink") == 0) {
171 1.4 atatat am_readlink = 1;
172 1.4 atatat options = "n";
173 1.4 atatat synopsis = "[-n] [file ...]";
174 1.4 atatat statfmt = "%Y";
175 1.4 atatat fmtchar = 'f';
176 1.4 atatat quiet = 1;
177 1.4 atatat } else {
178 1.4 atatat options = "f:FlLnqrst:x";
179 1.4 atatat synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]";
180 1.4 atatat }
181 1.4 atatat
182 1.4 atatat while ((ch = getopt(argc, argv, options)) != -1)
183 1.1 atatat switch (ch) {
184 1.1 atatat case 'F':
185 1.1 atatat lsF = 1;
186 1.1 atatat break;
187 1.1 atatat case 'L':
188 1.1 atatat usestat = 1;
189 1.1 atatat break;
190 1.1 atatat case 'n':
191 1.1 atatat nonl = 1;
192 1.1 atatat break;
193 1.4 atatat case 'q':
194 1.4 atatat quiet = 1;
195 1.4 atatat break;
196 1.1 atatat case 'f':
197 1.1 atatat statfmt = optarg;
198 1.1 atatat /* FALLTHROUGH */
199 1.1 atatat case 'l':
200 1.1 atatat case 'r':
201 1.1 atatat case 's':
202 1.1 atatat case 'x':
203 1.1 atatat if (fmtchar != 0)
204 1.1 atatat errx(1, "can't use format '%c' with '%c'",
205 1.1 atatat fmtchar, ch);
206 1.1 atatat fmtchar = ch;
207 1.1 atatat break;
208 1.1 atatat case 't':
209 1.1 atatat timefmt = optarg;
210 1.1 atatat break;
211 1.1 atatat default:
212 1.4 atatat usage(synopsis);
213 1.1 atatat }
214 1.1 atatat
215 1.1 atatat argc -= optind;
216 1.1 atatat argv += optind;
217 1.2 atatat fn = 1;
218 1.1 atatat
219 1.1 atatat if (fmtchar == '\0') {
220 1.1 atatat if (lsF)
221 1.1 atatat fmtchar = 'l';
222 1.1 atatat else {
223 1.1 atatat fmtchar = 'f';
224 1.1 atatat statfmt = DEF_FORMAT;
225 1.1 atatat }
226 1.1 atatat }
227 1.1 atatat
228 1.1 atatat if (lsF && fmtchar != 'l')
229 1.1 atatat errx(1, "can't use format '%c' with -F", fmtchar);
230 1.1 atatat
231 1.1 atatat switch (fmtchar) {
232 1.1 atatat case 'f':
233 1.1 atatat /* statfmt already set */
234 1.1 atatat break;
235 1.1 atatat case 'l':
236 1.1 atatat statfmt = lsF ? LSF_FORMAT : LS_FORMAT;
237 1.1 atatat break;
238 1.1 atatat case 'r':
239 1.1 atatat statfmt = RAW_FORMAT;
240 1.1 atatat break;
241 1.1 atatat case 's':
242 1.1 atatat statfmt = SHELL_FORMAT;
243 1.1 atatat break;
244 1.1 atatat case 'x':
245 1.1 atatat statfmt = LINUX_FORMAT;
246 1.1 atatat if (timefmt == NULL)
247 1.1 atatat timefmt = "%c";
248 1.1 atatat break;
249 1.1 atatat default:
250 1.4 atatat usage(synopsis);
251 1.1 atatat /*NOTREACHED*/
252 1.1 atatat }
253 1.1 atatat
254 1.1 atatat if (timefmt == NULL)
255 1.1 atatat timefmt = TIME_FORMAT;
256 1.1 atatat
257 1.1 atatat errs = 0;
258 1.1 atatat do {
259 1.1 atatat if (argc == 0)
260 1.1 atatat rc = fstat(STDIN_FILENO, &st);
261 1.1 atatat else if (usestat)
262 1.1 atatat rc = stat(argv[0], &st);
263 1.1 atatat else
264 1.1 atatat rc = lstat(argv[0], &st);
265 1.1 atatat
266 1.1 atatat if (rc == -1) {
267 1.1 atatat errs = 1;
268 1.4 atatat linkfail = 1;
269 1.4 atatat if (!quiet)
270 1.4 atatat warn("%s: stat",
271 1.4 atatat argc == 0 ? "(stdin)" : argv[0]);
272 1.1 atatat }
273 1.1 atatat else
274 1.4 atatat output(&st, argv[0], statfmt, fn, nonl, quiet);
275 1.1 atatat
276 1.1 atatat argv++;
277 1.1 atatat argc--;
278 1.2 atatat fn++;
279 1.1 atatat } while (argc > 0);
280 1.1 atatat
281 1.4 atatat return (am_readlink ? linkfail : errs);
282 1.1 atatat }
283 1.1 atatat
284 1.1 atatat void
285 1.4 atatat usage(const char *synopsis)
286 1.1 atatat {
287 1.1 atatat
288 1.4 atatat (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis);
289 1.1 atatat exit(1);
290 1.1 atatat }
291 1.1 atatat
292 1.1 atatat /*
293 1.1 atatat * Parses a format string.
294 1.1 atatat */
295 1.1 atatat void
296 1.1 atatat output(const struct stat *st, const char *file,
297 1.4 atatat const char *statfmt, int fn, int nonl, int quiet)
298 1.1 atatat {
299 1.1 atatat int flags, size, prec, ofmt, hilo, what;
300 1.4 atatat char buf[MAXPATHLEN];
301 1.1 atatat const char *subfmt;
302 1.1 atatat int nl, t, i;
303 1.1 atatat
304 1.4 atatat nl = 1;
305 1.1 atatat while (*statfmt != '\0') {
306 1.1 atatat
307 1.1 atatat /*
308 1.1 atatat * Non-format characters go straight out.
309 1.1 atatat */
310 1.2 atatat if (*statfmt != FMT_MAGIC) {
311 1.4 atatat addchar(stdout, *statfmt, &nl);
312 1.1 atatat statfmt++;
313 1.1 atatat continue;
314 1.1 atatat }
315 1.1 atatat
316 1.1 atatat /*
317 1.1 atatat * The current format "substring" starts here,
318 1.2 atatat * and then we skip the magic.
319 1.1 atatat */
320 1.1 atatat subfmt = statfmt;
321 1.1 atatat statfmt++;
322 1.1 atatat
323 1.1 atatat /*
324 1.1 atatat * Some simple one-character "formats".
325 1.1 atatat */
326 1.1 atatat switch (*statfmt) {
327 1.1 atatat case SIMPLE_NEWLINE:
328 1.4 atatat addchar(stdout, '\n', &nl);
329 1.1 atatat statfmt++;
330 1.1 atatat continue;
331 1.1 atatat case SIMPLE_TAB:
332 1.4 atatat addchar(stdout, '\t', &nl);
333 1.1 atatat statfmt++;
334 1.1 atatat continue;
335 1.1 atatat case SIMPLE_PERCENT:
336 1.4 atatat addchar(stdout, '%', &nl);
337 1.1 atatat statfmt++;
338 1.1 atatat continue;
339 1.2 atatat case SIMPLE_NUMBER: {
340 1.2 atatat char num[12], *p;
341 1.2 atatat
342 1.2 atatat snprintf(num, sizeof(num), "%d", fn);
343 1.2 atatat for (p = &num[0]; *p; p++)
344 1.4 atatat addchar(stdout, *p, &nl);
345 1.2 atatat statfmt++;
346 1.2 atatat continue;
347 1.2 atatat }
348 1.1 atatat }
349 1.1 atatat
350 1.1 atatat /*
351 1.1 atatat * This must be an actual format string. Format strings are
352 1.1 atatat * similar to printf(3) formats up to a point, and are of
353 1.1 atatat * the form:
354 1.1 atatat *
355 1.1 atatat * % required start of format
356 1.1 atatat * [-# +0] opt. format characters
357 1.1 atatat * size opt. field width
358 1.1 atatat * . opt. decimal separator, followed by
359 1.1 atatat * prec opt. precision
360 1.1 atatat * fmt opt. output specifier (string, numeric, etc.)
361 1.1 atatat * sub opt. sub field specifier (high, middle, low)
362 1.1 atatat * datum required field specifier (size, mode, etc)
363 1.1 atatat *
364 1.1 atatat * Only the % and the datum selector are required. All data
365 1.1 atatat * have reasonable default output forms. The "sub" specifier
366 1.1 atatat * only applies to certain data (mode, dev, rdev, filetype).
367 1.1 atatat * The symlink output defaults to STRING, yet will only emit
368 1.1 atatat * the leading " -> " if STRING is explicitly specified. The
369 1.1 atatat * sizerdev datum will generate rdev output for character or
370 1.1 atatat * block devices, and size output for all others.
371 1.1 atatat */
372 1.1 atatat flags = 0;
373 1.1 atatat do {
374 1.2 atatat if (*statfmt == FMT_POUND)
375 1.2 atatat flags |= FLAG_POUND;
376 1.2 atatat else if (*statfmt == FMT_SPACE)
377 1.2 atatat flags |= FLAG_SPACE;
378 1.2 atatat else if (*statfmt == FMT_PLUS)
379 1.2 atatat flags |= FLAG_PLUS;
380 1.2 atatat else if (*statfmt == FMT_ZERO)
381 1.2 atatat flags |= FLAG_ZERO;
382 1.2 atatat else if (*statfmt == FMT_MINUS)
383 1.2 atatat flags |= FLAG_MINUS;
384 1.1 atatat else
385 1.1 atatat break;
386 1.1 atatat statfmt++;
387 1.1 atatat } while (1/*CONSTCOND*/);
388 1.1 atatat
389 1.1 atatat size = -1;
390 1.1 atatat if (isdigit((unsigned)*statfmt)) {
391 1.1 atatat size = 0;
392 1.1 atatat while (isdigit((unsigned)*statfmt)) {
393 1.1 atatat size = (size * 10) + (*statfmt - '0');
394 1.1 atatat statfmt++;
395 1.1 atatat if (size < 0)
396 1.1 atatat goto badfmt;
397 1.1 atatat }
398 1.1 atatat }
399 1.1 atatat
400 1.1 atatat prec = -1;
401 1.2 atatat if (*statfmt == FMT_DOT) {
402 1.1 atatat statfmt++;
403 1.1 atatat
404 1.1 atatat prec = 0;
405 1.1 atatat while (isdigit((unsigned)*statfmt)) {
406 1.1 atatat prec = (prec * 10) + (*statfmt - '0');
407 1.1 atatat statfmt++;
408 1.1 atatat if (prec < 0)
409 1.1 atatat goto badfmt;
410 1.1 atatat }
411 1.1 atatat }
412 1.1 atatat
413 1.1 atatat #define fmtcase(x, y) case (y): (x) = (y); statfmt++; break
414 1.1 atatat switch (*statfmt) {
415 1.1 atatat fmtcase(ofmt, FMT_DECIMAL);
416 1.1 atatat fmtcase(ofmt, FMT_OCTAL);
417 1.1 atatat fmtcase(ofmt, FMT_UNSIGNED);
418 1.1 atatat fmtcase(ofmt, FMT_HEX);
419 1.1 atatat fmtcase(ofmt, FMT_FLOAT);
420 1.1 atatat fmtcase(ofmt, FMT_STRING);
421 1.1 atatat default:
422 1.1 atatat ofmt = 0;
423 1.1 atatat break;
424 1.1 atatat }
425 1.1 atatat
426 1.1 atatat switch (*statfmt) {
427 1.1 atatat fmtcase(hilo, HIGH_PIECE);
428 1.1 atatat fmtcase(hilo, MIDDLE_PIECE);
429 1.1 atatat fmtcase(hilo, LOW_PIECE);
430 1.1 atatat default:
431 1.1 atatat hilo = 0;
432 1.1 atatat break;
433 1.1 atatat }
434 1.1 atatat
435 1.1 atatat switch (*statfmt) {
436 1.1 atatat fmtcase(what, SHOW_st_dev);
437 1.1 atatat fmtcase(what, SHOW_st_ino);
438 1.1 atatat fmtcase(what, SHOW_st_mode);
439 1.1 atatat fmtcase(what, SHOW_st_nlink);
440 1.1 atatat fmtcase(what, SHOW_st_uid);
441 1.1 atatat fmtcase(what, SHOW_st_gid);
442 1.1 atatat fmtcase(what, SHOW_st_rdev);
443 1.1 atatat fmtcase(what, SHOW_st_atime);
444 1.1 atatat fmtcase(what, SHOW_st_mtime);
445 1.1 atatat fmtcase(what, SHOW_st_ctime);
446 1.1 atatat fmtcase(what, SHOW_st_size);
447 1.1 atatat fmtcase(what, SHOW_st_blocks);
448 1.1 atatat fmtcase(what, SHOW_st_blksize);
449 1.1 atatat fmtcase(what, SHOW_st_flags);
450 1.1 atatat fmtcase(what, SHOW_st_gen);
451 1.1 atatat fmtcase(what, SHOW_symlink);
452 1.1 atatat fmtcase(what, SHOW_filetype);
453 1.1 atatat fmtcase(what, SHOW_filename);
454 1.1 atatat fmtcase(what, SHOW_sizerdev);
455 1.1 atatat default:
456 1.1 atatat goto badfmt;
457 1.1 atatat }
458 1.1 atatat #undef fmtcase
459 1.1 atatat
460 1.1 atatat t = format1(st,
461 1.1 atatat file,
462 1.1 atatat subfmt, statfmt - subfmt,
463 1.4 atatat buf, sizeof(buf),
464 1.1 atatat flags, size, prec, ofmt, hilo, what);
465 1.1 atatat
466 1.4 atatat for (i = 0; i < t && i < sizeof(buf); i++)
467 1.4 atatat addchar(stdout, buf[i], &nl);
468 1.1 atatat
469 1.1 atatat continue;
470 1.1 atatat
471 1.1 atatat badfmt:
472 1.1 atatat errx(1, "%.*s: bad format",
473 1.1 atatat (int)(statfmt - subfmt + 1), subfmt);
474 1.1 atatat }
475 1.1 atatat
476 1.1 atatat if (!nl && !nonl)
477 1.4 atatat (void)fputc('\n', stdout);
478 1.4 atatat (void)fflush(stdout);
479 1.1 atatat }
480 1.1 atatat
481 1.1 atatat /*
482 1.1 atatat * Arranges output according to a single parsed format substring.
483 1.1 atatat */
484 1.1 atatat int
485 1.1 atatat format1(const struct stat *st,
486 1.1 atatat const char *file,
487 1.1 atatat const char *fmt, int flen,
488 1.1 atatat char *buf, size_t blen,
489 1.1 atatat int flags, int size, int prec, int ofmt,
490 1.1 atatat int hilo, int what)
491 1.1 atatat {
492 1.1 atatat u_int64_t data;
493 1.1 atatat char *sdata, lfmt[24], tmp[20];
494 1.1 atatat char smode[12], sid[12], path[MAXPATHLEN + 4];
495 1.1 atatat struct passwd *pw;
496 1.1 atatat struct group *gr;
497 1.1 atatat const struct timespec *tsp;
498 1.1 atatat struct timespec ts;
499 1.1 atatat struct tm *tm;
500 1.1 atatat int l, small, formats;
501 1.1 atatat
502 1.1 atatat tsp = NULL;
503 1.1 atatat formats = 0;
504 1.1 atatat small = 0;
505 1.1 atatat
506 1.1 atatat /*
507 1.1 atatat * First, pick out the data and tweak it based on hilo or
508 1.1 atatat * specified output format (symlink output only).
509 1.1 atatat */
510 1.1 atatat switch (what) {
511 1.1 atatat case SHOW_st_dev:
512 1.1 atatat case SHOW_st_rdev:
513 1.1 atatat small = (sizeof(st->st_dev) == 4);
514 1.1 atatat data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev;
515 1.1 atatat sdata = (what == SHOW_st_dev) ?
516 1.1 atatat devname(st->st_dev, S_IFBLK) :
517 1.1 atatat devname(st->st_rdev,
518 1.1 atatat S_ISCHR(st->st_mode) ? S_IFCHR :
519 1.1 atatat S_ISBLK(st->st_mode) ? S_IFBLK :
520 1.1 atatat 0U);
521 1.3 atatat if (sdata == NULL)
522 1.3 atatat sdata = "???";
523 1.1 atatat if (hilo == HIGH_PIECE) {
524 1.1 atatat data = major(data);
525 1.1 atatat hilo = 0;
526 1.1 atatat }
527 1.1 atatat else if (hilo == LOW_PIECE) {
528 1.1 atatat data = minor((unsigned)data);
529 1.1 atatat hilo = 0;
530 1.1 atatat }
531 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX |
532 1.1 atatat FMT_STRING;
533 1.1 atatat if (ofmt == 0)
534 1.1 atatat ofmt = FMT_UNSIGNED;
535 1.1 atatat break;
536 1.1 atatat case SHOW_st_ino:
537 1.1 atatat small = (sizeof(st->st_ino) == 4);
538 1.1 atatat data = st->st_ino;
539 1.1 atatat sdata = NULL;
540 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX;
541 1.1 atatat if (ofmt == 0)
542 1.1 atatat ofmt = FMT_UNSIGNED;
543 1.1 atatat break;
544 1.1 atatat case SHOW_st_mode:
545 1.1 atatat small = (sizeof(st->st_mode) == 4);
546 1.1 atatat data = st->st_mode;
547 1.1 atatat strmode(st->st_mode, smode);
548 1.1 atatat sdata = smode;
549 1.1 atatat l = strlen(sdata);
550 1.1 atatat if (sdata[l - 1] == ' ')
551 1.1 atatat sdata[--l] = '\0';
552 1.1 atatat if (hilo == HIGH_PIECE) {
553 1.1 atatat data >>= 12;
554 1.1 atatat sdata += 1;
555 1.1 atatat sdata[3] = '\0';
556 1.1 atatat hilo = 0;
557 1.1 atatat }
558 1.1 atatat else if (hilo == MIDDLE_PIECE) {
559 1.2 atatat data = (data >> 9) & 07;
560 1.1 atatat sdata += 4;
561 1.1 atatat sdata[3] = '\0';
562 1.1 atatat hilo = 0;
563 1.1 atatat }
564 1.1 atatat else if (hilo == LOW_PIECE) {
565 1.2 atatat data &= 0777;
566 1.1 atatat sdata += 7;
567 1.1 atatat sdata[3] = '\0';
568 1.1 atatat hilo = 0;
569 1.1 atatat }
570 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX |
571 1.1 atatat FMT_STRING;
572 1.1 atatat if (ofmt == 0)
573 1.1 atatat ofmt = FMT_OCTAL;
574 1.1 atatat break;
575 1.1 atatat case SHOW_st_nlink:
576 1.1 atatat small = (sizeof(st->st_dev) == 4);
577 1.1 atatat data = st->st_nlink;
578 1.1 atatat sdata = NULL;
579 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX;
580 1.1 atatat if (ofmt == 0)
581 1.1 atatat ofmt = FMT_UNSIGNED;
582 1.1 atatat break;
583 1.1 atatat case SHOW_st_uid:
584 1.1 atatat small = (sizeof(st->st_uid) == 4);
585 1.1 atatat data = st->st_uid;
586 1.1 atatat if ((pw = getpwuid(st->st_uid)) != NULL)
587 1.1 atatat sdata = pw->pw_name;
588 1.1 atatat else {
589 1.1 atatat snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid);
590 1.1 atatat sdata = sid;
591 1.1 atatat }
592 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX |
593 1.1 atatat FMT_STRING;
594 1.1 atatat if (ofmt == 0)
595 1.1 atatat ofmt = FMT_UNSIGNED;
596 1.1 atatat break;
597 1.1 atatat case SHOW_st_gid:
598 1.1 atatat small = (sizeof(st->st_gid) == 4);
599 1.1 atatat data = st->st_gid;
600 1.1 atatat if ((gr = getgrgid(st->st_gid)) != NULL)
601 1.1 atatat sdata = gr->gr_name;
602 1.1 atatat else {
603 1.1 atatat snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid);
604 1.1 atatat sdata = sid;
605 1.1 atatat }
606 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX |
607 1.1 atatat FMT_STRING;
608 1.1 atatat if (ofmt == 0)
609 1.1 atatat ofmt = FMT_UNSIGNED;
610 1.1 atatat break;
611 1.1 atatat case SHOW_st_atime:
612 1.1 atatat tsp = &st->st_atimespec;
613 1.1 atatat /* FALLTHROUGH */
614 1.1 atatat case SHOW_st_mtime:
615 1.1 atatat if (tsp == NULL)
616 1.1 atatat tsp = &st->st_mtimespec;
617 1.1 atatat /* FALLTHROUGH */
618 1.1 atatat case SHOW_st_ctime:
619 1.1 atatat if (tsp == NULL)
620 1.1 atatat tsp = &st->st_ctimespec;
621 1.1 atatat ts = *tsp; /* copy so we can muck with it */
622 1.1 atatat small = (sizeof(ts.tv_sec) == 4);
623 1.1 atatat data = ts.tv_sec;
624 1.1 atatat small = 1;
625 1.1 atatat tm = localtime(&ts.tv_sec);
626 1.1 atatat (void)strftime(path, sizeof(path), timefmt, tm);
627 1.1 atatat sdata = path;
628 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX |
629 1.1 atatat FMT_FLOAT | FMT_STRING;
630 1.1 atatat if (ofmt == 0)
631 1.1 atatat ofmt = FMT_DECIMAL;
632 1.1 atatat break;
633 1.1 atatat case SHOW_st_size:
634 1.1 atatat small = (sizeof(st->st_size) == 4);
635 1.1 atatat data = st->st_size;
636 1.1 atatat sdata = NULL;
637 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX;
638 1.1 atatat if (ofmt == 0)
639 1.1 atatat ofmt = FMT_UNSIGNED;
640 1.1 atatat break;
641 1.1 atatat case SHOW_st_blocks:
642 1.1 atatat small = (sizeof(st->st_blocks) == 4);
643 1.1 atatat data = st->st_blocks;
644 1.1 atatat sdata = NULL;
645 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX;
646 1.1 atatat if (ofmt == 0)
647 1.1 atatat ofmt = FMT_UNSIGNED;
648 1.1 atatat break;
649 1.1 atatat case SHOW_st_blksize:
650 1.1 atatat small = (sizeof(st->st_blksize) == 4);
651 1.1 atatat data = st->st_blksize;
652 1.1 atatat sdata = NULL;
653 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX;
654 1.1 atatat if (ofmt == 0)
655 1.1 atatat ofmt = FMT_UNSIGNED;
656 1.1 atatat break;
657 1.1 atatat case SHOW_st_flags:
658 1.1 atatat small = (sizeof(st->st_flags) == 4);
659 1.1 atatat data = st->st_flags;
660 1.1 atatat sdata = NULL;
661 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX;
662 1.1 atatat if (ofmt == 0)
663 1.1 atatat ofmt = FMT_UNSIGNED;
664 1.1 atatat break;
665 1.1 atatat case SHOW_st_gen:
666 1.1 atatat small = (sizeof(st->st_gen) == 4);
667 1.1 atatat data = st->st_gen;
668 1.1 atatat sdata = NULL;
669 1.1 atatat formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX;
670 1.1 atatat if (ofmt == 0)
671 1.1 atatat ofmt = FMT_UNSIGNED;
672 1.1 atatat break;
673 1.1 atatat case SHOW_symlink:
674 1.1 atatat small = 0;
675 1.1 atatat data = 0;
676 1.1 atatat if (S_ISLNK(st->st_mode)) {
677 1.1 atatat snprintf(path, sizeof(path), " -> ");
678 1.1 atatat l = readlink(file, path + 4, sizeof(path) - 4);
679 1.1 atatat if (l == -1) {
680 1.4 atatat linkfail = 1;
681 1.1 atatat l = 0;
682 1.1 atatat path[0] = '\0';
683 1.1 atatat }
684 1.1 atatat path[l + 4] = '\0';
685 1.1 atatat sdata = path + (ofmt == FMT_STRING ? 0 : 4);
686 1.1 atatat }
687 1.4 atatat else {
688 1.4 atatat linkfail = 1;
689 1.1 atatat sdata = "";
690 1.4 atatat }
691 1.1 atatat formats = FMT_STRING;
692 1.1 atatat if (ofmt == 0)
693 1.1 atatat ofmt = FMT_STRING;
694 1.1 atatat break;
695 1.1 atatat case SHOW_filetype:
696 1.1 atatat small = 0;
697 1.1 atatat data = 0;
698 1.1 atatat sdata = smode;
699 1.1 atatat sdata[0] = '\0';
700 1.1 atatat if (hilo == 0 || hilo == LOW_PIECE) {
701 1.1 atatat switch (st->st_mode & S_IFMT) {
702 1.1 atatat case S_IFIFO: (void)strcat(sdata, "|"); break;
703 1.1 atatat case S_IFDIR: (void)strcat(sdata, "/"); break;
704 1.1 atatat case S_IFREG:
705 1.1 atatat if (st->st_mode &
706 1.1 atatat (S_IXUSR | S_IXGRP | S_IXOTH))
707 1.1 atatat (void)strcat(sdata, "*");
708 1.1 atatat break;
709 1.1 atatat case S_IFLNK: (void)strcat(sdata, "@"); break;
710 1.1 atatat case S_IFSOCK: (void)strcat(sdata, "="); break;
711 1.1 atatat case S_IFWHT: (void)strcat(sdata, "%"); break;
712 1.1 atatat }
713 1.1 atatat hilo = 0;
714 1.1 atatat }
715 1.1 atatat else if (hilo == HIGH_PIECE) {
716 1.1 atatat switch (st->st_mode & S_IFMT) {
717 1.1 atatat case S_IFIFO: sdata = "Fifo File"; break;
718 1.1 atatat case S_IFCHR: sdata = "Character Device"; break;
719 1.1 atatat case S_IFDIR: sdata = "Directory"; break;
720 1.1 atatat case S_IFBLK: sdata = "Block Device"; break;
721 1.1 atatat case S_IFREG: sdata = "Regular File"; break;
722 1.1 atatat case S_IFLNK: sdata = "Symbolic Link"; break;
723 1.1 atatat case S_IFSOCK: sdata = "Socket"; break;
724 1.1 atatat case S_IFWHT: sdata = "Whiteout File"; break;
725 1.1 atatat default: sdata = "???"; break;
726 1.1 atatat }
727 1.1 atatat hilo = 0;
728 1.1 atatat }
729 1.1 atatat formats = FMT_STRING;
730 1.1 atatat if (ofmt == 0)
731 1.1 atatat ofmt = FMT_STRING;
732 1.1 atatat break;
733 1.1 atatat case SHOW_filename:
734 1.1 atatat small = 0;
735 1.1 atatat data = 0;
736 1.1 atatat if (file == NULL)
737 1.1 atatat (void)strncpy(path, "(stdin)", sizeof(path));
738 1.1 atatat else
739 1.1 atatat (void)strncpy(path, file, sizeof(path));
740 1.1 atatat sdata = path;
741 1.1 atatat formats = FMT_STRING;
742 1.1 atatat if (ofmt == 0)
743 1.1 atatat ofmt = FMT_STRING;
744 1.1 atatat break;
745 1.1 atatat case SHOW_sizerdev:
746 1.1 atatat if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
747 1.1 atatat char majdev[20], mindev[20];
748 1.1 atatat int l1, l2;
749 1.1 atatat
750 1.1 atatat l1 = format1(st,
751 1.1 atatat file,
752 1.1 atatat fmt, flen,
753 1.1 atatat majdev, sizeof(majdev),
754 1.1 atatat flags, size, prec,
755 1.1 atatat ofmt, HIGH_PIECE, SHOW_st_rdev);
756 1.1 atatat l2 = format1(st,
757 1.1 atatat file,
758 1.1 atatat fmt, flen,
759 1.1 atatat mindev, sizeof(mindev),
760 1.1 atatat flags, size, prec,
761 1.1 atatat ofmt, LOW_PIECE, SHOW_st_rdev);
762 1.1 atatat return (snprintf(buf, blen, "%.*s,%.*s",
763 1.1 atatat l1, majdev, l2, mindev));
764 1.1 atatat }
765 1.1 atatat else {
766 1.1 atatat return (format1(st,
767 1.1 atatat file,
768 1.1 atatat fmt, flen,
769 1.1 atatat buf, blen,
770 1.1 atatat flags, size, prec,
771 1.1 atatat ofmt, 0, SHOW_st_size));
772 1.1 atatat }
773 1.1 atatat /*NOTREACHED*/
774 1.1 atatat default:
775 1.1 atatat errx(1, "%.*s: bad format", (int)flen, fmt);
776 1.1 atatat }
777 1.1 atatat
778 1.1 atatat /*
779 1.1 atatat * If a subdatum was specified but not supported, or an output
780 1.1 atatat * format was selected that is not supported, that's an error.
781 1.1 atatat */
782 1.1 atatat if (hilo != 0 || (ofmt & formats) == 0)
783 1.1 atatat errx(1, "%.*s: bad format", (int)flen, fmt);
784 1.1 atatat
785 1.1 atatat /*
786 1.1 atatat * Assemble the format string for passing to printf(3).
787 1.1 atatat */
788 1.1 atatat lfmt[0] = '\0';
789 1.1 atatat (void)strcat(lfmt, "%");
790 1.2 atatat if (flags & FLAG_POUND)
791 1.1 atatat (void)strcat(lfmt, "#");
792 1.2 atatat if (flags & FLAG_SPACE)
793 1.1 atatat (void)strcat(lfmt, " ");
794 1.2 atatat if (flags & FLAG_PLUS)
795 1.1 atatat (void)strcat(lfmt, "+");
796 1.2 atatat if (flags & FLAG_MINUS)
797 1.1 atatat (void)strcat(lfmt, "-");
798 1.2 atatat if (flags & FLAG_ZERO)
799 1.1 atatat (void)strcat(lfmt, "0");
800 1.1 atatat
801 1.1 atatat /*
802 1.1 atatat * Only the timespecs support the FLOAT output format, and that
803 1.1 atatat * requires work that differs from the other formats.
804 1.1 atatat */
805 1.1 atatat if (ofmt == FMT_FLOAT) {
806 1.1 atatat /*
807 1.1 atatat * Nothing after the decimal point, so just print seconds.
808 1.1 atatat */
809 1.1 atatat if (prec == 0) {
810 1.1 atatat if (size != -1) {
811 1.1 atatat (void)snprintf(tmp, sizeof(tmp), "%d", size);
812 1.1 atatat (void)strcat(lfmt, tmp);
813 1.1 atatat }
814 1.1 atatat (void)strcat(lfmt, "d");
815 1.1 atatat return (snprintf(buf, blen, lfmt, ts.tv_sec));
816 1.1 atatat }
817 1.1 atatat
818 1.1 atatat /*
819 1.1 atatat * Unspecified precision gets all the precision we have:
820 1.1 atatat * 9 digits.
821 1.1 atatat */
822 1.1 atatat if (prec == -1)
823 1.1 atatat prec = 9;
824 1.1 atatat
825 1.1 atatat /*
826 1.1 atatat * Adjust the size for the decimal point and the digits
827 1.1 atatat * that will follow.
828 1.1 atatat */
829 1.1 atatat size -= prec + 1;
830 1.1 atatat
831 1.1 atatat /*
832 1.1 atatat * Any leftover size that's legitimate will be used.
833 1.1 atatat */
834 1.1 atatat if (size > 0) {
835 1.1 atatat (void)snprintf(tmp, sizeof(tmp), "%d", size);
836 1.1 atatat (void)strcat(lfmt, tmp);
837 1.1 atatat }
838 1.1 atatat (void)strcat(lfmt, "d");
839 1.1 atatat
840 1.1 atatat /*
841 1.1 atatat * The stuff after the decimal point always needs zero
842 1.1 atatat * filling.
843 1.1 atatat */
844 1.1 atatat (void)strcat(lfmt, ".%0");
845 1.1 atatat
846 1.1 atatat /*
847 1.1 atatat * We can "print" at most nine digits of precision. The
848 1.1 atatat * rest we will pad on at the end.
849 1.1 atatat */
850 1.1 atatat (void)snprintf(tmp, sizeof(tmp), "%dd", prec > 9 ? 9 : prec);
851 1.1 atatat (void)strcat(lfmt, tmp);
852 1.1 atatat
853 1.1 atatat /*
854 1.1 atatat * For precision of less that nine digits, trim off the
855 1.1 atatat * less significant figures.
856 1.1 atatat */
857 1.1 atatat for (; prec < 9; prec++)
858 1.1 atatat ts.tv_nsec /= 10;
859 1.1 atatat
860 1.1 atatat /*
861 1.1 atatat * Use the format, and then tack on any zeroes that
862 1.1 atatat * might be required to make up the requested precision.
863 1.1 atatat */
864 1.1 atatat l = snprintf(buf, blen, lfmt, ts.tv_sec, ts.tv_nsec);
865 1.1 atatat for (; prec > 9 && l < blen; prec--, l++)
866 1.1 atatat (void)strcat(buf, "0");
867 1.1 atatat return (l);
868 1.1 atatat }
869 1.1 atatat
870 1.1 atatat /*
871 1.1 atatat * Add on size and precision, if specified, to the format.
872 1.1 atatat */
873 1.1 atatat if (size != -1) {
874 1.1 atatat (void)snprintf(tmp, sizeof(tmp), "%d", size);
875 1.1 atatat (void)strcat(lfmt, tmp);
876 1.1 atatat }
877 1.1 atatat if (prec != -1) {
878 1.1 atatat (void)snprintf(tmp, sizeof(tmp), ".%d", prec);
879 1.1 atatat (void)strcat(lfmt, tmp);
880 1.1 atatat }
881 1.1 atatat
882 1.1 atatat /*
883 1.1 atatat * String output uses the temporary sdata.
884 1.1 atatat */
885 1.1 atatat if (ofmt == FMT_STRING) {
886 1.1 atatat if (sdata == NULL)
887 1.1 atatat errx(1, "%.*s: bad format", (int)flen, fmt);
888 1.1 atatat (void)strcat(lfmt, "s");
889 1.1 atatat return (snprintf(buf, blen, lfmt, sdata));
890 1.1 atatat }
891 1.1 atatat
892 1.1 atatat /*
893 1.1 atatat * Ensure that sign extension does not cause bad looking output
894 1.1 atatat * for some forms.
895 1.1 atatat */
896 1.1 atatat if (small && ofmt != FMT_DECIMAL)
897 1.1 atatat data = (u_int32_t)data;
898 1.1 atatat
899 1.1 atatat /*
900 1.1 atatat * The four "numeric" output forms.
901 1.1 atatat */
902 1.1 atatat (void)strcat(lfmt, "ll");
903 1.1 atatat switch (ofmt) {
904 1.1 atatat case FMT_DECIMAL: (void)strcat(lfmt, "d"); break;
905 1.1 atatat case FMT_OCTAL: (void)strcat(lfmt, "o"); break;
906 1.1 atatat case FMT_UNSIGNED: (void)strcat(lfmt, "u"); break;
907 1.1 atatat case FMT_HEX: (void)strcat(lfmt, "x"); break;
908 1.1 atatat }
909 1.1 atatat
910 1.1 atatat return (snprintf(buf, blen, lfmt, data));
911 1.1 atatat }
912