]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/stat/stat.c
libarchive: merge from vendor branch
[FreeBSD/FreeBSD.git] / usr.bin / stat / stat.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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", 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 = devname(what == SHOW_st_dev ? st->st_dev :
644                     st->st_rdev, S_ISCHR(st->st_mode) ? S_IFCHR :
645                     (S_ISBLK(st->st_mode) ? S_IFBLK : 0));
646 #endif /* HAVE_DEVNAME */
647                 if (hilo == HIGH_PIECE) {
648                         data = major(data);
649                         hilo = 0;
650                 }
651                 else if (hilo == LOW_PIECE) {
652                         data = minor((unsigned)data);
653                         hilo = 0;
654                 }
655                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
656 #if HAVE_DEVNAME
657                     FMTF_STRING;
658 #else /* HAVE_DEVNAME */
659                     0;
660 #endif /* HAVE_DEVNAME */
661                 if (ofmt == 0)
662                         ofmt = FMTF_UNSIGNED;
663                 break;
664         case SHOW_st_ino:
665                 small = (sizeof(st->st_ino) == 4);
666                 data = st->st_ino;
667                 sdata = NULL;
668                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
669                 if (ofmt == 0)
670                         ofmt = FMTF_UNSIGNED;
671                 break;
672         case SHOW_st_mode:
673                 small = (sizeof(st->st_mode) == 4);
674                 data = st->st_mode;
675                 strmode(st->st_mode, smode);
676                 stmp = smode;
677                 l = strlen(stmp);
678                 if (stmp[l - 1] == ' ')
679                         stmp[--l] = '\0';
680                 if (hilo == HIGH_PIECE) {
681                         data >>= 12;
682                         stmp += 1;
683                         stmp[3] = '\0';
684                         hilo = 0;
685                 }
686                 else if (hilo == MIDDLE_PIECE) {
687                         data = (data >> 9) & 07;
688                         stmp += 4;
689                         stmp[3] = '\0';
690                         hilo = 0;
691                 }
692                 else if (hilo == LOW_PIECE) {
693                         data &= 0777;
694                         stmp += 7;
695                         stmp[3] = '\0';
696                         hilo = 0;
697                 }
698                 sdata = stmp;
699                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
700                     FMTF_STRING;
701                 if (ofmt == 0)
702                         ofmt = FMTF_OCTAL;
703                 break;
704         case SHOW_st_nlink:
705                 small = (sizeof(st->st_dev) == 4);
706                 data = st->st_nlink;
707                 sdata = NULL;
708                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
709                 if (ofmt == 0)
710                         ofmt = FMTF_UNSIGNED;
711                 break;
712         case SHOW_st_uid:
713                 small = (sizeof(st->st_uid) == 4);
714                 data = st->st_uid;
715                 sdata = user_from_uid(st->st_uid, 1);
716                 if (sdata == NULL) {
717                         snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid);
718                         sdata = sid;
719                 }
720                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
721                     FMTF_STRING;
722                 if (ofmt == 0)
723                         ofmt = FMTF_UNSIGNED;
724                 break;
725         case SHOW_st_gid:
726                 small = (sizeof(st->st_gid) == 4);
727                 data = st->st_gid;
728                 sdata = group_from_gid(st->st_gid, 1);
729                 if (sdata == NULL) {
730                         snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid);
731                         sdata = sid;
732                 }
733                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
734                     FMTF_STRING;
735                 if (ofmt == 0)
736                         ofmt = FMTF_UNSIGNED;
737                 break;
738         case SHOW_st_atime:
739                 tsp = &st->st_atimespec;
740                 /* FALLTHROUGH */
741         case SHOW_st_mtime:
742                 if (tsp == NULL)
743                         tsp = &st->st_mtimespec;
744                 /* FALLTHROUGH */
745         case SHOW_st_ctime:
746                 if (tsp == NULL)
747                         tsp = &st->st_ctimespec;
748                 /* FALLTHROUGH */
749 #if HAVE_STRUCT_STAT_ST_BIRTHTIME
750         case SHOW_st_btime:
751                 if (tsp == NULL)
752                         tsp = &st->st_birthtimespec;
753 #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */
754                 ts = *tsp;              /* copy so we can muck with it */
755                 small = (sizeof(ts.tv_sec) == 4);
756                 data = ts.tv_sec;
757                 tm = localtime(&ts.tv_sec);
758                 if (tm == NULL) {
759                         ts.tv_sec = 0;
760                         tm = localtime(&ts.tv_sec);
761                 }
762                 (void)setlocale(LC_TIME, "");
763                 (void)strftime(path, sizeof(path), timefmt, tm);
764                 sdata = path;
765                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
766                     FMTF_FLOAT | FMTF_STRING;
767                 if (ofmt == 0)
768                         ofmt = FMTF_DECIMAL;
769                 break;
770         case SHOW_st_size:
771                 small = (sizeof(st->st_size) == 4);
772                 data = st->st_size;
773                 sdata = NULL;
774                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
775                 if (ofmt == 0)
776                         ofmt = FMTF_UNSIGNED;
777                 break;
778         case SHOW_st_blocks:
779                 small = (sizeof(st->st_blocks) == 4);
780                 data = st->st_blocks;
781                 sdata = NULL;
782                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
783                 if (ofmt == 0)
784                         ofmt = FMTF_UNSIGNED;
785                 break;
786         case SHOW_st_blksize:
787                 small = (sizeof(st->st_blksize) == 4);
788                 data = st->st_blksize;
789                 sdata = NULL;
790                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
791                 if (ofmt == 0)
792                         ofmt = FMTF_UNSIGNED;
793                 break;
794 #if HAVE_STRUCT_STAT_ST_FLAGS
795         case SHOW_st_flags:
796                 small = (sizeof(st->st_flags) == 4);
797                 data = st->st_flags;
798                 sdata = xfflagstostr(st->st_flags);
799                 if (*sdata == '\0')
800                         sdata = "-";
801                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX |
802                     FMTF_STRING;
803                 if (ofmt == 0)
804                         ofmt = FMTF_UNSIGNED;
805                 break;
806 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
807 #if HAVE_STRUCT_STAT_ST_GEN
808         case SHOW_st_gen:
809                 small = (sizeof(st->st_gen) == 4);
810                 data = st->st_gen;
811                 sdata = NULL;
812                 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX;
813                 if (ofmt == 0)
814                         ofmt = FMTF_UNSIGNED;
815                 break;
816 #endif /* HAVE_STRUCT_STAT_ST_GEN */
817         case SHOW_realpath:
818                 small = 0;
819                 data = 0;
820                 if (file == NULL) {
821                         (void)strlcpy(path, "(stdin)", sizeof(path));
822                         sdata = path;
823                 } else {
824                         snprintf(path, sizeof(path), " -> ");
825                         if (realpath(file, path + 4) == NULL) {
826                                 linkfail = 1;
827                                 l = 0;
828                                 path[0] = '\0';
829                         }
830                         sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
831                 }
832
833                 formats = FMTF_STRING;
834                 if (ofmt == 0)
835                         ofmt = FMTF_STRING;
836                 break;
837         case SHOW_symlink:
838                 small = 0;
839                 data = 0;
840                 if (S_ISLNK(st->st_mode)) {
841                         snprintf(path, sizeof(path), " -> ");
842                         l = readlink(file, path + 4, sizeof(path) - 4 - 1);
843                         if (l == -1) {
844                                 linkfail = 1;
845                                 l = 0;
846                                 path[0] = '\0';
847                         }
848                         path[l + 4] = '\0';
849                         sdata = path + (ofmt == FMTF_STRING ? 0 : 4);
850                 }
851                 else {
852                         linkfail = 1;
853                         sdata = "";
854                 }
855                 formats = FMTF_STRING;
856                 if (ofmt == 0)
857                         ofmt = FMTF_STRING;
858                 break;
859         case SHOW_filetype:
860                 small = 0;
861                 data = 0;
862                 sdata = "";
863                 if (hilo == 0 || hilo == LOW_PIECE) {
864                         switch (st->st_mode & S_IFMT) {
865                         case S_IFIFO:   sdata = "|";    break;
866                         case S_IFDIR:   sdata = "/";    break;
867                         case S_IFREG:
868                                 if (st->st_mode &
869                                     (S_IXUSR | S_IXGRP | S_IXOTH))
870                                         sdata = "*";
871                                 break;
872                         case S_IFLNK:   sdata = "@";    break;
873                         case S_IFSOCK:  sdata = "=";    break;
874 #ifdef S_IFWHT
875                         case S_IFWHT:   sdata = "%";    break;
876 #endif /* S_IFWHT */
877 #ifdef S_IFDOOR
878                         case S_IFDOOR:  sdata = ">";    break;
879 #endif /* S_IFDOOR */
880                         }
881                         hilo = 0;
882                 }
883                 else if (hilo == HIGH_PIECE) {
884                         switch (st->st_mode & S_IFMT) {
885                         case S_IFIFO:   sdata = "Fifo File";            break;
886                         case S_IFCHR:   sdata = "Character Device";     break;
887                         case S_IFDIR:   sdata = "Directory";            break;
888                         case S_IFBLK:   sdata = "Block Device";         break;
889                         case S_IFREG:   sdata = "Regular File";         break;
890                         case S_IFLNK:   sdata = "Symbolic Link";        break;
891                         case S_IFSOCK:  sdata = "Socket";               break;
892 #ifdef S_IFWHT
893                         case S_IFWHT:   sdata = "Whiteout File";        break;
894 #endif /* S_IFWHT */
895 #ifdef S_IFDOOR
896                         case S_IFDOOR:  sdata = "Door";                 break;
897 #endif /* S_IFDOOR */
898                         default:        sdata = "???";                  break;
899                         }
900                         hilo = 0;
901                 }
902                 formats = FMTF_STRING;
903                 if (ofmt == 0)
904                         ofmt = FMTF_STRING;
905                 break;
906         case SHOW_filename:
907                 small = 0;
908                 data = 0;
909                 (void)strlcpy(path, file, sizeof(path));
910                 sdata = path;
911                 formats = FMTF_STRING;
912                 if (ofmt == 0)
913                         ofmt = FMTF_STRING;
914                 break;
915         case SHOW_sizerdev:
916                 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
917                         char majdev[20], mindev[20];
918                         int l1, l2;
919
920                         l1 = format1(st,
921                             file,
922                             fmt, flen,
923                             majdev, sizeof(majdev),
924                             flags, size, prec,
925                             ofmt, HIGH_PIECE, SHOW_st_rdev);
926                         l2 = format1(st,
927                             file,
928                             fmt, flen,
929                             mindev, sizeof(mindev),
930                             flags, size, prec,
931                             ofmt, LOW_PIECE, SHOW_st_rdev);
932                         return (snprintf(buf, blen, "%.*s,%.*s",
933                             l1, majdev, l2, mindev));
934                 }
935                 else {
936                         return (format1(st,
937                             file,
938                             fmt, flen,
939                             buf, blen,
940                             flags, size, prec,
941                             ofmt, 0, SHOW_st_size));
942                 }
943                 /*NOTREACHED*/
944         default:
945                 errx(1, "%.*s: bad format", (int)flen, fmt);
946         }
947
948         /*
949          * If a subdatum was specified but not supported, or an output
950          * format was selected that is not supported, that's an error.
951          */
952         if (hilo != 0 || (ofmt & formats) == 0)
953                 errx(1, "%.*s: bad format", (int)flen, fmt);
954
955         /*
956          * Assemble the format string for passing to printf(3).
957          */
958         lfmt[0] = '\0';
959         (void)strcat(lfmt, "%");
960         if (flags & FLAG_POUND)
961                 (void)strcat(lfmt, "#");
962         if (flags & FLAG_SPACE)
963                 (void)strcat(lfmt, " ");
964         if (flags & FLAG_PLUS)
965                 (void)strcat(lfmt, "+");
966         if (flags & FLAG_MINUS)
967                 (void)strcat(lfmt, "-");
968         if (flags & FLAG_ZERO)
969                 (void)strcat(lfmt, "0");
970
971         /*
972          * Only the timespecs support the FLOAT output format, and that
973          * requires work that differs from the other formats.
974          */ 
975         if (ofmt == FMTF_FLOAT) {
976                 /*
977                  * Nothing after the decimal point, so just print seconds.
978                  */
979                 if (prec == 0) {
980                         if (size != -1) {
981                                 (void)snprintf(tmp, sizeof(tmp), "%d", size);
982                                 (void)strcat(lfmt, tmp);
983                         }
984                         (void)strcat(lfmt, "lld");
985                         return (snprintf(buf, blen, lfmt,
986                             (long long)ts.tv_sec));
987                 }
988
989                 /*
990                  * Unspecified precision gets all the precision we have:
991                  * 9 digits.
992                  */
993                 if (prec == -1)
994                         prec = 9;
995
996                 /*
997                  * Adjust the size for the decimal point and the digits
998                  * that will follow.
999                  */
1000                 size -= prec + 1;
1001
1002                 /*
1003                  * Any leftover size that's legitimate will be used.
1004                  */
1005                 if (size > 0) {
1006                         (void)snprintf(tmp, sizeof(tmp), "%d", size);
1007                         (void)strcat(lfmt, tmp);
1008                 }
1009                 /* Seconds: time_t cast to long long. */
1010                 (void)strcat(lfmt, "lld");
1011
1012                 /*
1013                  * The stuff after the decimal point always needs zero
1014                  * filling.
1015                  */
1016                 (void)strcat(lfmt, ".%0");
1017
1018                 /*
1019                  * We can "print" at most nine digits of precision.  The
1020                  * rest we will pad on at the end.
1021                  *
1022                  * Nanoseconds: long.
1023                  */
1024                 (void)snprintf(tmp, sizeof(tmp), "%dld", MIN(prec, 9));
1025                 (void)strcat(lfmt, tmp);
1026
1027                 /*
1028                  * For precision of less that nine digits, trim off the
1029                  * less significant figures.
1030                  */
1031                 for (; prec < 9; prec++)
1032                         ts.tv_nsec /= 10;
1033
1034                 /*
1035                  * Use the format, and then tack on any zeroes that
1036                  * might be required to make up the requested precision.
1037                  */
1038                 l = snprintf(buf, blen, lfmt, (long long)ts.tv_sec, ts.tv_nsec);
1039                 for (; prec > 9 && l < (int)blen; prec--, l++)
1040                         (void)strcat(buf, "0");
1041                 return (l);
1042         }
1043
1044         /*
1045          * Add on size and precision, if specified, to the format.
1046          */
1047         if (size != -1) {
1048                 (void)snprintf(tmp, sizeof(tmp), "%d", size);
1049                 (void)strcat(lfmt, tmp);
1050         }
1051         if (prec != -1) {
1052                 (void)snprintf(tmp, sizeof(tmp), ".%d", prec);
1053                 (void)strcat(lfmt, tmp);
1054         }
1055
1056         /*
1057          * String output uses the temporary sdata.
1058          */
1059         if (ofmt == FMTF_STRING) {
1060                 if (sdata == NULL)
1061                         errx(1, "%.*s: bad format", (int)flen, fmt);
1062                 (void)strcat(lfmt, "s");
1063                 return (snprintf(buf, blen, lfmt, sdata));
1064         }
1065
1066         /*
1067          * Ensure that sign extension does not cause bad looking output
1068          * for some forms.
1069          */
1070         if (small && ofmt != FMTF_DECIMAL)
1071                 data = (u_int32_t)data;
1072
1073         /*
1074          * The four "numeric" output forms.
1075          */
1076         (void)strcat(lfmt, "ll");
1077         switch (ofmt) {
1078         case FMTF_DECIMAL:      (void)strcat(lfmt, "d");        break;
1079         case FMTF_OCTAL:                (void)strcat(lfmt, "o");        break;
1080         case FMTF_UNSIGNED:     (void)strcat(lfmt, "u");        break;
1081         case FMTF_HEX:          (void)strcat(lfmt, "x");        break;
1082         }
1083
1084         return (snprintf(buf, blen, lfmt, data));
1085 }
1086
1087
1088 #define hex2nibble(c) (c <= '9' ? c - '0' : toupper(c) - 'A' + 10)
1089 int
1090 hex2byte(const char c[2]) {
1091         if (!(ishexnumber(c[0]) && ishexnumber(c[1])))
1092                 return -1;
1093         return (hex2nibble(c[0]) << 4) + hex2nibble(c[1]);
1094 }