#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>


static display(char *fmt, ...)
{
	static int started = 0;
	va_list args;

	if (fmt == 0)
	{
		putchar('\n');
		started = 0;
	}
	else if (started)
		putchar(' ');
	else
		started = 1;
	va_start(args, fmt);
	vprintf(fmt, args);
	va_end(args);
}


void printperms(umode_t m)
{
	display("");

	if (S_ISLNK(m)) putchar('l');
	else if (S_ISREG(m)) putchar('-');
	else if (S_ISDIR(m)) putchar('d');
	else if (S_ISCHR(m)) putchar('c');
	else if (S_ISBLK(m)) putchar('b');
	else if (S_ISFIFO(m)) putchar('p');
	else if (S_ISSOCK(m)) putchar('s');
	else putchar('?');

	putchar((m & S_IRUSR) ? 'r' : '-');
	putchar((m & S_IWUSR) ? 'w' : '-');
	putchar("-xSs"[((m & S_IXUSR) != 0) + (((m & S_ISUID) != 0) << 1)]);

	putchar((m & S_IRGRP) ? 'r' : '-');
	putchar((m & S_IWGRP) ? 'w' : '-');
	putchar("-xSs"[((m & S_IXGRP) != 0) + (((m & S_ISGID) != 0) << 1)]);

	putchar((m & S_IROTH) ? 'r' : '-');
	putchar((m & S_IWOTH) ? 'w' : '-');
	putchar("-xTt"[((m & S_IXOTH) != 0) + (((m & S_ISVTX) != 0) << 1)]);
}


static void printtime(time_t t)
{
	char *s = ctime(&t);
	if (*s) s[strlen(s) - 1] = 0;
	display("%s", s);
}


static void usage(void)
{
	fprintf(stderr, "usage: fstat [-options] file ...\n\
Options control what is displayed, and are selected from:\n\
   n   file name
   L   information for symbolic link, not target of link\n\
   d   device number\n\
   i   inode\n\
   p   file mode\n\
   P   file permissions\n\
   l   number of links\n\
   u   uid\n\
   g   gid\n\
   r   rdev number\n\
   s   size in bytes\n\
   B   block size\n\
   b   number of blocks\n\
   a   last access time (seconds since epoch)\n\
   A   last access time\n\
   m   modification time (seconds since epoch)\n\
   M   modification time\n\
   c   time of last change to inode (seconds since epoch)\n\
   C   time of last change to inode\n");
	exit(1);
}


main(int argc, char *argv[])
{
	struct stat s;
	int (*statfunc)(const char*, struct stat*) = stat;
	int fname, device, inode, perms, sperms, links, uid, gid, rdev, size;
	int bsize, blocks, atime, satime, mtime, smtime, ctime, sctime;
	int c;

	fname = device = inode = perms = sperms = links = uid = gid = 0;
	rdev = size = bsize = blocks = atime = satime = mtime = smtime = 0;
	ctime = sctime = 0;

	while ((c = getopt(argc, argv, "LdipPlugrsBbaAmMcC")) != -1)
		switch (c)
		{
			case 'n': fname = 1; break;
			case 'L': statfunc = lstat; break;
			case 'd': device = 1; break;
			case 'i': inode = 1; break;
			case 'p': perms = 1; break;
			case 'P': sperms = 1; break;
			case 'l': links = 1; break;
			case 'u': uid = 1; break;
			case 'g': gid = 1; break;
			case 'r': rdev = 1; break;
			case 's': size = 1; break;
			case 'B': bsize = 1; break;
			case 'b': blocks = 1; break;
			case 'a': atime = 1; break;
			case 'A': satime = 1; break;
			case 'm': mtime = 1; break;
			case 'M': smtime = 1; break;
			case 'c': ctime = 1; break;
			case 'C': sctime = 1; break;
			default: usage();
		}
	
	if (fname + device + inode + perms + sperms + links + uid + gid +
			rdev + size +bsize + blocks + atime + satime +
			mtime + smtime + ctime + sctime == 0)
		fname = inode = sperms = links = uid = gid = size =
			satime = smtime = 1;


	for ( ; optind < argc; optind++)
		if (stat(argv[optind], &s) == -1)
			perror(argv[optind]);
		else
		{
			if (fname) display("%s", argv[optind]);
			if (device) display("%u", s.st_dev);
			if (inode) display("%lu", s.st_ino);
			if (perms) display("%o", s.st_mode);
			if (sperms) printperms(s.st_mode);
			if (links) display("%u", s.st_nlink);
			if (uid) display("%u", s.st_uid);
			if (gid) display("%u", s.st_gid);
			if (rdev) display("%u", s.st_dev);
			if (size) display("%lu", s.st_size);
			if (bsize) display("%lu", s.st_blksize);
			if (blocks) display("%lu", s.st_blocks);
			if (atime) display("%ld", s.st_atime);
			if (satime) printtime(s.st_atime);
			if (mtime) display("%ld", s.st_mtime);
			if (smtime) printtime(s.st_mtime);
			if (ctime) display("%ld", s.st_ctime);
			if (sctime) printtime(s.st_ctime);
			display(0);
		}
	return 0;
}
