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