[BACK]Return to stat.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / stat

Annotation of src/usr.bin/stat/stat.c, Revision 1.3

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