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