#ifndef lint static const char *sccsid = "$dotat: things/stat.c,v 1.2 2001/12/17 16:26:58 fanf Exp $"; #endif /* lint */ /*** * program name: stat * function: Display the contents of an i-node. * switches: -f fmt Display the i-node according to the format characters in "fmt". Syntax is like that of printf(3s) with the formatting characters as encoded in FmtTbl below. * libraries used: libc * compile time parameters: cc -O -o stat stat.c * history: 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. fix-ups by fanf in 2001 ***/ /* * System - wide header files */ #include #include #include #include #include #include #include #include #include #include #include /* * Global structures and definitions */ #define FAIL -1 /* Failure return code from call */ #define OKAY 0 /* Success return code from call */ char *progname; /* Name we're called as */ struct mystat { char *filename; char *linkname; struct stat st; }; typedef size_t FmtFunc __P((char *, size_t, char *, struct mystat *)); size_t FmtInode __P((char *, size_t, char *, struct mystat *)); /* ** UidToS */ size_t UidToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { struct passwd *pwent; (void) setpwent(); if( (pwent = getpwuid(ip -> st.st_uid)) == NULL) return snprintf(buf, len, "%lu", (unsigned long) ip -> st.st_uid); else return snprintf(buf, len, "%lu/%s", (unsigned long) ip -> st.st_gid, pwent -> pw_name); } /* ** GidToS */ size_t GidToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { struct group *grent; (void) setgrent(); if( (grent = getgrgid(ip -> st.st_gid)) == NULL) return snprintf(buf, len, "%lu", (unsigned long) ip -> st.st_gid); else return snprintf(buf, len, "%lu/%s", (unsigned long) ip -> st.st_gid, grent -> gr_name); } /* ** RDevToS */ size_t RDevToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { if( !S_ISCHR(ip -> st.st_mode) && !S_ISBLK(ip -> st.st_mode)) return 0; else if( fmt != NULL) return FmtInode(buf, len, fmt, ip); else if( minor(ip -> st.st_rdev) > 255 || minor(ip -> st.st_rdev) < 0) return snprintf(buf, len, "%3d,0x%08x", major(ip -> st.st_rdev), minor(ip -> st.st_rdev)); else return snprintf(buf, len, "%3d,%3d", major(ip -> st.st_rdev), minor(ip -> st.st_rdev)); } /* ** ModeToS */ size_t ModeToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { char mode[12]; strmode(ip -> st.st_mode, mode); if( mode[10] == ' ') mode[10] = '\0'; return snprintf(buf, len, "%04o/%s", (int)(ip -> st.st_mode & ~S_IFMT), mode); } /* ** TypeToS */ size_t TypeToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { switch( ip -> st.st_mode & S_IFMT ) { case S_IFDIR: return strlcpy(buf, "Directory", len); case S_IFCHR: return strlcpy(buf, "Character Device", len); case S_IFBLK: return strlcpy(buf, "Block Device", len); case S_IFREG: return strlcpy(buf, "Regular File", len); case S_IFLNK: return strlcpy(buf, "Symbolic Link", len); case S_IFSOCK: return strlcpy(buf, "Socket", len); default: return strlcpy(buf, "Unknown", len); } } /* ** FileToS */ size_t FileToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return strlcpy(buf, ip -> filename, len); } /* ** LinkToS */ size_t LinkToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { if( ip -> linkname == NULL) return 0; else if( fmt != NULL) return FmtInode(buf, len, fmt, ip); else return strlcpy(buf, ip -> linkname, len); } /* ** QtoS */ size_t QtoS(buf, len, q) char buf []; size_t len; quad_t q; { return snprintf(buf, len, "%qd", q); } /* ** InoToS */ size_t InoToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return QtoS(buf, len, (quad_t) ip -> st.st_ino); } /* ** DevToS */ size_t DevToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return QtoS(buf, len, (quad_t) ip -> st.st_dev); } /* ** NLinkToS */ size_t NLinkToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return QtoS(buf, len, (quad_t) ip -> st.st_nlink); } /* ** SizeToS */ size_t SizeToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return QtoS(buf, len, (quad_t) ip -> st.st_size); } /* ** BlocksToS */ size_t BlocksToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return QtoS(buf, len, (quad_t) ip -> st.st_blocks); } /* ** TimeToS */ size_t TimeToS(buf, len, fmt, t) char buf []; size_t len; char *fmt; time_t t; { return strftime(buf, len, fmt ? fmt : "%c", localtime(&t)); } /* ** ATimeToS */ size_t ATimeToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return TimeToS(buf, len, fmt, ip -> st.st_atime); } /* ** CTimeToS */ size_t CTimeToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return TimeToS(buf, len, fmt, ip -> st.st_ctime); } /* ** MTimeToS */ size_t MTimeToS(buf, len, fmt, ip) char buf []; size_t len; char *fmt; struct mystat *ip; { return TimeToS(buf, len, fmt, ip -> st.st_mtime); } /* ** Formatting table for the above functions */ FmtFunc *FmtTbl[] = { /* a */ ATimeToS, /* b */ BlocksToS, /* c */ CTimeToS, /* d */ DevToS, /* e */ NULL, /* f */ TypeToS, /* g */ GidToS, /* h */ NULL, /* i */ InoToS, /* j */ NULL, /* k */ NULL, /* l */ NLinkToS, /* m */ MTimeToS, /* n */ FileToS, /* o */ NULL, /* p */ ModeToS, /* q */ NULL, /* r */ LinkToS, /* s */ SizeToS, /* t */ RDevToS, /* u */ UidToS, /* v */ NULL, /* w */ NULL, /* x */ NULL, /* y */ NULL, /* z */ NULL, }; /* ** Backslash */ int Backslash(buf, fmt) char *buf; char *fmt; { int c, n; switch (*fmt) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': for(n = c = 0; *fmt >= 0 && *fmt <= 7; fmt++, n++) c = c * 8 + *fmt - '0'; *buf = c; return n; case 'a': *buf++ = '\a'; return 1; case 'b': *buf++ = '\b'; return 1; case 'e': *buf++ = '\033'; return 1; case 'f': *buf++ = '\f'; return 1; case 'n': *buf++ = '\n'; return 1; case 'r': *buf++ = '\r'; return 1; case 't': *buf++ = '\t'; return 1; case 'v': *buf++ = '\v'; return 1; default: *buf++ = *fmt; return 1; } } /* ** FmtInode */ size_t FmtInode(buf, len, fmt, ip) char *buf; size_t len; char *fmt; struct mystat *ip; { FmtFunc *f; char *p; size_t count, n; count = 0; for( n = strcspn(fmt, "\\%"); fmt[n]; n = strcspn(fmt, "\\%")) { strlcpy(buf, fmt, n < len ? n+1 : len); count += n; buf += n; if( n < len) len -= n; else len = 0; p = fmt += n + 1; if( p[-1] == '\\') { if( len > 0) fmt += Backslash(buf, fmt); n = 1; } else if( islower(*p)) { f = FmtTbl[*p - 'a']; if( f == NULL) continue; n = f(buf, len, NULL, ip); } else if( *p == '{') { p = strchr(p, '}'); if( p == NULL) continue; if( !islower(*++p)) continue; f = FmtTbl[*p - 'a']; if( f == NULL) continue; p[-1] = '\0'; n = f(buf, len, fmt+1, ip); p[-1] = '}'; } else { continue; } count += n; buf += n; if( n < len) len -= n; else len = 0; fmt = p + 1; } count += strlcpy(buf, fmt, len); return count; } /* ** STAT_IT -- Display an i-node. ** ** Print the contents of an i-node. ** ** Parameters: ** filename -- Name of the file. ** ** Returns: ** None. ** ** Notes: ** None. ** */ int stat_it(format, filename) char *format; char *filename; { int count; char buf [BUFSIZ]; struct mystat Sbuf; /* for return values from stat() call */ #define LBUFSIZ 256 /* Length of symbolic link translation buffer */ char Lbuf [LBUFSIZ]; /* Symbolic link translation buffer */ size_t n; if( lstat(filename,&Sbuf.st) == FAIL) { fprintf(stderr,"lstat %s: %s\n", filename, strerror(errno)); return(FAIL); } if( (Sbuf.st.st_mode & S_IFMT) == S_IFLNK) { if( (count = readlink(filename, Lbuf, sizeof Lbuf)) == FAIL) { fprintf(stderr, "readlink %s: %s\n", filename, strerror(errno)); return(FAIL); } if( count >= sizeof Lbuf) { fprintf(stderr, "readlink %s: Link name too long\n", filename); return(FAIL); } Lbuf[count] = '\0'; Sbuf.linkname = Lbuf; } else { Sbuf.linkname = NULL; } Sbuf.filename = filename; n = FmtInode(buf, sizeof buf, format, &Sbuf); if( n >= sizeof(buf)) { fprintf(stderr, "Formatted string too long for %s\n", filename); return(FAIL); } printf("%s", buf); return(OKAY); } /* ** USAGE -- Display a reminder ** ** Print a reminder of the correct usage ** of the command. ** ** Parameters: ** None. ** ** Returns: ** None. ** ** Notes: ** None. ** */ void Usage() { fprintf(stderr, "Usage: %s [-f fmt] file1 file2 ...\n", progname); } int main(argc, argv) int argc; char *argv[]; { char *format; int c; /* * Note how we're called ... */ progname = argv [0]; /* * Set some defaults ... */ format = strdup(" File: \"%n\"%{ -> \"%r\"}r\n Size: %s\tAllocated Blocks: %b\tFiletype: %f\n Mode: (%p)\tUid: (%u) Gid: (%g)\nDevice: %d\tInode: %i\tLinks: %l%{\tDevice Type: %t}t\nAccess: %a\nModify: %m\nChange: %c\n\n"); /* * Check for flag arguments ... */ while ((c = getopt(argc, argv, "f:")) != EOF) switch (c) { default: Usage(); exit(1); case 'f': /* Format specifier */ format = optarg; break; } argc -= optind; argv += optind; /* * Ok, now for the file arguments ... */ if (argc == 0) { Usage(); exit(1); } while (argc--) { c += stat_it(format, *argv++); } /* check for errors, e.g. file does not exists */ if (c == 0) exit(0); else exit(2); }