From 5b812a140dd8247062b1d9d0b95a8bf5ad404528 Mon Sep 17 00:00:00 2001 From: Doug Barton Date: Tue, 13 Aug 2002 08:23:49 +0000 Subject: [PATCH] A cooperative effort... 1. Update the code to the latest from NetBSD, which includes: * A new command line option to suppress stat(2) errors * Output is now done via stdio * Fixes for bitwise OR'ing of letters 2. Andrew from NetBSD merged in our own Johan's readlink patches. 3. Andrew also merged in some brucifications. I made some small contributions to the header cleanup, and tried to generally improve the overall style(9)'ishness. I also documented the new -q option and added the *LINKS to the Makefile. All responsibility for mistakes in merging the contributions from the above is mine. Obtained from: Andrew Brown , bde, johan --- usr.bin/stat/Makefile | 3 + usr.bin/stat/stat.1 | 19 +++- usr.bin/stat/stat.c | 230 ++++++++++++++++++++++++------------------ 3 files changed, 151 insertions(+), 101 deletions(-) diff --git a/usr.bin/stat/Makefile b/usr.bin/stat/Makefile index 0c95721859c0..d33f0bfe634c 100644 --- a/usr.bin/stat/Makefile +++ b/usr.bin/stat/Makefile @@ -5,4 +5,7 @@ MAN= stat.1 SRCS= stat.c WARNS?= 2 +LINKS= ${BINDIR}/stat ${BINDIR}/readlink +MLINKS= stat.1 readlink.1 + .include diff --git a/usr.bin/stat/stat.1 b/usr.bin/stat/stat.1 index cd238233e1f3..c6bc6b48ab0e 100644 --- a/usr.bin/stat/stat.1 +++ b/usr.bin/stat/stat.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: stat.1,v 1.4 2002/05/09 17:52:03 atatat Exp $ +.\" $NetBSD: stat.1,v 1.5 2002/07/08 18:48:42 atatat Exp $ .\" .\" Copyright (c) 2002 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -40,7 +40,8 @@ .Dt STAT 1 .Os .Sh NAME -.Nm stat +.Nm stat , +.Nm readlink .Nd display file status .Sh SYNOPSIS .Nm @@ -48,12 +49,16 @@ .Oo .Fl f Ar format | .Fl l | +.Fl q | .Fl r | .Fl s | .Fl x .Oc .Op Fl t Ar timefmt .Op Ar +.Nm readlink +.Op Fl n +.Op Ar .Sh DESCRIPTION The .Nm @@ -65,6 +70,13 @@ searchable. If no argument is given, .Nm displays information about the file descriptor for standard input. .Pp +When invoked as +.Nm readlink , +only the target of the symbolic link is printed. If the given argument +is not a symbolic link, +.Nm readlink +will print nothing and exit with an error. +.Pp The information displayed is obtained by calling .Xr lstat 2 with the given argument and evaluating the returned structure. @@ -102,6 +114,9 @@ for a description of valid formats. Display output in .Ic ls Fl lT format. +.It Fl q +Suppress failure messages from +.Xr stat 2 . .It Fl r Display raw information. That is, for all the fields in the stat-structure, display the raw, numerical value (for example, times in seconds since the diff --git a/usr.bin/stat/stat.c b/usr.bin/stat/stat.c index d8810eeaf115..5d5f2a8dfffd 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.6 2002/07/09 21:25:00 atatat Exp $ */ /* * Copyright (c) 2002 The NetBSD Foundation, Inc. @@ -36,24 +36,27 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include +#if 0 #ifndef lint -__RCSID("$NetBSD: stat.c,v 1.3 2002/05/31 16:45:16 atatat Exp $"); -__FBSDID("$FreeBSD$"); +__RCSID("$NetBSD: stat.c,v 1.6 2002/07/09 21:25:00 atatat Exp $"); +#endif #endif -#include -#include +#include +__FBSDID("$FreeBSD$"); + #include -#include -#include -#include -#include +#include + #include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include #define DEF_FORMAT \ "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" %k %b %N" @@ -106,6 +109,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' @@ -130,9 +140,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 +151,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 +200,9 @@ main(int argc, char *argv[]) case 'n': nonl = 1; break; + case 'q': + quiet = 1; + break; case 'f': statfmt = optarg; /* FALLTHROUGH */ @@ -192,7 +219,7 @@ main(int argc, char *argv[]) timefmt = optarg; break; default: - usage(); + usage(synopsis); } argc -= optind; @@ -230,7 +257,7 @@ main(int argc, char *argv[]) timefmt = "%c"; break; default: - usage(); + usage(synopsis); /*NOTREACHED*/ } @@ -248,26 +275,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 +304,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 +335,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 +351,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 +420,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; @@ -438,16 +466,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 +485,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 +503,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; @@ -511,18 +540,18 @@ 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 | + FMTF_STRING; 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 +579,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 +601,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 +615,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; @@ -608,50 +637,50 @@ 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; 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; 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; case SHOW_symlink: small = 0; @@ -660,17 +689,20 @@ format1(const struct stat *st, snprintf(path, sizeof(path), " -> "); l = readlink(file, path + 4, sizeof(path) - 4); 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; @@ -706,9 +738,9 @@ format1(const struct stat *st, } 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 +750,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 +814,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 +894,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 +905,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 +913,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));