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