minix/commands/stat/stat.c
Evgeniy Ivanov ef0a265086 New stat structure.
* VFS and installed MFSes must be in sync before and after this change *

Use struct stat from NetBSD. It requires adding new STAT, FSTAT and LSTAT
syscalls. Libc modification is both backward and forward compatible.

Also new struct stat uses modern field sizes to avoid ABI
incompatibility, when we update uid_t, gid_t and company.
Exceptions are ino_t and off_t in old libc (though paddings added).
2011-07-12 16:39:55 +02:00

389 lines
9.7 KiB
C

/* stat.c Feb 1987 - main, printit, statit
*
* stat - a program to perform what the stat(2) call does.
*
* usage: stat [-] [-all] -<field> [-<field> ...] [file1 file2 file3 ...]
*
* where <field> is one of the struct stat fields without the leading "st_".
* The three times can be printed out as human times by requesting
* -Ctime instead of -ctime (upper case 1st letter).
* - means take the file names from stdin.
* -0.. means fd0..
* no files means all fds.
*
* output: if only one field is specified, that fields' contents are printed.
* if more than one field is specified, the output is
* file filed1: f1val, field2: f2val, etc
*
* written: Larry McVoy, (mcvoy@rsch.wisc.edu)
*/
# define ALLDEF /* Make -all default. (kjb) */
# include <sys/types.h>
# include <errno.h>
# include <limits.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <time.h>
# include <sys/stat.h>
# define addr(x) ((void*) &sbuf.x)
# define size(x) sizeof(sbuf.x)
# define equal(s, t) (strcmp(s, t) == 0)
# ifndef PATH_MAX
# define PATH_MAX 1024
# endif
# undef LS_ADDS_SPACE /* AT&T Unix PC, ls prints "file[* /]" */
/* This makes stat fail. */
# ifndef _MINIX /* All but Minix have u_* and st_blocks */
# define BSD
# endif
# ifndef S_IREAD
# define S_IREAD S_IRUSR
# define S_IWRITE S_IWUSR
# define S_IEXEC S_IXUSR
# endif
char * arg0;
struct stat sbuf;
extern int errno;
int first_file= 1;
#ifndef S_IFLNK
#define lstat stat
#endif
struct field {
char* f_name; /* field name in stat */
u_char* f_addr; /* address of the field in sbuf */
u_short f_size; /* size of the object, needed for pointer arith */
u_short f_print; /* show this field? */
} fields[] = {
{ "dev", addr(st_dev), size(st_dev), 0 },
{ "ino", addr(st_ino), size(st_ino), 0 },
{ "mode", addr(st_mode), size(st_mode), 0 },
{ "nlink", addr(st_nlink), size(st_nlink), 0 },
{ "uid", addr(st_uid), size(st_uid), 0 },
{ "gid", addr(st_gid), size(st_gid), 0 },
{ "rdev", addr(st_rdev), size(st_rdev), 0 },
{ "size", addr(st_size), size(st_size), 0 },
{ "Atime", addr(st_atime), size(st_atime), 0 },
{ "atime", addr(st_atime), size(st_atime), 0 },
{ "Mtime", addr(st_mtime), size(st_mtime), 0 },
{ "mtime", addr(st_mtime), size(st_mtime), 0 },
{ "Ctime", addr(st_ctime), size(st_ctime), 0 },
{ "ctime", addr(st_ctime), size(st_ctime), 0 },
{ "blksize", addr(st_blksize), size(st_blksize), 0 },
{ "blocks", addr(st_blocks), size(st_blocks), 0 },
{ NULL, 0, 0, 0 },
};
void printstat(struct stat *sbuf, int nprint);
void printit(struct stat* sb, struct field* f, int n);
void rwx(mode_t mode, char *bit);
void usage(void);
int do_readlink=0;
int main(int ac, char** av)
{
int i, j, nprint = 0, files = 0;
char buf[PATH_MAX], *check;
int sym=0, ret=0, from_stdin = 0;
int err;
u_long fd;
if ((arg0 = strrchr(av[0], '/')) == NULL) arg0 = av[0]; else arg0++;
#ifdef S_IFLNK
if (equal(arg0, "lstat")) sym = 1;
if (equal(arg0, "readlink")) do_readlink = 1;
#endif
if (ac > 1 && equal(av[i = 1], "-"))
i++, from_stdin++;
for (i= 1; i<ac; i++) {
if (av[i][0] == '-') {
if (equal(av[i], "-")) {
from_stdin= 1;
files++;
continue;
}
if (equal("-all", av[i])) {
for (j=0; fields[j].f_name; j++)
nprint++, fields[j].f_print++;
continue;
}
if (equal("-s", av[i])) {
#ifdef S_IFLNK
sym=1;
#endif
continue;
}
fd = strtoul(av[i]+1, &check, 0);
if (check != av[i]+1 && *check == '\0')
{
files++;
continue;
}
for (j=0; fields[j].f_name; j++)
if (equal(fields[j].f_name, &av[i][1])) {
nprint++, fields[j].f_print++;
break;
}
if (!fields[j].f_name) {
if (!equal("-?", av[i])) {
fprintf(stderr, "stat: %s: bad field\n", av[i]);
}
usage();
}
}
else
files++;
}
if (!nprint) {
# ifndef ALLDEF
usage();
# else
for (j=0; fields[j].f_name; j++)
nprint++, fields[j].f_print++;
# endif
}
if (from_stdin)
files++; /* We don't know how many files come from stdin. */
if (files == 0) { /* Stat all file descriptors. */
if(do_readlink) return 0;
for (i= 0; i<OPEN_MAX; i++) {
err= fstat(i, &sbuf);
if (err == -1 && errno == EBADF)
continue;
if (err == 0) {
if (!first_file) fputc('\n', stdout);
printf("fd %d:\n", i);
printstat(&sbuf, nprint);
}
else {
fprintf(stderr, "%s: fd %d: %s\n", arg0, i, strerror(errno));
ret= 1;
}
}
exit(ret);
}
for (i=1; i<ac; i++) {
if (equal(av[i], "-")) {
while (fgets(buf, sizeof(buf), stdin)) {
char *p= strchr(buf, '\n');
if (p) *p= 0;
#ifdef S_IFLNK
if(do_readlink) {
char sbuf[300];
int n;
if((n=readlink(buf, sbuf, sizeof(sbuf)-1)) < 0) {
perror(buf);
continue;
}
sbuf[n] = '\0';
printf("%s: %s\n", buf, sbuf);
continue;
}
#endif
if (!sym) err= stat(av[i], &sbuf);
if (sym || (err != 0 && errno == ENOENT)) {
err= lstat(av[i], &sbuf);
}
if (err == -1) {
fprintf(stderr, "%s: %s: %s\n",
arg0, av[i], strerror(errno));
ret= 1;
}
else {
if (!first_file) fputc('\n', stdout);
printf("%s:\n", buf);
printstat(&sbuf, nprint);
}
}
continue;
}
if (av[i][0] == '-') {
fd= strtoul(av[i]+1, &check, 10);
if (check == av[i]+1 || *check != '\0') continue;
if (fd >= INT_MAX) {
err= -1;
errno= EBADF;
}
else {
err= fstat((int) fd, &sbuf);
}
if (err != -1) {
if (!first_file) fputc('\n', stdout);
if (files != 1) printf("fd %lu:\n", fd);
printstat(&sbuf, nprint);
}
else {
fprintf(stderr, "fd %lu: %s\n", fd, strerror(errno));
ret= 1;
}
continue;
}
if(do_readlink) {
char sbuf[300];
int n;
if((n=err=readlink(av[i], sbuf, sizeof(sbuf)-1)) < 0) {
perror(av[i]);
continue;
}
sbuf[n] = '\0';
printf("%s: %s\n", av[i], sbuf);
continue;
}
if (!sym) err= stat(av[i], &sbuf);
if (sym || (err != 0 && errno == ENOENT)) err= lstat(av[i], &sbuf);
if (err != -1) {
if (!first_file) fputc('\n', stdout);
if (files != 1) printf("%s:\n", av[i]);
printstat(&sbuf, nprint);
}
else {
fprintf(stderr, "%s: %s: %s\n", arg0, av[i], strerror(errno));
ret= 1;
}
}
exit(ret);
}
/*------------------------------------------------------------------30/Jan/87-*
* printstat(file, nprint) - do the work
*----------------------------------------------------------------larry mcvoy-*/
void printstat(struct stat *sbuf, int nprint)
{
int j;
int first_field= 1;
for (j=0; fields[j].f_name; j++) {
if (fields[j].f_print) {
if (!first_field) fputc('\n', stdout);
printit(sbuf, &fields[j], nprint);
first_field= 0;
}
}
fputc('\n', stdout);
first_file= 0;
}
/*------------------------------------------------------------------30/Jan/87-*
* printit(sb, f, n) - print the field
*
* Inputs -> (struct stat*), (struct field*), (int)
*
* Results -> Displays the field, with special handling of weird fields like
* mode and mtime. The mode field is dumped in octal, followed
* by one or more of the S_IF<X> and/or S_I<X> values.
*----------------------------------------------------------------larry mcvoy-*/
void printit(struct stat* sb, struct field* f, int n)
{
if (n > 1)
printf("%s: ", f->f_name);
if (equal(f->f_name, "mode")) {
/* This lot changed to my personal liking. (kjb) */
char bit[11];
printf("%07lo, ", (u_long) sb->st_mode);
strcpy(bit, "----------");
switch (sb->st_mode&S_IFMT) {
case S_IFDIR: bit[0]='d'; break;
# ifdef S_IFFIFO
case S_IFFIFO: bit[0]='p'; break;
# endif
case S_IFCHR: bit[0]='c'; break;
case S_IFBLK: bit[0]='b'; break;
# ifdef S_IFSOCK
case S_IFSOCK: bit[0]='S'; break;
# endif
# ifdef S_IFMPC
case S_IFMPC: bit[0]='C'; break;
# endif
# ifdef S_IFMPB
case S_IFMPB: bit[0]='B'; break;
# endif
# ifdef S_IFLNK
case S_IFLNK: bit[0]='l'; break;
# endif
}
rwx(sb->st_mode, bit+1);
rwx(sb->st_mode<<3, bit+4);
rwx(sb->st_mode<<6, bit+7);
if (sb->st_mode&S_ISUID) bit[3]='s';
if (sb->st_mode&S_ISGID) bit[6]='s';
if (sb->st_mode&S_ISVTX) bit[9]='t';
printf("\"%s\"", bit);
}
/* times in human form, uppercase first letter */
else if (equal("Ctime", f->f_name)) {
printf("%.24s (%lu)", ctime(&sb->st_ctime), (u_long) sb->st_ctime);
f[1].f_print= 0;
}
else if (equal("Mtime", f->f_name)) {
printf("%.24s (%lu)", ctime(&sb->st_mtime), (u_long) sb->st_mtime);
f[1].f_print= 0;
}
else if (equal("Atime", f->f_name)) {
printf("%.24s (%lu)", ctime(&sb->st_atime), (u_long) sb->st_atime);
f[1].f_print= 0;
}
else if (equal("ctime", f->f_name)) {
printf("%lu", (u_long) sb->st_ctime);
}
else if (equal("mtime", f->f_name)) {
printf("%lu", (u_long) sb->st_mtime);
}
else if (equal("atime", f->f_name)) {
printf("%lu", (u_long) sb->st_atime);
}
else {
switch (f->f_size) {
case sizeof(char):
printf("%d", * (u_char *) f->f_addr);
break;
case sizeof(short):
printf("%u", (u_int) * (u_short *) f->f_addr);
break;
#if INT_MAX != SHRT_MAX
case sizeof(int):
printf("%u", * (u_int *) f->f_addr);
break;
#endif
#if LONG_MAX != INT_MAX && LONG_MAX != SHRT_MAX
case sizeof(long):
printf("%lu", * (u_long *) f->f_addr);
break;
#endif
default:
fprintf(stderr, "\nProgram error: bad '%s' field size %d\n",
f->f_name, f->f_size);
break;
}
}
}
void rwx(mode_t mode, char *bit)
{
if (mode&S_IREAD) bit[0]='r';
if (mode&S_IWRITE) bit[1]='w';
if (mode&S_IEXEC) bit[2]='x';
}
void usage(void)
{
fprintf(stderr,
"Usage: %s [-] [-fd] [-all] [-s] [-field ...] [file1 ...]\n",
arg0);
exit(1);
}