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