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