]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/stat/stat.c
Merge bmake-20220330
[FreeBSD/FreeBSD.git] / usr.bin / stat / stat.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 #if 0
34 #ifndef lint
35 __RCSID("$NetBSD: stat.c,v 1.33 2011/01/15 22:54:10 njoly Exp $"
36 "$OpenBSD: stat.c,v 1.14 2009/06/24 09:44:25 sobrado Exp $");
37 #endif
38 #endif
39
40 __FBSDID("$FreeBSD$");
41
42 #if HAVE_CONFIG_H
43 #include "config.h" 
44 #else  /* HAVE_CONFIG_H */
45 #define HAVE_STRUCT_STAT_ST_FLAGS 1
46 #define HAVE_STRUCT_STAT_ST_GEN 1
47 #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
48 #define HAVE_STRUCT_STAT_ST_MTIMENSEC 1
49 #define HAVE_DEVNAME 1
50 #endif /* HAVE_CONFIG_H */
51
52 #include <sys/param.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <sys/mount.h>
56
57 #include <ctype.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <grp.h>
61 #include <limits.h>
62 #include <locale.h>
63 #include <paths.h>
64 #include <pwd.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <time.h>
69 #include <unistd.h>
70
71 #if HAVE_STRUCT_STAT_ST_FLAGS
72 #define DEF_F "%#Xf "
73 #define RAW_F "%f "
74 #define SHELL_F " st_flags=%f"
75 #else /* HAVE_STRUCT_STAT_ST_FLAGS */
76 #define DEF_F
77 #define RAW_F
78 #define SHELL_F
79 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
80
81 #if HAVE_STRUCT_STAT_ST_BIRTHTIME
82 #define DEF_B "\"%SB\" "
83 #define RAW_B "%B "
84 #define SHELL_B "st_birthtime=%B "
85 #else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
86 #define DEF_B
87 #define RAW_B
88 #define SHELL_B
89 #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
90
91 #if HAVE_STRUCT_STAT_ST_ATIM
92 #define st_atimespec st_atim
93 #define st_ctimespec st_ctim
94 #define st_mtimespec st_mtim
95 #endif /* HAVE_STRUCT_STAT_ST_ATIM */
96
97 #define DEF_FORMAT \
98         "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \
99         "%k %b " DEF_F "%N"
100 #define RAW_FORMAT      "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \
101         "%k %b " RAW_F "%N"
102 #define LS_FORMAT       "%Sp %l %Su %Sg %Z %Sm %N%SY"
103 #define LSF_FORMAT      "%Sp %l %Su %Sg %Z %Sm %N%T%SY"
104 #define SHELL_FORMAT \
105         "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \
106         "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \
107         "st_atime=%a st_mtime=%m st_ctime=%c " SHELL_B \
108         "st_blksize=%k st_blocks=%b" SHELL_F
109 #define LINUX_FORMAT \
110         "  File: \"%N\"%n" \
111         "  Size: %-11z  FileType: %HT%n" \
112         "  Mode: (%OMp%03OLp/%.10Sp)         Uid: (%5u/%8Su)  Gid: (%5g/%8Sg)%n" \
113         "Device: %Hd,%Ld   Inode: %i    Links: %l%n" \
114         "Access: %Sa%n" \
115         "Modify: %Sm%n" \
116         "Change: %Sc%n" \
117         " Birth: %SB"
118
119 #define TIME_FORMAT     "%b %e %T %Y"
120
121 #define FLAG_POUND      0x01
122 #define FLAG_SPACE      0x02
123 #define FLAG_PLUS       0x04
124 #define FLAG_ZERO       0x08
125 #define FLAG_MINUS      0x10
126
127 /*
128  * These format characters must all be unique, except the magic one.
129  */
130 #define FMT_MAGIC       '%'
131 #define FMT_DOT         '.'
132
133 #define SIMPLE_NEWLINE  'n'
134 #define SIMPLE_TAB      't'
135 #define SIMPLE_PERCENT  '%'
136 #define SIMPLE_NUMBER   '@'
137
138 #define FMT_POUND       '#'
139 #define FMT_SPACE       ' '
140 #define FMT_PLUS        '+'
141 #define FMT_ZERO        '0'
142 #define FMT_MINUS       '-'
143
144 #define FMT_DECIMAL     'D'
145 #define FMT_OCTAL       'O'
146 #define FMT_UNSIGNED    'U'
147 #define FMT_HEX         'X'
148 #define FMT_FLOAT       'F'
149 #define FMT_STRING      'S'
150
151 #define FMTF_DECIMAL    0x01
152 #define FMTF_OCTAL      0x02
153 #define FMTF_UNSIGNED   0x04
154 #define FMTF_HEX        0x08
155 #define FMTF_FLOAT      0x10
156 #define FMTF_STRING     0x20
157
158 #define HIGH_PIECE      'H'
159 #define MIDDLE_PIECE    'M'
160 #define LOW_PIECE       'L'
161
162 #define SHOW_realpath   'R'
163 #define SHOW_st_dev     'd'
164 #define SHOW_st_ino     'i'
165 #define SHOW_st_mode    'p'
166 #define SHOW_st_nlink   'l'
167 #define SHOW_st_uid     'u'
168 #define SHOW_st_gid     'g'
169 #define SHOW_st_rdev    'r'
170 #define SHOW_st_atime   'a'
171 #define SHOW_st_mtime   'm'
172 #define SHOW_st_ctime   'c'
173 #define SHOW_st_btime   'B'
174 #define SHOW_st_size    'z'
175 #define SHOW_st_blocks  'b'
176 #define SHOW_st_blksize 'k'
177 #define SHOW_st_flags   'f'
178 #define SHOW_st_gen     'v'
179 #define SHOW_symlink    'Y'
180 #define SHOW_filetype   'T'
181 #define SHOW_filename   'N'
182 #define SHOW_sizerdev   'Z'
183
184 void    usage(const char *);
185 void    output(const struct stat *, const char *,
186             const char *, int, int);
187 int     format1(const struct stat *,    /* stat info */
188             const char *,               /* the file name */
189             const char *, int,          /* the format string itself */
190             char *, size_t,             /* a place to put the output */
191             int, int, int, int,         /* the parsed format */
192             int, int);
193 int     hex2byte(const char [2]);
194 #if HAVE_STRUCT_STAT_ST_FLAGS
195 char   *xfflagstostr(unsigned long);
196 #endif
197
198 static const char *timefmt;
199 static int linkfail;
200
201 #define addchar(s, c, nl) \
202         do { \
203                 (void)fputc((c), (s)); \
204                 (*nl) = ((c) == '\n'); \
205         } while (0/*CONSTCOND*/)
206
207 int
208 main(int argc, char *argv[])
209 {
210         struct stat st;
211         int ch, rc, errs, am_readlink;
212         int lsF, fmtchar, usestat, nfs_handle, fn, nonl, quiet;
213         const char *statfmt, *options, *synopsis;
214         char dname[sizeof _PATH_DEV + SPECNAMELEN] = _PATH_DEV;
215         fhandle_t fhnd;
216         const char *file;
217
218         am_readlink = 0;
219         lsF = 0;
220         fmtchar = '\0';
221         usestat = 0;
222         nfs_handle = 0;
223         nonl = 0;
224         quiet = 0;
225         linkfail = 0;
226         statfmt = NULL;
227         timefmt = NULL;
228
229         if (strcmp(getprogname(), "readlink") == 0) {
230                 am_readlink = 1;
231                 options = "fn";
232                 synopsis = "[-fn] [file ...]";
233                 statfmt = "%Y";
234                 fmtchar = 'f';
235                 quiet = 1;
236         } else {
237                 options = "f:FHlLnqrst:x";
238                 synopsis = "[-FLnq] [-f format | -l | -r | -s | -x] "
239                     "[-t timefmt] [file|handle ...]";
240         }
241
242         while ((ch = getopt(argc, argv, options)) != -1)
243                 switch (ch) {
244                 case 'F':
245                         lsF = 1;
246                         break;
247                 case 'H':
248                         nfs_handle = 1;
249                         break;
250                 case 'L':
251                         usestat = 1;
252                         break;
253                 case 'n':
254                         nonl = 1;
255                         break;
256                 case 'q':
257                         quiet = 1;
258                         break;
259                 case 'f':
260                         if (am_readlink) {
261                                 statfmt = "%R";
262                                 break;
263                         }
264                         statfmt = optarg;
265                         /* FALLTHROUGH */
266                 case 'l':
267                 case 'r':
268                 case 's':
269                 case 'x':
270                         if (fmtchar != 0)
271                                 errx(1, "can't use format '%c' with '%c'",
272                                     fmtchar, ch);
273                         fmtchar = ch;
274                         break;
275                 case 't':
276                         timefmt = optarg;
277                         break;
278                 default:
279                         usage(synopsis);
280                 }
281
282         argc -= optind;
283         argv += optind;
284         fn = 1;
285
286         if (fmtchar == '\0') {
287                 if (lsF)
288                         fmtchar = 'l';
289                 else {
290                         fmtchar = 'f';
291                         statfmt = DEF_FORMAT;
292                 }
293         }
294
295         if (lsF && fmtchar != 'l')
296                 errx(1, "can't use format '%c' with -F", fmtchar);
297
298         switch (fmtchar) {
299         case 'f':
300                 /* statfmt already set */
301                 break;
302         case 'l':
303                 statfmt = lsF ? LSF_FORMAT : LS_FORMAT;
304                 break;
305         case 'r':
306                 statfmt = RAW_FORMAT;
307                 break;
308         case 's':
309                 statfmt = SHELL_FORMAT;
310                 break;
311         case 'x':
312                 statfmt = LINUX_FORMAT;
313                 if (timefmt == NULL)
314                         timefmt = "%c";
315                 break;
316         default:
317                 usage(synopsis);
318                 /*NOTREACHED*/
319         }
320
321         if (timefmt == NULL)
322                 timefmt = TIME_FORMAT;
323
324         errs = 0;
325         do {
326                 if (argc == 0) {
327                         if (fdevname_r(STDIN_FILENO, dname +
328                             sizeof _PATH_DEV - 1, SPECNAMELEN) != NULL)
329                                 file = dname;
330                         else
331                                 file = "(stdin)";
332                         rc = fstat(STDIN_FILENO, &st);
333                 } else {
334                         int j;
335
336                         file = argv[0];
337                         if (nfs_handle) {
338                                 rc = 0;
339                                 bzero(&fhnd, sizeof(fhnd));
340                                 j = MIN(2 * sizeof(fhnd), strlen(file));
341                                 if ((j & 1) != 0) {
342                                         rc = -1;
343                                 } else {
344                                         while (j) {
345                                                 rc = hex2byte(&file[j - 2]);
346                                                 if (rc == -1)
347                                                         break;
348                                                 ((char*) &fhnd)[j / 2 - 1] = rc;
349                                                 j -= 2;
350                                         }
351                                 }
352                                 if (rc == -1)
353                                         errno = EINVAL;
354                                 else
355                                         rc = fhstat(&fhnd, &st);
356
357                         } else if (usestat) {
358                                 /*
359                                  * Try stat() and if it fails, fall back to
360                                  * lstat() just in case we're examining a
361                                  * broken symlink.
362                                  */
363                                 if ((rc = stat(file, &st)) == -1 &&
364                                     errno == ENOENT &&
365                                     (rc = lstat(file, &st)) == -1)
366                                         errno = ENOENT;
367                         }
368                         else
369                                 rc = lstat(file, &st);
370                 }
371
372                 if (rc == -1) {
373                         errs = 1;
374                         linkfail = 1;
375                         if (!quiet)
376                                 warn("%s: stat", file);
377                 }
378                 else
379                         output(&st, file, statfmt, fn, nonl);
380
381                 argv++;
382                 argc--;
383                 fn++;
384         } while (argc > 0);
385
386         return (am_readlink ? linkfail : errs);
387 }
388
389 #if HAVE_STRUCT_STAT_ST_FLAGS
390 /*
391  * fflagstostr() wrapper that leaks only once
392  */
393 char *
394 xfflagstostr(unsigned long fflags)
395 {
396         static char *str = NULL;
397
398         if (str != NULL)
399                 free(str);
400
401         str = fflagstostr(fflags);
402         if (str == NULL)
403                 err(1, "fflagstostr");
404         return (str);
405 }
406 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
407
408 void
409 usage(const char *synopsis)
410 {
411
412         (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis);
413         exit(1);
414 }
415
416 /* 
417  * Parses a format string.
418  */
419 void
420 output(const struct stat *st, const char *file,
421     const char *statfmt, int fn, int nonl)
422 {
423         int flags, size, prec, ofmt, hilo, what;
424         char buf[PATH_MAX + 4 + 1];
425         const char *subfmt;
426         int nl, t, i;
427
428         nl = 1;
429         while (*statfmt != '\0') {
430
431                 /*
432                  * Non-format characters go straight out.
433                  */
434                 if (*statfmt != FMT_MAGIC) {
435                         addchar(stdout, *statfmt, &nl);
436                         statfmt++;
437                         continue;
438                 }
439
440                 /*
441                  * The current format "substring" starts here,
442                  * and then we skip the magic.
443                  */
444                 subfmt = statfmt;
445                 statfmt++;
446
447                 /*
448                  * Some simple one-character "formats".
449                  */
450                 switch (*statfmt) {
451                 case SIMPLE_NEWLINE:
452                         addchar(stdout, '\n', &nl);
453                         statfmt++;
454                         continue;
455                 case SIMPLE_TAB:
456                         addchar(stdout, '\t', &nl);
457                         statfmt++;
458                         continue;
459                 case SIMPLE_PERCENT:
460                         addchar(stdout, '%', &nl);
461                         statfmt++;
462                         continue;
463                 case SIMPLE_NUMBER: {
464                         char num[12], *p;
465
466                         snprintf(num, sizeof(num), "%d", fn);
467                         for (p = &num[0]; *p; p++)
468                                 addchar(stdout, *p, &nl);
469                         statfmt++;
470                         continue;
471                 }
472                 }
473
474                 /*
475                  * This must be an actual format string.  Format strings are
476                  * similar to printf(3) formats up to a point, and are of
477                  * the form:
478                  *
479                  *      %       required start of format
480                  *      [-# +0] opt. format characters
481                  *      size    opt. field width
482                  *      .       opt. decimal separator, followed by
483                  *      prec    opt. precision
484                  *      fmt     opt. output specifier (string, numeric, etc.)
485                  *      sub     opt. sub field specifier (high, middle, low)
486                  *      datum   required field specifier (size, mode, etc)
487                  *
488                  * Only the % and the datum selector are required.  All data
489                  * have reasonable default output forms.  The "sub" specifier
490                  * only applies to certain data (mode, dev, rdev, filetype).
491                  * The symlink output defaults to STRING, yet will only emit
492                  * the leading " -> " if STRING is explicitly specified.  The
493                  * sizerdev datum will generate rdev output for character or
494                  * block devices, and size output for all others.
495                  */
496                 flags = 0;
497                 do {
498                         if      (*statfmt == FMT_POUND)
499                                 flags |= FLAG_POUND;
500                         else if (*statfmt == FMT_SPACE)
501                                 flags |= FLAG_SPACE;
502                         else if (*statfmt == FMT_PLUS)
503                                 flags |= FLAG_PLUS;
504                         else if (*statfmt == FMT_ZERO)
505                                 flags |= FLAG_ZERO;
506                         else if (*statfmt == FMT_MINUS)
507                                 flags |= FLAG_MINUS;
508                         else
509                                 break;
510                         statfmt++;
511                 } while (1/*CONSTCOND*/);
512
513                 size = -1;
514                 if (isdigit((unsigned)*statfmt)) {
515                         size = 0;
516                         while (isdigit((unsigned)*statfmt)) {
517                                 size = (size * 10) + (*statfmt - '0');
518                                 statfmt++;
519                                 if (size < 0)
520                                         goto badfmt;
521                         }
522                 }
523
524                 prec = -1;
525                 if (*statfmt == FMT_DOT) {
526                         statfmt++;
527
528                         prec = 0;
529                         while (isdigit((unsigned)*statfmt)) {
530                                 prec = (prec * 10) + (*statfmt - '0');
531                                 statfmt++;
532                                 if (prec < 0)
533                                         goto badfmt;
534                         }
535                 }
536
537 #define fmtcase(x, y)           case (y): (x) = (y); statfmt++; break
538 #define fmtcasef(x, y, z)       case (y): (x) = (z); statfmt++; break
539                 switch (*statfmt) {
540                         fmtcasef(ofmt, FMT_DECIMAL,     FMTF_DECIMAL);
541                         fmtcasef(ofmt, FMT_OCTAL,       FMTF_OCTAL);
542                         fmtcasef(ofmt, FMT_UNSIGNED,    FMTF_UNSIGNED);
543                         fmtcasef(ofmt, FMT_HEX,         FMTF_HEX);
544                         fmtcasef(ofmt, FMT_FLOAT,       FMTF_FLOAT);
545                         fmtcasef(ofmt, FMT_STRING,      FMTF_STRING);
546                 default:
547                         ofmt = 0;
548                         break;
549                 }
550
551                 switch (*statfmt) {
552                         fmtcase(hilo, HIGH_PIECE);
553                         fmtcase(hilo, MIDDLE_PIECE);
554                         fmtcase(hilo, LOW_PIECE);
555                 default:
556                         hilo = 0;
557                         break;
558                 }
559
560                 switch (*statfmt) {
561                         fmtcase(what, SHOW_realpath);
562                         fmtcase(what, SHOW_st_dev);
563                         fmtcase(what, SHOW_st_ino);
564                         fmtcase(what, SHOW_st_mode);
565                         fmtcase(what, SHOW_st_nlink);
566                         fmtcase(what, SHOW_st_uid);
567                         fmtcase(what, SHOW_st_gid);
568                         fmtcase(what, SHOW_st_rdev);
569                         fmtcase(what, SHOW_st_atime);
570                         fmtcase(what, SHOW_st_mtime);
571                         fmtcase(what, SHOW_st_ctime);
572                         fmtcase(what, SHOW_st_btime);
573                         fmtcase(what, SHOW_st_size);
574                         fmtcase(what, SHOW_st_blocks);
575                         fmtcase(what, SHOW_st_blksize);
576                         fmtcase(what, SHOW_st_flags);
577                         fmtcase(what, SHOW_st_gen);
578                         fmtcase(what, SHOW_symlink);
579                         fmtcase(what, SHOW_filetype);
580                         fmtcase(what, SHOW_filename);
581                         fmtcase(what, SHOW_sizerdev);
582                 default:
583                         goto badfmt;
584                 }
585 #undef fmtcasef
586 #undef fmtcase
587
588                 t = format1(st,
589                      file,
590                      subfmt, statfmt - subfmt,
591                      buf, sizeof(buf),
592                      flags, size, prec, ofmt, hilo, what);
593
594                 for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++)
595                         addchar(stdout, buf[i], &nl);
596
597                 continue;
598
599         badfmt:
600                 errx(1, "%.*s: bad format",
601                     (int)(statfmt - subfmt + 1), subfmt);
602         }
603
604         if (!nl && !nonl)
605                 (void)fputc('\n', stdout);
606         (void)fflush(stdout);
607 }
608
609 /*
610  * Arranges output according to a single parsed format substring.
611  */
612 int
613 format1(const struct stat *st,
614     const char *file,
615     const char *fmt, int flen,
616     char *buf, size_t blen,
617     int flags, int size, int prec, int ofmt,
618     int hilo, int what)
619 {
620         u_int64_t data;
621         char *stmp, lfmt[24], tmp[20];
622         const char *sdata;
623         char smode[12], sid[12], path[PATH_MAX + 4];
624         const struct timespec *tsp;
625         struct timespec ts;
626         struct tm *tm;
627         int l, small, formats;
628
629         tsp = NULL;
630         formats = 0;
631         small = 0;
632
633         /*
634          * First, pick out the data and tweak it based on hilo or
635          * specified output format (symlink output only).
636          */
637         switch (what) {
638         case SHOW_st_dev:
639         case SHOW_st_rdev:
640                 small = (sizeof(st->st_dev) == 4);
641                 data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev;
642 #if HAVE_DEVNAME
643                 sdata = (what == SHOW_st_dev) ?
644                     devname(st->st_dev, S_IFBLK) :
645                     devname(st->st_rdev, 
646                     S_ISCHR(st->st_mode) ? S_IFCHR :
647                     S_ISBLK(st->st_mode) ? S_IFBLK :
648                     0U);
649                 if (sdata == NULL)
650                         sdata = "???";
651 #endif /* HAVE_DEVNAME */
652                 if (hilo == HIGH_PIECE) {
653                         data = major(data);
654                         hilo = 0;
655                 }
656                 else if (hilo == LOW_PIECE) {
657                         data = minor((unsigned)data);
658                         hilo = 0;
659                 }
660                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
661 #if HAVE_DEVNAME
662                     FMTF_STRING;
663 #else /* HAVE_DEVNAME */
664                     0;
665 #endif /* HAVE_DEVNAME */
666                 if (ofmt == 0)
667                         ofmt = FMTF_UNSIGNED;
668                 break;
669         case SHOW_st_ino:
670                 small = (sizeof(st->st_ino) == 4);
671                 data = st->st_ino;
672                 sdata = NULL;
673                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
674                 if (ofmt == 0)
675                         ofmt = FMTF_UNSIGNED;
676                 break;
677         case SHOW_st_mode:
678                 small = (sizeof(st->st_mode) == 4);
679                 data = st->st_mode;
680                 strmode(st->st_mode, smode);
681                 stmp = smode;
682                 l = strlen(stmp);
683                 if (stmp[l - 1] == ' ')
684                         stmp[--l] = '\0';
685                 if (hilo == HIGH_PIECE) {
686                         data >>= 12;
687                         stmp += 1;
688                         stmp[3] = '\0';
689                         hilo = 0;
690                 }
691                 else if (hilo == MIDDLE_PIECE) {
692                         data = (data >> 9) & 07;
693                         stmp += 4;
694                         stmp[3] = '\0';
695                         hilo = 0;
696                 }
697                 else if (hilo == LOW_PIECE) {
698                         data &= 0777;
699                         stmp += 7;
700                         stmp[3] = '\0';
701                         hilo = 0;
702                 }
703                 sdata = stmp;
704                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
705                     FMTF_STRING;
706                 if (ofmt == 0)
707                         ofmt = FMTF_OCTAL;
708                 break;
709         case SHOW_st_nlink:
710                 small = (sizeof(st->st_dev) == 4);
711                 data = st->st_nlink;
712                 sdata = NULL;
713                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
714                 if (ofmt == 0)
715                         ofmt = FMTF_UNSIGNED;
716                 break;
717         case SHOW_st_uid:
718                 small = (sizeof(st->st_uid) == 4);
719                 data = st->st_uid;
720                 sdata = user_from_uid(st->st_uid, 1);
721                 if (sdata == NULL) {
722                         snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid);
723                         sdata = sid;
724                 }
725                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
726                     FMTF_STRING;
727                 if (ofmt == 0)
728                         ofmt = FMTF_UNSIGNED;
729                 break;
730         case SHOW_st_gid:
731                 small = (sizeof(st->st_gid) == 4);
732                 data = st->st_gid;
733                 sdata = group_from_gid(st->st_gid, 1);
734                 if (sdata == NULL) {
735                         snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid);
736                         sdata = sid;
737                 }
738                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
739                     FMTF_STRING;
740                 if (ofmt == 0)
741                         ofmt = FMTF_UNSIGNED;
742                 break;
743         case SHOW_st_atime:
744                 tsp = &st->st_atimespec;
745                 /* FALLTHROUGH */
746         case SHOW_st_mtime:
747                 if (tsp == NULL)
748                         tsp = &st->st_mtimespec;
749                 /* FALLTHROUGH */
750         case SHOW_st_ctime:
751                 if (tsp == NULL)
752                         tsp = &st->st_ctimespec;
753                 /* FALLTHROUGH */
754 #if HAVE_STRUCT_STAT_ST_BIRTHTIME
755         case SHOW_st_btime:
756                 if (tsp == NULL)
757                         tsp = &st->st_birthtimespec;
758 #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
759                 ts = *tsp;              /* copy so we can muck with it */
760                 small = (sizeof(ts.tv_sec) == 4);
761                 data = ts.tv_sec;
762                 tm = localtime(&ts.tv_sec);
763                 if (tm == NULL) {
764                         ts.tv_sec = 0;
765                         tm = localtime(&ts.tv_sec);
766                 }
767                 (void)setlocale(LC_TIME, "");
768                 (void)strftime(path, sizeof(path), timefmt, tm);
769                 sdata = path;
770                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
771                     FMTF_FLOAT | FMTF_STRING;
772                 if (ofmt == 0)
773                         ofmt = FMTF_DECIMAL;
774                 break;
775         case SHOW_st_size:
776                 small = (sizeof(st->st_size) == 4);
777                 data = st->st_size;
778                 sdata = NULL;
779                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
780                 if (ofmt == 0)
781                         ofmt = FMTF_UNSIGNED;
782                 break;
783         case SHOW_st_blocks:
784                 small = (sizeof(st->st_blocks) == 4);
785                 data = st->st_blocks;
786                 sdata = NULL;
787                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
788                 if (ofmt == 0)
789                         ofmt = FMTF_UNSIGNED;
790                 break;
791         case SHOW_st_blksize:
792                 small = (sizeof(st->st_blksize) == 4);
793                 data = st->st_blksize;
794                 sdata = NULL;
795                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
796                 if (ofmt == 0)
797                         ofmt = FMTF_UNSIGNED;
798                 break;
799 #if HAVE_STRUCT_STAT_ST_FLAGS
800         case SHOW_st_flags:
801                 small = (sizeof(st->st_flags) == 4);
802                 data = st->st_flags;
803                 sdata = xfflagstostr(st->st_flags);
804                 if (*sdata == '\0')
805                         sdata = "-";
806                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
807                     FMTF_STRING;
808                 if (ofmt == 0)
809                         ofmt = FMTF_UNSIGNED;
810                 break;
811 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
812 #if HAVE_STRUCT_STAT_ST_GEN
813         case SHOW_st_gen:
814                 small = (sizeof(st->st_gen) == 4);
815                 data = st->st_gen;
816                 sdata = NULL;
817                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
818                 if (ofmt == 0)
819                         ofmt = FMTF_UNSIGNED;
820                 break;
821 #endif /* HAVE_STRUCT_STAT_ST_GEN */
822         case SHOW_realpath:
823                 small = 0;
824                 data = 0;
825                 if (file == NULL) {
826                         (void)strlcpy(path, "(stdin)", sizeof(path));
827                         sdata = path;
828                 } else {
829                         snprintf(path, sizeof(path), " -> ");
830                         if (realpath(file, path + 4) == NULL) {
831                                 linkfail = 1;
832                                 l = 0;
833                                 path[0] = '\0';
834                         }
835                         sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
836                 }
837
838                 formats = FMTF_STRING;
839                 if (ofmt == 0)
840                         ofmt = FMTF_STRING;
841                 break;
842         case SHOW_symlink:
843                 small = 0;
844                 data = 0;
845                 if (S_ISLNK(st->st_mode)) {
846                         snprintf(path, sizeof(path), " -> ");
847                         l = readlink(file, path + 4, sizeof(path) - 4 - 1);
848                         if (l == -1) {
849                                 linkfail = 1;
850                                 l = 0;
851                                 path[0] = '\0';
852                         }
853                         path[l + 4] = '\0';
854                         sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
855                 }
856                 else {
857                         linkfail = 1;
858                         sdata = "";
859                 }
860                 formats = FMTF_STRING;
861                 if (ofmt == 0)
862                         ofmt = FMTF_STRING;
863                 break;
864         case SHOW_filetype:
865                 small = 0;
866                 data = 0;
867                 sdata = "";
868                 if (hilo == 0 || hilo == LOW_PIECE) {
869                         switch (st->st_mode & S_IFMT) {
870                         case S_IFIFO:   sdata = "|";    break;
871                         case S_IFDIR:   sdata = "/";    break;
872                         case S_IFREG:
873                                 if (st->st_mode &
874                                     (S_IXUSR | S_IXGRP | S_IXOTH))
875                                         sdata = "*";
876                                 break;
877                         case S_IFLNK:   sdata = "@";    break;
878                         case S_IFSOCK:  sdata = "=";    break;
879 #ifdef S_IFWHT
880                         case S_IFWHT:   sdata = "%";    break;
881 #endif /* S_IFWHT */
882 #ifdef S_IFDOOR
883                         case S_IFDOOR:  sdata = ">";    break;
884 #endif /* S_IFDOOR */
885                         }
886                         hilo = 0;
887                 }
888                 else if (hilo == HIGH_PIECE) {
889                         switch (st->st_mode & S_IFMT) {
890                         case S_IFIFO:   sdata = "Fifo File";            break;
891                         case S_IFCHR:   sdata = "Character Device";     break;
892                         case S_IFDIR:   sdata = "Directory";            break;
893                         case S_IFBLK:   sdata = "Block Device";         break;
894                         case S_IFREG:   sdata = "Regular File";         break;
895                         case S_IFLNK:   sdata = "Symbolic Link";        break;
896                         case S_IFSOCK:  sdata = "Socket";               break;
897 #ifdef S_IFWHT
898                         case S_IFWHT:   sdata = "Whiteout File";        break;
899 #endif /* S_IFWHT */
900 #ifdef S_IFDOOR
901                         case S_IFDOOR:  sdata = "Door";                 break;
902 #endif /* S_IFDOOR */
903                         default:        sdata = "???";                  break;
904                         }
905                         hilo = 0;
906                 }
907                 formats = FMTF_STRING;
908                 if (ofmt == 0)
909                         ofmt = FMTF_STRING;
910                 break;
911         case SHOW_filename:
912                 small = 0;
913                 data = 0;
914                 (void)strlcpy(path, file, sizeof(path));
915                 sdata = path;
916                 formats = FMTF_STRING;
917                 if (ofmt == 0)
918                         ofmt = FMTF_STRING;
919                 break;
920         case SHOW_sizerdev:
921                 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
922                         char majdev[20], mindev[20];
923                         int l1, l2;
924
925                         l1 = format1(st,
926                             file,
927                             fmt, flen,
928                             majdev, sizeof(majdev),
929                             flags, size, prec,
930                             ofmt, HIGH_PIECE, SHOW_st_rdev);
931                         l2 = format1(st,
932                             file,
933                             fmt, flen,
934                             mindev, sizeof(mindev),
935                             flags, size, prec,
936                             ofmt, LOW_PIECE, SHOW_st_rdev);
937                         return (snprintf(buf, blen, "%.*s,%.*s",
938                             l1, majdev, l2, mindev));
939                 }
940                 else {
941                         return (format1(st,
942                             file,
943                             fmt, flen,
944                             buf, blen,
945                             flags, size, prec,
946                             ofmt, 0, SHOW_st_size));
947                 }
948                 /*NOTREACHED*/
949         default:
950                 errx(1, "%.*s: bad format", (int)flen, fmt);
951         }
952
953         /*
954          * If a subdatum was specified but not supported, or an output
955          * format was selected that is not supported, that's an error.
956          */
957         if (hilo != 0 || (ofmt & formats) == 0)
958                 errx(1, "%.*s: bad format", (int)flen, fmt);
959
960         /*
961          * Assemble the format string for passing to printf(3).
962          */
963         lfmt[0] = '\0';
964         (void)strcat(lfmt, "%");
965         if (flags & FLAG_POUND)
966                 (void)strcat(lfmt, "#");
967         if (flags & FLAG_SPACE)
968                 (void)strcat(lfmt, " ");
969         if (flags & FLAG_PLUS)
970                 (void)strcat(lfmt, "+");
971         if (flags & FLAG_MINUS)
972                 (void)strcat(lfmt, "-");
973         if (flags & FLAG_ZERO)
974                 (void)strcat(lfmt, "0");
975
976         /*
977          * Only the timespecs support the FLOAT output format, and that
978          * requires work that differs from the other formats.
979          */ 
980         if (ofmt == FMTF_FLOAT) {
981                 /*
982                  * Nothing after the decimal point, so just print seconds.
983                  */
984                 if (prec == 0) {
985                         if (size != -1) {
986                                 (void)snprintf(tmp, sizeof(tmp), "%d", size);
987                                 (void)strcat(lfmt, tmp);
988                         }
989                         (void)strcat(lfmt, "lld");
990                         return (snprintf(buf, blen, lfmt,
991                             (long long)ts.tv_sec));
992                 }
993
994                 /*
995                  * Unspecified precision gets all the precision we have:
996                  * 9 digits.
997                  */
998                 if (prec == -1)
999                         prec = 9;
1000
1001                 /*
1002                  * Adjust the size for the decimal point and the digits
1003                  * that will follow.
1004                  */
1005                 size -= prec + 1;
1006
1007                 /*
1008                  * Any leftover size that's legitimate will be used.
1009                  */
1010                 if (size > 0) {
1011                         (void)snprintf(tmp, sizeof(tmp), "%d", size);
1012                         (void)strcat(lfmt, tmp);
1013                 }
1014                 /* Seconds: time_t cast to long long. */
1015                 (void)strcat(lfmt, "lld");
1016
1017                 /*
1018                  * The stuff after the decimal point always needs zero
1019                  * filling.
1020                  */
1021                 (void)strcat(lfmt, ".%0");
1022
1023                 /*
1024                  * We can "print" at most nine digits of precision.  The
1025                  * rest we will pad on at the end.
1026                  *
1027                  * Nanoseconds: long.
1028                  */
1029                 (void)snprintf(tmp, sizeof(tmp), "%dld", MIN(prec, 9));
1030                 (void)strcat(lfmt, tmp);
1031
1032                 /*
1033                  * For precision of less that nine digits, trim off the
1034                  * less significant figures.
1035                  */
1036                 for (; prec < 9; prec++)
1037                         ts.tv_nsec /= 10;
1038
1039                 /*
1040                  * Use the format, and then tack on any zeroes that
1041                  * might be required to make up the requested precision.
1042                  */
1043                 l = snprintf(buf, blen, lfmt, (long long)ts.tv_sec, ts.tv_nsec);
1044                 for (; prec > 9 && l < (int)blen; prec--, l++)
1045                         (void)strcat(buf, "0");
1046                 return (l);
1047         }
1048
1049         /*
1050          * Add on size and precision, if specified, to the format.
1051          */
1052         if (size != -1) {
1053                 (void)snprintf(tmp, sizeof(tmp), "%d", size);
1054                 (void)strcat(lfmt, tmp);
1055         }
1056         if (prec != -1) {
1057                 (void)snprintf(tmp, sizeof(tmp), ".%d", prec);
1058                 (void)strcat(lfmt, tmp);
1059         }
1060
1061         /*
1062          * String output uses the temporary sdata.
1063          */
1064         if (ofmt == FMTF_STRING) {
1065                 if (sdata == NULL)
1066                         errx(1, "%.*s: bad format", (int)flen, fmt);
1067                 (void)strcat(lfmt, "s");
1068                 return (snprintf(buf, blen, lfmt, sdata));
1069         }
1070
1071         /*
1072          * Ensure that sign extension does not cause bad looking output
1073          * for some forms.
1074          */
1075         if (small && ofmt != FMTF_DECIMAL)
1076                 data = (u_int32_t)data;
1077
1078         /*
1079          * The four "numeric" output forms.
1080          */
1081         (void)strcat(lfmt, "ll");
1082         switch (ofmt) {
1083         case FMTF_DECIMAL:      (void)strcat(lfmt, "d");        break;
1084         case FMTF_OCTAL:                (void)strcat(lfmt, "o");        break;
1085         case FMTF_UNSIGNED:     (void)strcat(lfmt, "u");        break;
1086         case FMTF_HEX:          (void)strcat(lfmt, "x");        break;
1087         }
1088
1089         return (snprintf(buf, blen, lfmt, data));
1090 }
1091
1092
1093 #define hex2nibble(c) (c <= '9' ? c - '0' : toupper(c) - 'A' + 10)
1094 int
1095 hex2byte(const char c[2]) {
1096         if (!(ishexnumber(c[0]) && ishexnumber(c[1])))
1097                 return -1;
1098         return (hex2nibble(c[0]) << 4) + hex2nibble(c[1]);
1099 }