diff options
Diffstat (limited to 'usr.bin/stat')
-rw-r--r-- | usr.bin/stat/Makefile | 1 | ||||
-rw-r--r-- | usr.bin/stat/stat.1 | 104 | ||||
-rw-r--r-- | usr.bin/stat/stat.c | 118 |
3 files changed, 160 insertions, 63 deletions
diff --git a/usr.bin/stat/Makefile b/usr.bin/stat/Makefile index 808c41e..1a54979 100644 --- a/usr.bin/stat/Makefile +++ b/usr.bin/stat/Makefile @@ -1,7 +1,6 @@ # $FreeBSD$ PROG= stat -WARNS?= 2 LINKS= ${BINDIR}/stat ${BINDIR}/readlink MLINKS= stat.1 readlink.1 diff --git a/usr.bin/stat/stat.1 b/usr.bin/stat/stat.1 index 8bbdda4..92a8515 100644 --- a/usr.bin/stat/stat.1 +++ b/usr.bin/stat/stat.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: stat.1,v 1.11 2003/05/08 13:07:10 wiz Exp $ +.\" $NetBSD: stat.1,v 1.28 2010/04/05 21:25:01 joerg Exp $ .\" .\" Copyright (c) 2002 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -29,7 +29,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 24, 2010 +.Dd December 5, 2010 .Dt STAT 1 .Os .Sh NAME @@ -43,15 +43,15 @@ .Op Fl t Ar timefmt .Op Ar .Nm readlink -.Op Fl n +.Op Fl fn .Op Ar .Sh DESCRIPTION The .Nm utility displays information about the file pointed to by .Ar file . -Read, write or execute permissions of the named file are not required, but -all directories listed in the path name leading to the file must be +Read, write, or execute permissions of the named file are not required, but +all directories listed in the pathname leading to the file must be searchable. If no argument is given, .Nm @@ -60,13 +60,42 @@ displays information about the file descriptor for standard input. When invoked as .Nm readlink , only the target of the symbolic link is printed. -If the given argument is not a symbolic link, +If the given argument is not a symbolic link and the +.Fl f +option is not specified, .Nm readlink will print nothing and exit with an error. +If the +.Fl f +option is specified, the output is canonicalized by following every symlink +in every component of the given path recursively. +.Nm readlink +will resolve both absolute and relative paths, and return the absolute pathname +corresponding to +.Ar file . +In this case, the argument does not need to be a symbolic link. .Pp The information displayed is obtained by calling .Xr lstat 2 with the given argument and evaluating the returned structure. +The default format displays the +.Fa st_dev , +.Fa st_ino , +.Fa st_mode , +.Fa st_nlink , +.Fa st_uid , +.Fa st_gid , +.Fa st_rdev , +.Fa st_size , +.Fa st_atime , +.Fa st_mtime , +.Fa st_ctime , +.Fa st_birthtime , +.Fa st_blksize , +.Fa st_blocks , +and +.Fa st_flags +fields, in that order. .Pp The options are as follows: .Bl -tag -width indent @@ -107,6 +136,14 @@ will refer to the target of if file is a symbolic link, and not to .Ar file itself. +If the link is broken or the target does not exist, +fall back on +.Xr lstat 2 +and report information about the link. +.It Fl l +Display output in +.Ic ls Fl lT +format. .It Fl n Do not force a newline to appear at the end of each piece of output. .It Fl q @@ -136,7 +173,8 @@ display the raw, numerical value (for example, times in seconds since the epoch, etc.). .It Fl s Display information in -.Dq "shell output" , +.Dq shell output +format, suitable for initializing variables. .It Fl x Display information in a more verbose way as known from some @@ -334,49 +372,62 @@ A required field specifier, being one of the following: .It Cm d Device upon which .Ar file -resides. +resides +.Pq Fa st_dev . .It Cm i .Ar file Ns 's -inode number. +inode number +.Pq Fa st_ino . .It Cm p -File type and permissions. +File type and permissions +.Pq Fa st_mode . .It Cm l Number of hard links to -.Ar file . +.Ar file +.Pq Fa st_nlink . .It Cm u , g User ID and group ID of .Ar file Ns 's -owner. +owner +.Pq Fa st_uid , st_gid . .It Cm r -Device number for character and block device special files. +Device number for character and block device special files +.Pq Fa st_rdev . .It Cm a , m , c , B The time .Ar file -was last accessed or modified, of when the inode was last changed, or -the birth time of the inode. +was last accessed or modified, or when the inode was last changed, or +the birth time of the inode +.Pq Fa st_atime , st_mtime , st_ctime , st_birthtime . .It Cm z The size of .Ar file -in bytes. +in bytes +.Pq Fa st_size . .It Cm b Number of blocks allocated for -.Ar file . +.Ar file +.Pq Fa st_blocks . .It Cm k -Optimal file system I/O operation block size. +Optimal file system I/O operation block size +.Pq Fa st_blksize . .It Cm f User defined flags for .Ar file . .It Cm v -Inode generation number. +Inode generation number +.Pq Fa st_gen . .El .Pp -The following four field specifiers are not drawn directly from the +The following five field specifiers are not drawn directly from the data in .Vt "struct stat" , but are: .Bl -tag -width indent .It Cm N The name of the file. +.It Cm R +The absolute pathname corresponding to the file. .It Cm T The file type, either as in .Nm ls Fl F @@ -406,12 +457,12 @@ as an output form, with the exception of .Cm p which defaults to -.Cm O , +.Cm O ; .Cm a , m , and .Cm c which default to -.Cm D , +.Cm D ; and .Cm Y , T , and @@ -421,8 +472,15 @@ which default to .Sh EXIT STATUS .Ex -std stat readlink .Sh EXAMPLES +If no options are specified, the default format is +"%d %i %Sp %l %Su %Sg %r %z \e"%Sa\e" \e"%Sm\e" \e"%Sc\e" \e"%SB\e" %k %b %#Xf %N". +.Bd -literal -offset indent +\*[Gt] stat /tmp/bar +0 78852 -rw-r--r-- 1 root wheel 0 0 "Jul 8 10:26:03 2004" "Jul 8 10:26:03 2004" "Jul 8 10:28:13 2004" "Jan 1 09:00:00 1970" 16384 0 0 /tmp/bar +.Ed +.Pp Given a symbolic link -.Pa foo +.Dq foo that points from .Pa /tmp/foo to diff --git a/usr.bin/stat/stat.c b/usr.bin/stat/stat.c index 1ab10ea..950c76a 100644 --- a/usr.bin/stat/stat.c +++ b/usr.bin/stat/stat.c @@ -30,7 +30,7 @@ #include <sys/cdefs.h> #if 0 #ifndef lint -__RCSID("$NetBSD: stat.c,v 1.13 2003/07/25 03:21:17 atatat Exp $"); +__RCSID("$NetBSD: stat.c,v 1.30 2010/11/25 04:33:30 dholland Exp $"); #endif #endif @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <ctype.h> #include <err.h> +#include <errno.h> #include <grp.h> #include <limits.h> #include <paths.h> @@ -152,6 +153,7 @@ __FBSDID("$FreeBSD$"); #define MIDDLE_PIECE 'M' #define LOW_PIECE 'L' +#define SHOW_realpath 'R' #define SHOW_st_dev 'd' #define SHOW_st_ino 'i' #define SHOW_st_mode 'p' @@ -175,7 +177,7 @@ __FBSDID("$FreeBSD$"); void usage(const char *); void output(const struct stat *, const char *, - const char *, int, int, int); + const char *, int, int); int format1(const struct stat *, /* stat info */ const char *, /* the file name */ const char *, int, /* the format string itself */ @@ -186,7 +188,7 @@ int format1(const struct stat *, /* stat info */ char *xfflagstostr(unsigned long); #endif -char *timefmt; +const char *timefmt; int linkfail; #define addchar(s, c, nl) \ @@ -201,7 +203,7 @@ main(int argc, char *argv[]) struct stat st; int ch, rc, errs, am_readlink; int lsF, fmtchar, usestat, fn, nonl, quiet; - char *statfmt, *options, *synopsis; + const char *statfmt, *options, *synopsis; char dname[sizeof _PATH_DEV + SPECNAMELEN] = _PATH_DEV; const char *file; @@ -217,8 +219,8 @@ main(int argc, char *argv[]) if (strcmp(getprogname(), "readlink") == 0) { am_readlink = 1; - options = "n"; - synopsis = "[-n] [file ...]"; + options = "fn"; + synopsis = "[-fn] [file ...]"; statfmt = "%Y"; fmtchar = 'f'; quiet = 1; @@ -242,6 +244,10 @@ main(int argc, char *argv[]) quiet = 1; break; case 'f': + if (am_readlink) { + statfmt = "%R"; + break; + } statfmt = optarg; /* FALLTHROUGH */ case 'l': @@ -313,8 +319,17 @@ main(int argc, char *argv[]) rc = fstat(STDIN_FILENO, &st); } else { file = argv[0]; - if (usestat) - rc = stat(file, &st); + if (usestat) { + /* + * Try stat() and if it fails, fall back to + * lstat() just in case we're examining a + * broken symlink. + */ + if ((rc = stat(file, &st)) == -1 && + errno == ENOENT && + (rc = lstat(file, &st)) == -1) + errno = ENOENT; + } else rc = lstat(file, &st); } @@ -326,7 +341,7 @@ main(int argc, char *argv[]) warn("%s: stat", file); } else - output(&st, file, statfmt, fn, nonl, quiet); + output(&st, file, statfmt, fn, nonl); argv++; argc--; @@ -368,10 +383,10 @@ usage(const char *synopsis) */ void output(const struct stat *st, const char *file, - const char *statfmt, int fn, int nonl, int quiet) + const char *statfmt, int fn, int nonl) { int flags, size, prec, ofmt, hilo, what; - char buf[PATH_MAX]; + char buf[PATH_MAX + 4 + 1]; const char *subfmt; int nl, t, i; @@ -508,6 +523,7 @@ output(const struct stat *st, const char *file, } switch (*statfmt) { + fmtcase(what, SHOW_realpath); fmtcase(what, SHOW_st_dev); fmtcase(what, SHOW_st_ino); fmtcase(what, SHOW_st_mode); @@ -540,7 +556,7 @@ output(const struct stat *st, const char *file, buf, sizeof(buf), flags, size, prec, ofmt, hilo, what); - for (i = 0; i < t && i < sizeof(buf); i++) + for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++) addchar(stdout, buf[i], &nl); continue; @@ -567,7 +583,8 @@ format1(const struct stat *st, int hilo, int what) { u_int64_t data; - char *sdata, lfmt[24], tmp[20]; + char *stmp, lfmt[24], tmp[20]; + const char *sdata; char smode[12], sid[12], path[PATH_MAX + 4]; struct passwd *pw; struct group *gr; @@ -628,28 +645,29 @@ format1(const struct stat *st, small = (sizeof(st->st_mode) == 4); data = st->st_mode; strmode(st->st_mode, smode); - sdata = smode; - l = strlen(sdata); - if (sdata[l - 1] == ' ') - sdata[--l] = '\0'; + stmp = smode; + l = strlen(stmp); + if (stmp[l - 1] == ' ') + stmp[--l] = '\0'; if (hilo == HIGH_PIECE) { data >>= 12; - sdata += 1; - sdata[3] = '\0'; + stmp += 1; + stmp[3] = '\0'; hilo = 0; } else if (hilo == MIDDLE_PIECE) { data = (data >> 9) & 07; - sdata += 4; - sdata[3] = '\0'; + stmp += 4; + stmp[3] = '\0'; hilo = 0; } else if (hilo == LOW_PIECE) { data &= 0777; - sdata += 7; - sdata[3] = '\0'; + stmp += 7; + stmp[3] = '\0'; hilo = 0; } + sdata = stmp; formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | FMTF_STRING; if (ofmt == 0) @@ -710,7 +728,6 @@ format1(const struct stat *st, ts = *tsp; /* copy so we can muck with it */ small = (sizeof(ts.tv_sec) == 4); data = ts.tv_sec; - small = 1; tm = localtime(&ts.tv_sec); (void)strftime(path, sizeof(path), timefmt, tm); sdata = path; @@ -766,6 +783,26 @@ format1(const struct stat *st, ofmt = FMTF_UNSIGNED; break; #endif /* HAVE_STRUCT_STAT_ST_GEN */ + case SHOW_realpath: + small = 0; + data = 0; + if (file == NULL) { + (void)strncpy(path, "(stdin)", sizeof(path)); + sdata = path; + } else { + snprintf(path, sizeof(path), " -> "); + if (realpath(file, path + 4) == NULL) { + linkfail = 1; + l = 0; + path[0] = '\0'; + } + sdata = path + (ofmt == FMTF_STRING ? 0 : 4); + } + + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; case SHOW_symlink: small = 0; data = 0; @@ -791,24 +828,23 @@ format1(const struct stat *st, case SHOW_filetype: small = 0; data = 0; - sdata = smode; - sdata[0] = '\0'; + sdata = ""; if (hilo == 0 || hilo == LOW_PIECE) { switch (st->st_mode & S_IFMT) { - case S_IFIFO: (void)strcat(sdata, "|"); break; - case S_IFDIR: (void)strcat(sdata, "/"); break; + case S_IFIFO: sdata = "|"; break; + case S_IFDIR: sdata = "/"; break; case S_IFREG: if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) - (void)strcat(sdata, "*"); + sdata = "*"; break; - case S_IFLNK: (void)strcat(sdata, "@"); break; - case S_IFSOCK: (void)strcat(sdata, "="); break; + case S_IFLNK: sdata = "@"; break; + case S_IFSOCK: sdata = "="; break; #ifdef S_IFWHT - case S_IFWHT: (void)strcat(sdata, "%"); break; + case S_IFWHT: sdata = "%"; break; #endif /* S_IFWHT */ #ifdef S_IFDOOR - case S_IFDOOR: (void)strcat(sdata, ">"); break; + case S_IFDOOR: sdata = ">"; break; #endif /* S_IFDOOR */ } hilo = 0; @@ -914,8 +950,9 @@ format1(const struct stat *st, (void)snprintf(tmp, sizeof(tmp), "%d", size); (void)strcat(lfmt, tmp); } - (void)strcat(lfmt, "d"); - return (snprintf(buf, blen, lfmt, ts.tv_sec)); + (void)strcat(lfmt, "lld"); + return (snprintf(buf, blen, lfmt, + (long long)ts.tv_sec)); } /* @@ -938,7 +975,8 @@ format1(const struct stat *st, (void)snprintf(tmp, sizeof(tmp), "%d", size); (void)strcat(lfmt, tmp); } - (void)strcat(lfmt, "d"); + /* Seconds: time_t cast to long long. */ + (void)strcat(lfmt, "lld"); /* * The stuff after the decimal point always needs zero @@ -949,8 +987,10 @@ format1(const struct stat *st, /* * We can "print" at most nine digits of precision. The * rest we will pad on at the end. + * + * Nanoseconds: long. */ - (void)snprintf(tmp, sizeof(tmp), "%dd", prec > 9 ? 9 : prec); + (void)snprintf(tmp, sizeof(tmp), "%dld", prec > 9 ? 9 : prec); (void)strcat(lfmt, tmp); /* @@ -964,8 +1004,8 @@ format1(const struct stat *st, * Use the format, and then tack on any zeroes that * might be required to make up the requested precision. */ - l = snprintf(buf, blen, lfmt, ts.tv_sec, ts.tv_nsec); - for (; prec > 9 && l < blen; prec--, l++) + l = snprintf(buf, blen, lfmt, (long long)ts.tv_sec, ts.tv_nsec); + for (; prec > 9 && l < (int)blen; prec--, l++) (void)strcat(buf, "0"); return (l); } |