[net.sources] stat

rsk@pucc-k (Wombat) (07/13/85)

	This is the nth rewrite of stat(1), which I believe was originally
written by Phil Hochstetler (now of Sequent).  Essentially, it prints out
the structure returned by stat(2).  Defining SINCE will cause stat(1) to
print the difference between "now" and "then" for each of the three dates
associated with the inode--this is probably easy to demo than to explain.

	Here's a sample of the output:
-----
  File: "."
  Size: 2048         Allocated Blocks: 4            Filetype: Directory
  Mode: (0751/drwxr-x--x)         Uid: (18506/     rsk)  Gid: (    4/   staff)
Device: 5      Inode: 41167     Links: 3    
Access: Sat Jul 13 09:54:12 1985(00000.00:04:11)
Modify: Sat Jul 13 09:58:22 1985(00000.00:00:01)
Change: Sat Jul 13 09:58:22 1985(00000.00:00:01)
-----

	The times in ()'s are generated by the SINCE-defined code; note
that they are of the format ddddd.hh.mm.ss, which should be more than
sufficient to cover times back as far as the Epoch.

	Please report bugs to me; also please note that this is for 4.2bsd;
although I have little doubt that it would be easily adaptable to other
species--such mods are also welcome in my mailbox.

/*
 *	Stat.c		Dump out inode info in nice form.
 *			pur-ee!pucc-j!rsk, rsk@purdue-asc.arpa
 *			Original version by Hoch in the dark days of v6;
 *			this one by Rsk for 4.2 BSD.
 */

#include	<stdio.h>
#include	<time.h>
#include	<sys/types.h>
#include	<ctype.h>
#include	<sys/stat.h>
#include	<pwd.h>
#include	<grp.h>

#define	FAIL	-1		/* Failure return code from call */	
#define OKAY	0		/* Success return code from call */

struct 	stat	Sbuf;		/* for return values from stat() call */
char	*ctime();		/* Time conversion	*/
struct	passwd	*getpwuid();	/* User entry */
struct	passwd	*pwent;		/* User structure */
struct	group	*getgrgid();	/* Group entry */
struct	group	*grent;		/* Group structure */

char	Mode[10];	/* File protection mode */

#define	LBUFSIZ	256	/* Length for symbolic link translation buffer */
char	Lbuf[LBUFSIZ];	/* Symbolic link translation buffer */

main(argc, argv)
int argc;
char *argv[];
{
	int i;

	i = 1;
	if(argc == 1) {
		fprintf(stderr,"Usage: stat file1 [file2 ...]\n");
		exit(1);
	}

	do {
		stat_it(argv[i]);
		if(  (argc > 1) && (i < (argc-1)) )
			printf("\n");
	}
	while(++i < argc);

	exit(0);
}

/*
 * stat_it() - Actually stat and format results from file.
 *		exit -    OKAY if no problems encountered
 *			  FAIL if couldn't open or other nastiness
 *
 */
stat_it(filename)
char	*filename;
{
	int	count;

	if( lstat(filename,&Sbuf) == FAIL) {
		fprintf(stderr,"Can't lstat %s\n",filename); 
		return(FAIL);
	}

	if( (Sbuf.st_mode & S_IFMT) == S_IFLNK) {
		if( (count=readlink(filename,Lbuf,LBUFSIZ)) == FAIL) {
			fprintf(stderr,"Can't readlink %s\n", filename);
			return(FAIL);
		}
		if( count < LBUFSIZ)
			Lbuf[count] = '\0';
		printf("  File: \"%s\" -> \"%s\"\n",filename,Lbuf);
	}
	else
		printf("  File: \"%s\"\n", filename);

	printf("  Size: %-10d", Sbuf.st_size);
 	printf("   Allocated Blocks: %-10ld", Sbuf.st_blocks);

	printf("   Filetype: ");
	switch( Sbuf.st_mode & S_IFMT) {
		case	S_IFDIR:	printf("Directory\n");
					break;
		case	S_IFCHR:	printf("Character Device\n");
					break;
		case	S_IFBLK:	printf("Block Device\n");
					break;
		case	S_IFREG:	printf("Regular File\n");
					break;
		case	S_IFLNK:	printf("Symbolic Link\n");
					break;
		case	S_IFSOCK:	printf("Socket\n");
					break;
		default		:	printf("Unknown\n");
	}

	strcpy(Mode,"----------");
	if(Sbuf.st_mode & 0000001)	/* Other execute */
		Mode[9] = 'x';
	if(Sbuf.st_mode & 0000002)	/* Other write */
		Mode[8] = 'w';
	if(Sbuf.st_mode & 0000004)	/* Other read */
		Mode[7] = 'r';
	if(Sbuf.st_mode & 0000010)	/* Group execute */
		Mode[6] = 'x';
	if(Sbuf.st_mode & 0000020)	/* Group write */
		Mode[5] = 'w';
	if(Sbuf.st_mode & 0000040)	/* Group read */
		Mode[4] = 'r';
	if(Sbuf.st_mode & 0000100)	/* User execute */
		Mode[3] = 'x';
	if(Sbuf.st_mode & 0000200)	/* User write */
		Mode[2] = 'w';
	if(Sbuf.st_mode & 0000400)	/* User read */
		Mode[1] = 'r';
	if(Sbuf.st_mode & 0001000)	/* Sticky bit */
		Mode[9] = 't';
	if(Sbuf.st_mode & 0002000)	/* Set group id */
		Mode[6] = 's';
	if(Sbuf.st_mode & 0004000)	/* Set user id */
		Mode[4] = 's';
	switch( Sbuf.st_mode & S_IFMT) {
		case	S_IFDIR:	Mode[0] = 'd';
					break;
		case	S_IFCHR:	Mode[0] = 'c';
					break;
		case	S_IFBLK:	Mode[0] = 'b';
					break;
		case	S_IFREG:	Mode[0] = '-';
					break;
		case	S_IFLNK:	Mode[0] = 'l';
					break;
		case	S_IFSOCK:	Mode[0] = 's';
					break;
		default		:	Mode[0] = '?';
	}
	printf("  Mode: (%04o/%s)", Sbuf.st_mode&07777,Mode);


	(void) setpwent();
	if( (pwent = getpwuid(Sbuf.st_uid)) == NULL) {
		fprintf(stderr,"getpwuid() failed\n");
		exit(1);
	}
	printf("         Uid: (%5d/%8s)", Sbuf.st_uid, pwent->pw_name);

	(void) setgrent();
	if( (grent = getgrgid(Sbuf.st_gid)) == NULL) {
		fprintf(stderr,"getgrgid() failed\n");
		exit(1);
	}
	printf("  Gid: (%5d/%8s)\n", Sbuf.st_gid, grent->gr_name);


	printf("Device: %-5d", Sbuf.st_dev);
	printf("  Inode: %-10d", Sbuf.st_ino);
	printf("Links: %-5d", Sbuf.st_nlink);

	/* Only meaningful if file is device */

	if(  ( (Sbuf.st_mode & S_IFMT) == S_IFCHR)
		|| ( (Sbuf.st_mode & S_IFMT) == S_IFBLK) )
		printf("     Device type: %d\n",Sbuf.st_rdev);
	else
		printf("\n");

	/* The %.24s strips the newline from the ctime() string */

#ifdef SINCE
	printf("Access: %.24s",ctime(&Sbuf.st_atime));
	tsince(Sbuf.st_atime);
	printf("Modify: %.24s",ctime(&Sbuf.st_mtime));
	tsince(Sbuf.st_mtime);
	printf("Change: %.24s",ctime(&Sbuf.st_ctime));
	tsince(Sbuf.st_ctime);
#else SINCE
	printf("Access: %s",ctime(&Sbuf.st_atime));
	printf("Modify: %s",ctime(&Sbuf.st_mtime));
	printf("Change: %s",ctime(&Sbuf.st_ctime));
#endif SINCE

/*
* 	Should I put this in somewhere?  No.
*
* 	printf("Optimal Blocksize: %ld\n", Sbuf.st_blksize);
*/

	return(OKAY);
}

#ifdef SINCE
tsince(time_sec)
long	time_sec;
{
	long	time_buf;
	long	d_since;	/* days elapsed since time */
	long	h_since;	/* hours elapsed since time */
	long	m_since;	/* minutes elapsed since time */
	long	s_since;	/* seconds elapsed since time */

	time(&time_buf);

	if(time_sec > time_buf) {
		fprintf(stderr,"Time going backwards\n");
		exit(1);
	}

	s_since = time_buf - time_sec;
	d_since = s_since / 86400l ;
	s_since -= d_since * 86400l ;
	h_since = s_since / 3600l ;
	s_since -= h_since * 3600l ;
	m_since = s_since / 60l ;
	s_since -= m_since * 60l ;

	printf("(%05ld.%02ld:%02ld:%02ld)\n",d_since,h_since,m_since,s_since);

	return(OKAY);
}
#endif SINCE
-- 
Rich Kulawiec	rsk@{pur-ee,purdue}.uucp, rsk@purdue-asc.csnet
		rsk@purdue-asc.arpa (soon to be) rsk@asc.purdue.edu

tom@uwvax.UUCP (03/30/87)

In article <3391@rsch.WISC.EDU> mcvoy@rsch.WISC.EDU (Larry McVoy) writes:
>Hi,
>    Here's another screwdriver.  It's basically no more than a way to get at 
>stat struct while in the shell.  There are a few niceties, see the man page.
>
>
>Typical usage:
>
>% stat -atime *.c
>
>		P L E A S E ! !
>
>Don't flame me for this prog.  I know anyone could write it.  If you don't
>want it then don't take it.  It was useful to me, maybe it's useful to you.
>
>--larry

There are a few bugs in this program.  I fixed them and sent them
off to Larry, who said to go ahead and post the revised version 
and announce that this is the one you want to grab, if in fact you 
wish to grab one.

I have fixed the following things:
     * give correct exit stati; 0 means all ok, -1 means some error
     * give perror() messages for bad stats (missing files)
     * report file types corrects (larry messed this up)
     * give user, group, and other read/write/exec info

WARNING: I suspect that some non bsd cpp's won't tolerate a couple
		 of the macros I use, like:

		#define IS_MODE(FIELD) \
			if ((MODE(sb) & S_I/**/FIELD) == S_I/**/FIELD) printf(mask,"FIELD")

--tom

UUCP:	...!{harvard,ihnp4,seismo,topaz}!uwvax!tom
ARPA:	tom@{rsch,crys,gumby,pokey,limby,odin,puff,balder}.wisc.edu
USMAIL: Thomas Scott Christiansen
	System Manager, Department of Computer Sciences
	University of Wisconsin
	1210 W Dayton Street
	Madison, WI  53706
MABELL:	(608) 262-2389

=================== this is a the new stat.c file =============
/* stat.c Feb 1987 - main, sizecheck, 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).
 *	   - in av[1] means take the file names from stdin.
 *
 * 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)  
 *  
 * modified: Tom Christiansen on Sunday, 29 Mar 87 -- 13:02:49 CDT
 *  	-- give correct exit stati
 *	-- give perror() messages for bad stats
 *  	-- give correct file types
 *  	-- give user, group, and other read/write/exec info
 */

# include	<stdio.h>
# include	<sys/types.h>
# include	<sys/stat.h>
# define	addr(x)		(u_char*)(&sbuf.x)
# define	size(x)		sizeof(sbuf.x)
# define	equal(s, t)	(!strcmp(s, t))
# define	careful()	sizecheck()	/**/
# define	MAXPATH		500
# define	LS_ADDS_SPACE	/* AT&T Unix PC, ls prints "file[ */]" */
				/* This makes stat fail. */

# ifndef S_IFSOCK		/* sys v doesn't do this, I guess */
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned long u_long;
# else
#   define BSD
# endif

struct stat sbuf;
extern int  errno;
char* 	    ctime();

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, },
    { "Mtime",		addr(st_mtime),		size(st_mtime),		0, },
    { "Ctime",		addr(st_ctime),		size(st_ctime),		0, },
    { "atime",		addr(st_atime),		size(st_atime),		0, },
    { "mtime",		addr(st_mtime),		size(st_mtime),		0, },
    { "ctime",		addr(st_ctime),		size(st_ctime),		0, },
# ifdef BSD
    { "blksize", 	addr(st_blksize),	size(st_blksize),	0, },
    { "blocks",		addr(st_blocks),	size(st_blocks),	0, },
# endif
    { NULL,		  0,			0, },
};
    
main(ac, av)
    char** av;
{
    register i, j, nprint = 0, files = 0;
    char     buf[MAXPATH];
    int      ret, from_stdin = 0;

    careful();

    if (equal(av[i = 1], "-"))
	i++, from_stdin++;

    for ( ; i<ac; i++)  {
	if (av[i][0] == '-')  {
	    if (equal("-all", av[i]))
		for (j=0; fields[j].f_name; j++)
		    nprint++, fields[j].f_print++;
	    for (j=0; fields[j].f_name; j++) 
		if (equal(fields[j].f_name, &av[i][1]))
		    nprint++, fields[j].f_print++;
	}
	else 
	    files++;
    }
    if (!nprint) {
	fprintf(stderr, "usage: %s [-] [-all] -field[s] file1[s]\n", av[0]);
	exit(0);
    }
    for (i=1; i<ac; i++)
	if (av[i][0] != '-')  
	    ret |= statit(av[i], nprint, files);

    if (from_stdin)
	while (gets(buf)) {
# ifdef LS_ADDS_SPACE
	    buf[strlen(buf)-1] = NULL;
# endif
	    ret |= statit(buf, nprint, 2);
	}
    exit(ret);
}

/*------------------------------------------------------------------30/Jan/87-*
 * statit(file, nprint, files) - do the work
 *----------------------------------------------------------------larry mcvoy-*/
statit(file, nprint, files)
    char* file;
{
    register ret, j;
	
    if (ret = stat(file, &sbuf)) {
	perror(file);
        return ret;
    } else {
	register first = 1;

	for (j=0; fields[j].f_name; j++) {
	    if (fields[j].f_print) {
		if (first) {
		    if (files > 1)
			printf("%-14s ", file);
		    first = 0;
		}
		else
		    printf("  ");
		printit(&sbuf, &fields[j], nprint);
	    }
	}
	printf("\n");
    }
    return 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 spare4.  The mode field is dumped in octal, followed
 *		by one or more of the S_IF<X> and/or S_I<X> values.  The spare4
 *		field is dumped as two u_longs.  All other fields are dumped
 *		as u_char, u_short, or u_long, as apporpriate.
 *----------------------------------------------------------------larry mcvoy-*/
#define MODE(x) ((x->st_mode) & ~S_IFMT) 
#define TYPE(x) ((x->st_mode) &  S_IFMT) 

#define S_IOREAD	(S_IREAD >> 6)
#define S_IOWRITE	(S_IWRITE >> 6)
#define S_IOEXEC	(S_IEXEC >> 6)
#define S_IGREAD	(S_IREAD >> 3)
#define S_IGWRITE	(S_IWRITE >> 3)
#define S_IGEXEC	(S_IEXEC >> 3)
#define S_IUREAD	S_IREAD
#define S_IUWRITE	S_IWRITE
#define S_IUEXEC	S_IEXEC

#define IS_MODE(FIELD) if ((MODE(sb) & S_I/**/FIELD) == S_I/**/FIELD) printf(mask,"FIELD")
#define IS_TYPE(FIELD) if ((TYPE(sb)) == S_IF/**/FIELD) printf(mask,"FIELD")

printit(sb, f, n)
    register struct stat* sb;
    register struct field* f;
    int n;
{
    static char mask[] = ", %s";

    if (n > 1)
	printf("%s: ", f->f_name);

    if (equal(f->f_name, "mode")) {
	printf("%07o", sb->st_mode);


	IS_TYPE(DIR);

# ifdef S_IFFIFO
	IS_TYPE(FIFO);
# endif
	IS_TYPE(CHR);
	IS_TYPE(BLK);
	IS_TYPE(REG);

# ifdef S_IFSOCK
	IS_TYPE(SOCK);
# endif

	IS_MODE(SUID);
	IS_MODE(SGID);
	IS_MODE(SVTX);
	IS_MODE(UREAD);
	IS_MODE(UWRITE);
	IS_MODE(UEXEC);
	IS_MODE(GREAD);
	IS_MODE(GWRITE);
	IS_MODE(GEXEC);
	IS_MODE(OREAD);
	IS_MODE(OWRITE);
	IS_MODE(OEXEC);
    }

    /* times in human form, uppercase first letter */
    else if (equal("Ctime", f->f_name)) 
	printf("%.24s", ctime(&sb->st_ctime));
    else if (equal("Mtime", f->f_name))
	printf("%.24s", ctime(&sb->st_mtime));
    else if (equal("Atime", f->f_name))
	printf("%.24s", ctime(&sb->st_atime));
    else if (equal("ctime", f->f_name)) 
	printf("%u", sb->st_ctime);
    else if (equal("mtime", f->f_name))
	printf("%u", sb->st_mtime);
    else if (equal("atime", f->f_name))
	printf("%u", sb->st_atime);
# ifdef S_IFSOCK
    else if (equal("spare4", f->f_name))
	printf("%u %u", sb->st_spare4[0], sb->st_spare4[1]);
# endif
    else
	switch (f->f_size) {
	    case 1: printf("%u", *(u_char*)f->f_addr);
		    break;
	    case 2: printf("%u", *((u_short*)f->f_addr));
		    break;
	    case 4: printf("%u", *((u_long*)f->f_addr));
		    break;
	    default:fprintf(stderr, "\nError: bad field size %d (%s)\n", 
			    f->f_size, f->f_name);
		    break;
	}
}

sizecheck()
{
    if (sizeof(char) != 1)
	fprintf(stderr, "warning: char != 1\n");
    if (sizeof(short) != 2)
	fprintf(stderr, "warning: short != 2\n");
    if (sizeof(long) != 4)
	fprintf(stderr, "warning: long != 4\n");
}