/* * finfo - print file info * * Chet Ramey * chet@po.cwru.edu */ #ifdef HAVE_CONFIG_H # include #endif #include #include "posixstat.h" #include #include #include #include #include "posixtime.h" #include "bashansi.h" #include "shell.h" #include "builtins.h" #include "common.h" #ifndef errno extern int errno; #endif extern char **make_builtin_argv (); static int printst(); static int printsome(); static int printfinfo(); static int finfo_main(); extern int sh_optind; extern char *sh_optarg; extern char *this_command_name; static char *prog; static int pmask; #define OPT_UID 0x00001 #define OPT_GID 0x00002 #define OPT_DEV 0x00004 #define OPT_INO 0x00008 #define OPT_PERM 0x00010 #define OPT_LNKNAM 0x00020 #define OPT_FID 0x00040 #define OPT_NLINK 0x00080 #define OPT_RDEV 0x00100 #define OPT_SIZE 0x00200 #define OPT_ATIME 0x00400 #define OPT_MTIME 0x00800 #define OPT_CTIME 0x01000 #define OPT_BLKSIZE 0x02000 #define OPT_BLKS 0x04000 #define OPT_FTYPE 0x08000 #define OPT_PMASK 0x10000 #define OPT_OPERM 0x20000 #define OPT_ASCII 0x1000000 #define OPTIONS "acdgiflmnopsuACGMP:U" static int octal(s) char *s; { int r; r = *s - '0'; while (*++s >= '0' && *s <= '7') r = (r * 8) + (*s - '0'); return r; } static int finfo_main(argc, argv) int argc; char **argv; { register int i; int mode, flags, opt; sh_optind = 0; /* XXX */ prog = base_pathname(argv[0]); if (argc == 1) { builtin_usage(); return(1); } flags = 0; while ((opt = sh_getopt(argc, argv, OPTIONS)) != EOF) { switch(opt) { case 'a': flags |= OPT_ATIME; break; case 'A': flags |= OPT_ATIME|OPT_ASCII; break; case 'c': flags |= OPT_CTIME; break; case 'C': flags |= OPT_CTIME|OPT_ASCII; break; case 'd': flags |= OPT_DEV; break; case 'i': flags |= OPT_INO; break; case 'f': flags |= OPT_FID; break; case 'g': flags |= OPT_GID; break; case 'G': flags |= OPT_GID|OPT_ASCII; break; case 'l': flags |= OPT_LNKNAM; break; case 'm': flags |= OPT_MTIME; break; case 'M': flags |= OPT_MTIME|OPT_ASCII; break; case 'n': flags |= OPT_NLINK; break; case 'o': flags |= OPT_OPERM; break; case 'p': flags |= OPT_PERM; break; case 'P': flags |= OPT_PMASK; pmask = octal(sh_optarg); break; case 's': flags |= OPT_SIZE; break; case 'u': flags |= OPT_UID; break; case 'U': flags |= OPT_UID|OPT_ASCII; break; default: builtin_usage (); return(1); } } argc -= sh_optind; argv += sh_optind; if (argc == 0) { builtin_usage(); return(1); } for (i = 0; i < argc; i++) opt = flags ? printsome (argv[i], flags) : printfinfo(argv[i]); return(opt); } static struct stat * getstat(f) char *f; { static struct stat st; int fd, r; intmax_t lfd; if (strncmp(f, "/dev/fd/", 8) == 0) { if ((legal_number(f + 8, &lfd) == 0) || (int)lfd != lfd) { builtin_error("%s: invalid fd", f + 8); return ((struct stat *)0); } fd = lfd; r = fstat(fd, &st); } else #ifdef HAVE_LSTAT r = lstat(f, &st); #else r = stat(f, &st); #endif if (r < 0) { builtin_error("%s: cannot stat: %s", f, strerror(errno)); return ((struct stat *)0); } return (&st); } static int printfinfo(f) char *f; { struct stat *st; st = getstat(f); return (st ? printst(st) : 1); } static int getperm(m) int m; { return (m & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID)); } static int perms(m) int m; { char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */ int i; i = 0; if (m & S_IRUSR) ubits[i++] = 'r'; if (m & S_IWUSR) ubits[i++] = 'w'; if (m & S_IXUSR) ubits[i++] = 'x'; ubits[i] = '\0'; i = 0; if (m & S_IRGRP) gbits[i++] = 'r'; if (m & S_IWGRP) gbits[i++] = 'w'; if (m & S_IXGRP) gbits[i++] = 'x'; gbits[i] = '\0'; i = 0; if (m & S_IROTH) obits[i++] = 'r'; if (m & S_IWOTH) obits[i++] = 'w'; if (m & S_IXOTH) obits[i++] = 'x'; obits[i] = '\0'; if (m & S_ISUID) ubits[2] = (m & S_IXUSR) ? 's' : 'S'; if (m & S_ISGID) gbits[2] = (m & S_IXGRP) ? 's' : 'S'; if (m & S_ISVTX) obits[2] = (m & S_IXOTH) ? 't' : 'T'; printf ("u=%s,g=%s,o=%s", ubits, gbits, obits); } static int printmode(mode) int mode; { if (S_ISBLK(mode)) printf("S_IFBLK "); if (S_ISCHR(mode)) printf("S_IFCHR "); if (S_ISDIR(mode)) printf("S_IFDIR "); if (S_ISREG(mode)) printf("S_IFREG "); if (S_ISFIFO(mode)) printf("S_IFIFO "); if (S_ISLNK(mode)) printf("S_IFLNK "); if (S_ISSOCK(mode)) printf("S_IFSOCK "); #ifdef S_ISWHT if (S_ISWHT(mode)) printf("S_ISWHT "); #endif perms(getperm(mode)); printf("\n"); } static int printst(st) struct stat *st; { struct passwd *pw; struct group *gr; char *owner; int ma, mi, d; ma = major (st->st_rdev); mi = minor (st->st_rdev); #if defined (makedev) d = makedev (ma, mi); #else d = st->st_rdev & 0xFF; #endif printf("Device (major/minor): %d (%d/%d)\n", d, ma, mi); printf("Inode: %d\n", (int) st->st_ino); printf("Mode: (%o) ", (int) st->st_mode); printmode((int) st->st_mode); printf("Link count: %d\n", (int) st->st_nlink); pw = getpwuid(st->st_uid); owner = pw ? pw->pw_name : "unknown"; printf("Uid of owner: %d (%s)\n", (int) st->st_uid, owner); gr = getgrgid(st->st_gid); owner = gr ? gr->gr_name : "unknown"; printf("Gid of owner: %d (%s)\n", (int) st->st_gid, owner); printf("Device type: %d\n", (int) st->st_rdev); printf("File size: %ld\n", (long) st->st_size); printf("File last access time: %s", ctime (&st->st_atime)); printf("File last modify time: %s", ctime (&st->st_mtime)); printf("File last status change time: %s", ctime (&st->st_ctime)); fflush(stdout); return(0); } static int printsome(f, flags) char *f; int flags; { struct stat *st; struct passwd *pw; struct group *gr; int p; char *b; st = getstat(f); if (st == NULL) return (1); /* Print requested info */ if (flags & OPT_ATIME) { if (flags & OPT_ASCII) printf("%s", ctime(&st->st_atime)); else printf("%ld\n", st->st_atime); } else if (flags & OPT_MTIME) { if (flags & OPT_ASCII) printf("%s", ctime(&st->st_mtime)); else printf("%ld\n", st->st_mtime); } else if (flags & OPT_CTIME) { if (flags & OPT_ASCII) printf("%s", ctime(&st->st_ctime)); else printf("%ld\n", st->st_ctime); } else if (flags & OPT_DEV) printf("%d\n", st->st_dev); else if (flags & OPT_INO) printf("%d\n", st->st_ino); else if (flags & OPT_FID) printf("%d:%ld\n", st->st_dev, st->st_ino); else if (flags & OPT_NLINK) printf("%d\n", st->st_nlink); else if (flags & OPT_LNKNAM) { #ifdef S_ISLNK b = xmalloc(4096); p = readlink(f, b, 4096); if (p >= 0 && p < 4096) b[p] = '\0'; else { p = errno; strcpy(b, prog); strcat(b, ": "); strcat(b, strerror(p)); } printf("%s\n", b); free(b); #else printf("%s\n", f); #endif } else if (flags & OPT_PERM) { perms(st->st_mode); printf("\n"); } else if (flags & OPT_OPERM) printf("%o\n", getperm(st->st_mode)); else if (flags & OPT_PMASK) printf("%o\n", getperm(st->st_mode) & pmask); else if (flags & OPT_UID) { pw = getpwuid(st->st_uid); if (flags & OPT_ASCII) printf("%s\n", pw ? pw->pw_name : "unknown"); else printf("%d\n", st->st_uid); } else if (flags & OPT_GID) { gr = getgrgid(st->st_gid); if (flags & OPT_ASCII) printf("%s\n", gr ? gr->gr_name : "unknown"); else printf("%d\n", st->st_gid); } else if (flags & OPT_SIZE) printf("%ld\n", (long) st->st_size); return (0); } #ifndef NOBUILTIN int finfo_builtin(list) WORD_LIST *list; { int c, r; char **v; WORD_LIST *l; v = make_builtin_argv (list, &c); r = finfo_main (c, v); free (v); return r; } static char *finfo_doc[] = { "Display information about file attributes.", "", "Display information about each FILE. Only single operators should", "be supplied. If no options are supplied, a summary of the info", "available about each FILE is printed. If FILE is of the form", "/dev/fd/XX, file descriptor XX is described. Operators, if supplied,", "have the following meanings:", "", " -a last file access time", " -A last file access time in ctime format", " -c last file status change time", " -C last file status change time in ctime format", " -m last file modification time", " -M last file modification time in ctime format", " -d device", " -i inode", " -f composite file identifier (device:inode)", " -g gid of owner", " -G group name of owner", " -l name of file pointed to by symlink", " -n link count", " -o permissions in octal", " -p permissions in ascii", " -P mask permissions ANDed with MASK (like with umask)", " -s file size in bytes", " -u uid of owner", " -U user name of owner", (char *)0 }; struct builtin finfo_struct = { "finfo", finfo_builtin, BUILTIN_ENABLED, finfo_doc, "finfo [-acdgiflmnopsuACGMPU] file [file...]", 0 }; #endif #ifdef NOBUILTIN #if defined (PREFER_STDARG) # include #else # if defined (PREFER_VARARGS) # include # endif #endif char *this_command_name; main(argc, argv) int argc; char **argv; { this_command_name = argv[0]; exit(finfo_main(argc, argv)); } void builtin_usage() { fprintf(stderr, "%s: usage: %s [-%s] [file ...]\n", prog, OPTIONS); } #ifndef HAVE_STRERROR char * strerror(e) int e; { static char ebuf[40]; extern int sys_nerr; extern char *sys_errlist[]; if (e < 0 || e > sys_nerr) { sprintf(ebuf,"Unknown error code %d", e); return (&ebuf[0]); } return (sys_errlist[e]); } #endif char * xmalloc(s) size_t s; { char *ret; extern char *malloc(); ret = malloc(s); if (ret) return (ret); fprintf(stderr, "%s: cannot malloc %d bytes\n", prog, s); exit(1); } char * base_pathname(p) char *p; { char *t; if (t = strrchr(p, '/')) return(++t); return(p); } int legal_number (string, result) char *string; long *result; { int sign; long value; sign = 1; value = 0; if (result) *result = 0; /* Skip leading whitespace characters. */ while (whitespace (*string)) string++; if (!*string) return (0); /* We allow leading `-' or `+'. */ if (*string == '-' || *string == '+') { if (!digit (string[1])) return (0); if (*string == '-') sign = -1; string++; } while (digit (*string)) { if (result) value = (value * 10) + digit_value (*string); string++; } /* Skip trailing whitespace, if any. */ while (whitespace (*string)) string++; /* Error if not at end of string. */ if (*string) return (0); if (result) *result = value * sign; return (1); } int sh_optind; char *sh_optarg; int sh_opterr; extern int optind; extern char *optarg; int sh_getopt(c, v, o) int c; char **v, *o; { int r; r = getopt(c, v, o); sh_optind = optind; sh_optarg = optarg; return r; } #if defined (USE_VARARGS) void #if defined (PREFER_STDARG) builtin_error (const char *format, ...) #else builtin_error (format, va_alist) const char *format; va_dcl #endif { va_list args; if (this_command_name && *this_command_name) fprintf (stderr, "%s: ", this_command_name); #if defined (PREFER_STDARG) va_start (args, format); #else va_start (args); #endif vfprintf (stderr, format, args); va_end (args); fprintf (stderr, "\n"); } #else void builtin_error (format, arg1, arg2, arg3, arg4, arg5) char *format, *arg1, *arg2, *arg3, *arg4, *arg5; { if (this_command_name && *this_command_name) fprintf (stderr, "%s: ", this_command_name); fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5); fprintf (stderr, "\n"); fflush (stderr); } #endif /* !USE_VARARGS */ #endif