diff --git a/usr.bin/stat/stat.c b/usr.bin/stat/stat.c index d8810eeaf115..c41a8f0193e9 100644 --- a/usr.bin/stat/stat.c +++ b/usr.bin/stat/stat.c @@ -1,4 +1,4 @@ -/* $NetBSD: stat.c,v 1.3 2002/05/31 16:45:16 atatat Exp $ */ +/* $NetBSD: stat.c,v 1.13 2003/07/25 03:21:17 atatat Exp $ */ /* * Copyright (c) 2002 The NetBSD Foundation, Inc. @@ -37,34 +37,72 @@ */ #include -#ifndef lint -__RCSID("$NetBSD: stat.c,v 1.3 2002/05/31 16:45:16 atatat Exp $"); -__FBSDID("$FreeBSD$"); +#if defined(__RCSID) && !defined(lint) +__RCSID("$NetBSD: stat.c,v 1.13 2003/07/25 03:21:17 atatat Exp $"); #endif +#if HAVE_CONFIG_H +#include "config.h" +#else /* HAVE_CONFIG_H */ +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#define HAVE_STRUCT_STAT_ST_GEN 1 +#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +#define HAVE_DEVNAME 1 +#endif /* HAVE_CONFIG_H */ + #include -#include #include -#include -#include -#include -#include + #include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_STRUCT_STAT_ST_FLAGS +#define DEF_F "%#Xf " +#define RAW_F "%f " +#define SHELL_F " st_flags=%f" +#else /* HAVE_STRUCT_STAT_ST_FLAGS */ +#define DEF_F +#define RAW_F +#define SHELL_F +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ + +#if HAVE_STRUCT_STAT_ST_BIRTHTIME +#define DEF_B "\"%SB\" " +#define RAW_B "%B " +#define SHELL_B "st_birthtime=%B " +#else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ +#define DEF_B +#define RAW_B +#define SHELL_B +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ + +#if HAVE_STRUCT_STAT_ST_ATIM +#define st_atimespec st_atim +#define st_ctimespec st_ctim +#define st_mtimespec st_mtim +#endif /* HAVE_STRUCT_STAT_ST_ATIM */ #define DEF_FORMAT \ - "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" %k %b %N" -#define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c %k %b %N" + "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \ + "%k %b " DEF_F "%N" +#define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \ + "%k %b " RAW_F "%N" #define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" #define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" #define SHELL_FORMAT \ "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ - "st_atimespec=%a st_mtimespec=%m st_ctimespec=%c " \ - "st_blksize=%k st_blocks=%b" + "st_atime=%a st_mtime=%m st_ctime=%c " SHELL_B \ + "st_blksize=%k st_blocks=%b" SHELL_F #define LINUX_FORMAT \ " File: \"%N\"%n" \ " Size: %-11z FileType: %HT%n" \ @@ -106,6 +144,13 @@ __FBSDID("$FreeBSD$"); #define FMT_FLOAT 'F' #define FMT_STRING 'S' +#define FMTF_DECIMAL 0x01 +#define FMTF_OCTAL 0x02 +#define FMTF_UNSIGNED 0x04 +#define FMTF_HEX 0x08 +#define FMTF_FLOAT 0x10 +#define FMTF_STRING 0x20 + #define HIGH_PIECE 'H' #define MIDDLE_PIECE 'M' #define LOW_PIECE 'L' @@ -120,6 +165,7 @@ __FBSDID("$FreeBSD$"); #define SHOW_st_atime 'a' #define SHOW_st_mtime 'm' #define SHOW_st_ctime 'c' +#define SHOW_st_btime 'B' #define SHOW_st_size 'z' #define SHOW_st_blocks 'b' #define SHOW_st_blksize 'k' @@ -130,9 +176,9 @@ __FBSDID("$FreeBSD$"); #define SHOW_filename 'N' #define SHOW_sizerdev 'Z' -void usage(void); +void usage(const char *); void output(const struct stat *, const char *, - const char *, int, int); + const char *, int, int, int); int format1(const struct stat *, /* stat info */ const char *, /* the file name */ const char *, int, /* the format string itself */ @@ -141,31 +187,45 @@ int format1(const struct stat *, /* stat info */ int, int); char *timefmt; +int linkfail; -#define addchar(b, n, l, c, nl) \ +#define addchar(s, c, nl) \ do { \ - if ((*(n)) < (l)) { \ - (b)[(*(n))++] = (c); \ - (*nl) = ((c) == '\n'); \ - } \ + (void)fputc((c), (s)); \ + (*nl) = ((c) == '\n'); \ } while (0/*CONSTCOND*/) int main(int argc, char *argv[]) { struct stat st; - int ch, rc, errs; - int lsF, fmtchar, usestat, fn, nonl; - char *statfmt; + int ch, rc, errs, am_readlink; + int lsF, fmtchar, usestat, fn, nonl, quiet; + char *statfmt, *options, *synopsis; + am_readlink = 0; lsF = 0; fmtchar = '\0'; usestat = 0; nonl = 0; + quiet = 0; + linkfail = 0; statfmt = NULL; timefmt = NULL; - while ((ch = getopt(argc, argv, "f:FlLnrst:x")) != -1) + if (strcmp(getprogname(), "readlink") == 0) { + am_readlink = 1; + options = "n"; + synopsis = "[-n] [file ...]"; + statfmt = "%Y"; + fmtchar = 'f'; + quiet = 1; + } else { + options = "f:FlLnqrst:x"; + synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]"; + } + + while ((ch = getopt(argc, argv, options)) != -1) switch (ch) { case 'F': lsF = 1; @@ -176,6 +236,9 @@ main(int argc, char *argv[]) case 'n': nonl = 1; break; + case 'q': + quiet = 1; + break; case 'f': statfmt = optarg; /* FALLTHROUGH */ @@ -192,7 +255,7 @@ main(int argc, char *argv[]) timefmt = optarg; break; default: - usage(); + usage(synopsis); } argc -= optind; @@ -230,7 +293,7 @@ main(int argc, char *argv[]) timefmt = "%c"; break; default: - usage(); + usage(synopsis); /*NOTREACHED*/ } @@ -248,26 +311,27 @@ main(int argc, char *argv[]) if (rc == -1) { errs = 1; - warn("%s: stat", argc == 0 ? "(stdin)" : argv[0]); + linkfail = 1; + if (!quiet) + warn("%s: stat", + argc == 0 ? "(stdin)" : argv[0]); } else - output(&st, argv[0], statfmt, fn, nonl); + output(&st, argv[0], statfmt, fn, nonl, quiet); argv++; argc--; fn++; } while (argc > 0); - return (errs); + return (am_readlink ? linkfail : errs); } void -usage(void) +usage(const char *synopsis) { - (void)fprintf(stderr, - "usage: %s [-FlLnrsx] [-f format] [-t timefmt] [file ...]\n", - getprogname()); + (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis); exit(1); } @@ -276,22 +340,21 @@ usage(void) */ void output(const struct stat *st, const char *file, - const char *statfmt, int fn, int nonl) + const char *statfmt, int fn, int nonl, int quiet) { int flags, size, prec, ofmt, hilo, what; - char buf[4096], subbuf[MAXPATHLEN]; + char buf[PATH_MAX]; const char *subfmt; int nl, t, i; - size_t len; - len = 0; + nl = 1; while (*statfmt != '\0') { /* * Non-format characters go straight out. */ if (*statfmt != FMT_MAGIC) { - addchar(buf, &len, sizeof(buf), *statfmt, &nl); + addchar(stdout, *statfmt, &nl); statfmt++; continue; } @@ -308,15 +371,15 @@ output(const struct stat *st, const char *file, */ switch (*statfmt) { case SIMPLE_NEWLINE: - addchar(buf, &len, sizeof(buf), '\n', &nl); + addchar(stdout, '\n', &nl); statfmt++; continue; case SIMPLE_TAB: - addchar(buf, &len, sizeof(buf), '\t', &nl); + addchar(stdout, '\t', &nl); statfmt++; continue; case SIMPLE_PERCENT: - addchar(buf, &len, sizeof(buf), '%', &nl); + addchar(stdout, '%', &nl); statfmt++; continue; case SIMPLE_NUMBER: { @@ -324,7 +387,7 @@ output(const struct stat *st, const char *file, snprintf(num, sizeof(num), "%d", fn); for (p = &num[0]; *p; p++) - addchar(buf, &len, sizeof(buf), *p, &nl); + addchar(stdout, *p, &nl); statfmt++; continue; } @@ -393,14 +456,15 @@ output(const struct stat *st, const char *file, } } -#define fmtcase(x, y) case (y): (x) = (y); statfmt++; break +#define fmtcase(x, y) case (y): (x) = (y); statfmt++; break +#define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break switch (*statfmt) { - fmtcase(ofmt, FMT_DECIMAL); - fmtcase(ofmt, FMT_OCTAL); - fmtcase(ofmt, FMT_UNSIGNED); - fmtcase(ofmt, FMT_HEX); - fmtcase(ofmt, FMT_FLOAT); - fmtcase(ofmt, FMT_STRING); + fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); + fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); + fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); + fmtcasef(ofmt, FMT_HEX, FMTF_HEX); + fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); + fmtcasef(ofmt, FMT_STRING, FMTF_STRING); default: ofmt = 0; break; @@ -426,6 +490,7 @@ output(const struct stat *st, const char *file, fmtcase(what, SHOW_st_atime); fmtcase(what, SHOW_st_mtime); fmtcase(what, SHOW_st_ctime); + fmtcase(what, SHOW_st_btime); fmtcase(what, SHOW_st_size); fmtcase(what, SHOW_st_blocks); fmtcase(what, SHOW_st_blksize); @@ -438,16 +503,17 @@ output(const struct stat *st, const char *file, default: goto badfmt; } +#undef fmtcasef #undef fmtcase t = format1(st, file, subfmt, statfmt - subfmt, - subbuf, sizeof(subbuf), + buf, sizeof(buf), flags, size, prec, ofmt, hilo, what); - for (i = 0; i < t && i < sizeof(subbuf); i++) - addchar(buf, &len, sizeof(buf), subbuf[i], &nl); + for (i = 0; i < t && i < sizeof(buf); i++) + addchar(stdout, buf[i], &nl); continue; @@ -456,9 +522,9 @@ output(const struct stat *st, const char *file, (int)(statfmt - subfmt + 1), subfmt); } - (void)write(STDOUT_FILENO, buf, len); if (!nl && !nonl) - (void)write(STDOUT_FILENO, "\n", sizeof("\n") - 1); + (void)fputc('\n', stdout); + (void)fflush(stdout); } /* @@ -474,7 +540,7 @@ format1(const struct stat *st, { u_int64_t data; char *sdata, lfmt[24], tmp[20]; - char smode[12], sid[12], path[MAXPATHLEN + 4]; + char smode[12], sid[12], path[PATH_MAX + 4]; struct passwd *pw; struct group *gr; const struct timespec *tsp; @@ -495,6 +561,7 @@ format1(const struct stat *st, case SHOW_st_rdev: small = (sizeof(st->st_dev) == 4); data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; +#if HAVE_DEVNAME sdata = (what == SHOW_st_dev) ? devname(st->st_dev, S_IFBLK) : devname(st->st_rdev, @@ -503,6 +570,7 @@ format1(const struct stat *st, 0U); if (sdata == NULL) sdata = "???"; +#endif /* HAVE_DEVNAME */ if (hilo == HIGH_PIECE) { data = major(data); hilo = 0; @@ -511,18 +579,22 @@ format1(const struct stat *st, data = minor((unsigned)data); hilo = 0; } - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX | - FMT_STRING; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | +#if HAVE_DEVNAME + FMTF_STRING; +#else /* HAVE_DEVNAME */ + 0; +#endif /* HAVE_DEVNAME */ if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; case SHOW_st_ino: small = (sizeof(st->st_ino) == 4); data = st->st_ino; sdata = NULL; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; case SHOW_st_mode: small = (sizeof(st->st_mode) == 4); @@ -550,18 +622,18 @@ format1(const struct stat *st, sdata[3] = '\0'; hilo = 0; } - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX | - FMT_STRING; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; if (ofmt == 0) - ofmt = FMT_OCTAL; + ofmt = FMTF_OCTAL; break; case SHOW_st_nlink: small = (sizeof(st->st_dev) == 4); data = st->st_nlink; sdata = NULL; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; case SHOW_st_uid: small = (sizeof(st->st_uid) == 4); @@ -572,10 +644,10 @@ format1(const struct stat *st, snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); sdata = sid; } - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX | - FMT_STRING; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; case SHOW_st_gid: small = (sizeof(st->st_gid) == 4); @@ -586,10 +658,10 @@ format1(const struct stat *st, snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); sdata = sid; } - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX | - FMT_STRING; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; case SHOW_st_atime: tsp = &st->st_atimespec; @@ -601,6 +673,12 @@ format1(const struct stat *st, case SHOW_st_ctime: if (tsp == NULL) tsp = &st->st_ctimespec; + /* FALLTHROUGH */ +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + case SHOW_st_btime: + if (tsp == NULL) + tsp = &st->st_birthtimespec; +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ ts = *tsp; /* copy so we can muck with it */ small = (sizeof(ts.tv_sec) == 4); data = ts.tv_sec; @@ -608,69 +686,76 @@ format1(const struct stat *st, tm = localtime(&ts.tv_sec); (void)strftime(path, sizeof(path), timefmt, tm); sdata = path; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX | - FMT_FLOAT | FMT_STRING; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_FLOAT | FMTF_STRING; if (ofmt == 0) - ofmt = FMT_DECIMAL; + ofmt = FMTF_DECIMAL; break; case SHOW_st_size: small = (sizeof(st->st_size) == 4); data = st->st_size; sdata = NULL; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; case SHOW_st_blocks: small = (sizeof(st->st_blocks) == 4); data = st->st_blocks; sdata = NULL; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; case SHOW_st_blksize: small = (sizeof(st->st_blksize) == 4); data = st->st_blksize; sdata = NULL; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; +#if HAVE_STRUCT_STAT_ST_FLAGS case SHOW_st_flags: small = (sizeof(st->st_flags) == 4); data = st->st_flags; sdata = NULL; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ +#if HAVE_STRUCT_STAT_ST_GEN case SHOW_st_gen: small = (sizeof(st->st_gen) == 4); data = st->st_gen; sdata = NULL; - formats = FMT_DECIMAL | FMT_OCTAL | FMT_UNSIGNED | FMT_HEX; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; if (ofmt == 0) - ofmt = FMT_UNSIGNED; + ofmt = FMTF_UNSIGNED; break; +#endif /* HAVE_STRUCT_STAT_ST_GEN */ case SHOW_symlink: small = 0; data = 0; if (S_ISLNK(st->st_mode)) { snprintf(path, sizeof(path), " -> "); - l = readlink(file, path + 4, sizeof(path) - 4); + l = readlink(file, path + 4, sizeof(path) - 4 - 1); if (l == -1) { + linkfail = 1; l = 0; path[0] = '\0'; } path[l + 4] = '\0'; - sdata = path + (ofmt == FMT_STRING ? 0 : 4); + sdata = path + (ofmt == FMTF_STRING ? 0 : 4); } - else + else { + linkfail = 1; sdata = ""; - formats = FMT_STRING; + } + formats = FMTF_STRING; if (ofmt == 0) - ofmt = FMT_STRING; + ofmt = FMTF_STRING; break; case SHOW_filetype: small = 0; @@ -688,7 +773,12 @@ format1(const struct stat *st, break; case S_IFLNK: (void)strcat(sdata, "@"); break; case S_IFSOCK: (void)strcat(sdata, "="); break; +#ifdef S_IFWHT case S_IFWHT: (void)strcat(sdata, "%"); break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: (void)strcat(sdata, ">"); break; +#endif /* S_IFDOOR */ } hilo = 0; } @@ -701,14 +791,19 @@ format1(const struct stat *st, case S_IFREG: sdata = "Regular File"; break; case S_IFLNK: sdata = "Symbolic Link"; break; case S_IFSOCK: sdata = "Socket"; break; +#ifdef S_IFWHT case S_IFWHT: sdata = "Whiteout File"; break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: sdata = "Door"; break; +#endif /* S_IFDOOR */ default: sdata = "???"; break; } hilo = 0; } - formats = FMT_STRING; + formats = FMTF_STRING; if (ofmt == 0) - ofmt = FMT_STRING; + ofmt = FMTF_STRING; break; case SHOW_filename: small = 0; @@ -718,9 +813,9 @@ format1(const struct stat *st, else (void)strncpy(path, file, sizeof(path)); sdata = path; - formats = FMT_STRING; + formats = FMTF_STRING; if (ofmt == 0) - ofmt = FMT_STRING; + ofmt = FMTF_STRING; break; case SHOW_sizerdev: if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { @@ -782,7 +877,7 @@ format1(const struct stat *st, * Only the timespecs support the FLOAT output format, and that * requires work that differs from the other formats. */ - if (ofmt == FMT_FLOAT) { + if (ofmt == FMTF_FLOAT) { /* * Nothing after the decimal point, so just print seconds. */ @@ -862,7 +957,7 @@ format1(const struct stat *st, /* * String output uses the temporary sdata. */ - if (ofmt == FMT_STRING) { + if (ofmt == FMTF_STRING) { if (sdata == NULL) errx(1, "%.*s: bad format", (int)flen, fmt); (void)strcat(lfmt, "s"); @@ -873,7 +968,7 @@ format1(const struct stat *st, * Ensure that sign extension does not cause bad looking output * for some forms. */ - if (small && ofmt != FMT_DECIMAL) + if (small && ofmt != FMTF_DECIMAL) data = (u_int32_t)data; /* @@ -881,10 +976,10 @@ format1(const struct stat *st, */ (void)strcat(lfmt, "ll"); switch (ofmt) { - case FMT_DECIMAL: (void)strcat(lfmt, "d"); break; - case FMT_OCTAL: (void)strcat(lfmt, "o"); break; - case FMT_UNSIGNED: (void)strcat(lfmt, "u"); break; - case FMT_HEX: (void)strcat(lfmt, "x"); break; + case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; + case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; + case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; + case FMTF_HEX: (void)strcat(lfmt, "x"); break; } return (snprintf(buf, blen, lfmt, data));