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