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