
/* stat.c */

/* Copyright 1994 by Steven G. Isaacson */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <time.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
 
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

static char Sccsid[]="@(#) stat.c  01/31/94 stevei";

char *h[] = {
"    -     Read filenames from stdin",
"    -F [filename]   file containing list of filenames",
"    -x    Default output: mode nlink uid gid size mtime name",
"    -n    Print name",
"    -p    Look up user and group names (default prints uid and gid)",
"    -i    Inode number",
"    -d    ID of device containing a directory entry for this file",
"    -D    ID of device: defined for character/block special files",
"    -l    Number of links",
"    -u    User ID of the file's owner",
"    -g    Group ID of the file's group",
"    -s    File size in bytes",
"    -o    File mode [see mknod(2)]          -O decimal value",
"    -a    Time of last access               -A decimal value",
"    -m    Time of last data modification    -M decimal value",
"    -c    Time of last file status 'change' -C decimal value",
"    -t    Show all three times",
"    -w    Print no extra white space",
#ifdef S_IFLNK
"    -L    Print symbolic link name",
#endif
"    -h    This help page",
'\0' };

char *usage_str="usage: stat [-args] [filename(s)|-]";

int mode=FALSE;       /* o */
int mode_dec=FALSE;   /* O */
int ino=FALSE;        /* i */
int dev=FALSE;        /* d */
int rdev=FALSE;       /* D */
int nlink=FALSE;      /* l */
int uid=FALSE;        /* u */
int gid=FALSE;        /* q */
int size=FALSE;       /* s */
int atime=FALSE;      /* a */
int Atime=FALSE;      /* a */
int mtime=FALSE;      /* m */
int Mtime=FALSE;      /* m */
int xctime=FALSE;     /* c */
int xCtime=FALSE;     /* c */
int name=FALSE;       /* n */
int lkupname=FALSE;   /* p */
int symlnkname=FALSE; /* L */
int showt=FALSE;      /* t */
int nwht=FALSE;       /* w */

#define DEFAULT_OUTPUT mode=nlink=uid=gid=size=\
    mtime=name=lkupname=TRUE;

main(argc, argv)
int argc;
char *argv[];
{
    int i;
    extern int optind;
    extern char *optarg;
    char *options="oOidDlugsaAmMcCxhn?F:Lptw";
    int c, errflg=0;
    char stdinfn[256];
    FILE *stdinfp;

    stdinfn[0] = '\0';

    if (argc == 1) {
        fprintf(stderr, "%s\n", usage_str);
        exit(1);
    }

    /* default same as -x */
    if ( argv[1][0] != '-' || (argv[1][0] == '-' && ! argv[1][1]) )
        DEFAULT_OUTPUT ;

    while ((c=getopt(argc, argv, options)) != EOF) {

        switch (c) {
            case 'x':
                DEFAULT_OUTPUT ;
                break;

            case 'L':
                symlnkname=TRUE;
                break;

            case 'o':
                mode=TRUE;
                break;
            
            case 'O':
                mode_dec=TRUE;
                break;
            
            case 't':
                showt=TRUE;
                break;
            
            case 'i':
                ino=TRUE;
                break;
            
            case 'd':
                dev=TRUE;
                break;
            
            case 'D':
                rdev=TRUE;
                break;
            
            case 'l':
                nlink=TRUE;
                break;
            
            case 'u':
                uid=TRUE;
                break;
            
            case 'g':
                gid=TRUE;
                break;
            
            case 's':
                size=TRUE;
                break;
            
            case 'a':
                atime=TRUE;
                break;
            
            case 'A':
                Atime=TRUE;
                break;
            
            case 'm':
                mtime=TRUE;
                break;
            
            case 'M':
                Mtime=TRUE;
                break;
            
            case 'c':
                xctime=TRUE;
                break;

            case 'C':
                xCtime=TRUE;
                break;

            case 'n':
                name=TRUE;
                break;
            
            case 'p':
                lkupname=TRUE;
                break;
            
            case 'F':
                strcpy(stdinfn, optarg);
                break;

            case 'w':
                nwht=TRUE;
                break;
            
            case '?':
            case 'h':
            default:
                errflg++;
                break;
        }

        if (errflg) {
            help();
            exit(0);
        }
    }

    if (stdinfn[0]) {
        /* input is coming from a file */

        if ((stdinfp = fopen(stdinfn, "r")) == NULL) {
            fprintf ( stderr,
              "*** could not open: %s\n", stdinfn );
            exit ( 2 );
        }

        stat_input(stdinfp);
    }
    else {
        /* input is coming from the command-line or 
           stdin */

        for (i=optind; i < argc; i++) {
            if ( argv[i][0] == '-' )
                stat_input(stdin);
            else
                stat_fn(argv[i]);
        }
    }
    exit(0);
}

/* ================================================= */
/* display help page                                 */

help()
{
    int i;

    fprintf(stderr, "%s\n", usage_str);
    for (i=0; h[i]; i++)
        fprintf(stderr, "%s\n", h[i]);

    fprintf(stderr, "\n");
}

/* ================================================= */
/* get the list of files to stat from a file.        */

#define MAX_FN 1024

stat_input(fp)
FILE *fp;
{
    char buf[MAX_FN], *retval;

    while ((retval=fgets(buf, MAX_FN, fp)) != (char *)EOF
            && retval != NULL ) {
        buf[strlen(buf) - 1] = '\0';
        stat_fn(buf);
    }
}

/* ================================================= */
/* make the stat (or lstat) system call.             */

#define SYM_LN_BUF 256

stat_fn(fn)
char *fn;
{
    struct stat sbuf;
    struct passwd *ppass;
    struct group  *pgroup;
    char *pc;
    int cc;
    char buf[SYM_LN_BUF];

#ifdef S_IFLNK
    if ( lstat(fn, &sbuf )) {
#else
    if (  stat(fn, &sbuf )) {
#endif
        perror(fn);
        return;
    }

    if (mode) {

#ifdef S_IFLNK
        if ((sbuf.st_mode & S_IFMT) == S_IFLNK)
        /* symbolic link */
            printf("l");
#else
        if ( FALSE )
            ;
#endif
        else if ((sbuf.st_mode & S_IFMT) == S_IFDIR)
          /* directory */
            printf("d");
        else if ((sbuf.st_mode & S_IFMT) == S_IFCHR)
          /* character special device */
            printf("c");
        else if ((sbuf.st_mode & S_IFMT) == S_IFBLK)
          /* block special device */
            printf("b");
        else if ((sbuf.st_mode & S_IFMT) == S_IFREG)
          /* regular file */
            printf("-");
        else if ((sbuf.st_mode & S_IFMT) == S_IFIFO)
          /* fifo */
            printf("p");

#ifdef S_INSHD
        else if ((sbuf.st_mode & S_IFMT) == S_INSHD)
          /* shared memory */
            printf("m");
#endif

#ifdef S_IFNAM 
        else if ((sbuf.st_mode & S_IFMT) == S_IFNAM)
          /* name special entry */
            printf("n");
#endif

#ifdef S_INSEM
        else if ((sbuf.st_mode & S_IFMT) == S_INSEM)
          /* semaphore */
            printf("s");
#endif

#ifdef S_IFSOCK
        else if ((sbuf.st_mode & S_IFMT) == S_IFSOCK)
          /* socket */
            printf("s");
#endif
        else
          /* don't know! */
            printf("?");

        if ((sbuf.st_mode & S_IREAD) ==  S_IREAD)
          /* 00400 */ printf("r");
        else
            printf("-");

        if ((sbuf.st_mode & S_IWRITE) ==  S_IWRITE )
            /* 00200 */ printf("w");
        else
            printf("-");

        if ((sbuf.st_mode & S_IEXEC) ==  S_IEXEC &&
            (sbuf.st_mode & S_ISUID) ==  S_ISUID)
                /* 00100 */ printf("s");
        else if ((sbuf.st_mode & S_IEXEC) ==  S_IEXEC)
            printf("x");
        else
            printf("-");

        if ((sbuf.st_mode & 00040) ==  00040)
            /* 00400 */ printf("r");
        else
            printf("-");

        if ((sbuf.st_mode & 00020) ==  00020)
            /* 00200 */ printf("w");
        else
            printf("-");

        if ((sbuf.st_mode & 00010) ==  00010 &&
            (sbuf.st_mode & S_ISGID) ==  S_ISGID)
            printf("s");
        else if ((sbuf.st_mode & 00010) ==  00010)
            /* 00100 */ printf("x");
        else
            printf("-");

        if ((sbuf.st_mode & 00004) ==  00004)
            /* 00400 */ printf("r");
        else
            printf("-");

        if ((sbuf.st_mode & 00002) ==  00002)
            /* 00200 */ printf("w");
        else
            printf("-");

        if ((sbuf.st_mode & 00001) ==  00001)
            /* 00100 */ printf("x");
        else
            printf("-");

        printf(" ");
    }

    if (mode_dec) printf("%hu ", sbuf.st_mode);

    if (ino) printf("%d ", sbuf.st_ino);
    if (dev)
        printf("%d,%d ", 
          major(sbuf.st_dev), minor(sbuf.st_dev));
    if (rdev)
        printf("%d,%d ",
          major(sbuf.st_rdev), minor(sbuf.st_rdev));
    if (nlink) {
        nwht ? printf("%d ", sbuf.st_nlink) : 
          printf("%2d ", sbuf.st_nlink);
    }

    if (uid) {
        if (lkupname) {
            ppass=(struct passwd *)getpwuid(sbuf.st_uid);
            if ( ppass ) {
                nwht ? printf("%s ", ppass->pw_name) :
                  printf("%-8s ", ppass->pw_name);
            }
            else
                printf("%d ", sbuf.st_uid);
        }
        else
            printf("%d ", sbuf.st_uid);
    }
    if (gid) {
        if (lkupname) {
            pgroup=(struct group *)getgrgid(sbuf.st_gid);
            if (pgroup) {
                nwht ? printf("%s ", pgroup->gr_name) :
                  printf("%-8s ", pgroup->gr_name);
            }
            else
                printf("%d ", sbuf.st_gid);
        }
        else
            printf("%d ", sbuf.st_gid);
    }
    if (size) {
        nwht ? printf("%d ", sbuf.st_size) :
          printf("%8d ", sbuf.st_size);
    }

    if (atime) { /* time of last access */
        pc=ctime(&sbuf.st_atime);
        pc[strlen(pc) - 1]='\0';
        printf("%s ", pc);
    }
    if (Atime) printf("%d ", sbuf.st_atime);
    if (mtime) { /* time of last data modification */
        pc=ctime(&sbuf.st_mtime);
        pc[strlen(pc) - 1]='\0';
        printf("%s ", pc);
    }
    if (Mtime) printf("%d ", sbuf.st_mtime);
    if (xctime) { /* time of last file status 'change' */
        pc=ctime(&sbuf.st_ctime);
        pc[strlen(pc) - 1]='\0';
        printf("%s ", pc);
    }
    if (xCtime) printf("%d ", sbuf.st_ctime);

    if (name)
        printf("%s", fn);

#ifdef S_IFLNK
    if (symlnkname) {

        cc=readlink(fn, buf, SYM_LN_BUF);
        buf[cc]='\0';
        printf(" -> %s", buf);
    }
#endif

    if (showt) {

        printf("\n");
        pc=ctime(&sbuf.st_atime);
        pc[strlen(pc) - 1]='\0';
        printf("access time: %s\n", pc);

        pc=ctime(&sbuf.st_mtime);
        pc[strlen(pc) - 1]='\0';
        printf("   mod time: %s\n", pc);

        pc=ctime(&sbuf.st_ctime);
        pc[strlen(pc) - 1]='\0';
        printf("change time: %s", pc);
    }

    printf("\n");
}

