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