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