/*
 * format:
 * 			%ur, %uw, %ux - user read/write/execute
 *			%gr, %gw, %gx - group read/write/execute
 *			%or, %ow, %ox - other read/write/execute
 *			%su, %sg, %ss  - set uid/gid, sticky bit
 *			%fn - filename
 *			%tp - filetype: regular/dir/char_dev/block_dev/fifo/symlink/socket
 *			%wn - owner
 *			%gp - group
 *			%cu, %cr, %ct - selinux label: user role type
 */


#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

char *filename;
char *fmtstr;

#define MAX_BUF 1024

char se_user[MAX_BUF], se_role[MAX_BUF], se_type[MAX_BUF];

void terminate(char *leftstr)
{
	printf("error: parsing error from %s\n", leftstr);
	exit(0);
}

void print_filename()
{
	char buf[PATH_MAX];
//	char *p;

	realpath(filename, buf);
	printf("%s", buf);
/*
	printf("[");
	p = &(buf[1]);
	while (*p) {
		char *p2 = strchr(p, '/');
		if (p2 == NULL) {
			printf("'%s'", p);
			p = p + strlen(p);
		} else {
			printf("'");
			while (p < p2) {
				printf("%c", *p);
				++p;
			}
			printf("'");
			printf(", ");
			p = p2+1;
		}
	}
	printf("]");
*/
}

void print_filetype(struct stat *st)
{
	int cnt = 0;

	if (S_ISREG(st->st_mode)) {
		printf("regular");
		++cnt;
	}
	if (S_ISDIR(st->st_mode)) {
		printf("dir");
		++cnt;
	}
	if (S_ISCHR(st->st_mode)) {
		printf("char_dev");
		++cnt;
	}
	if (S_ISBLK(st->st_mode)) {
		printf("block_dev");
		++cnt;
	}
	if (S_ISFIFO(st->st_mode)) {
		printf("fifo");
		++cnt;
	}
	if (S_ISLNK(st->st_mode)) {
		printf("symlink");
		++cnt;
	}
	if (S_ISSOCK(st->st_mode)) {
		printf("socket");
		++cnt;
	}

	assert(cnt == 1);
}

char *print_item(struct stat *st, char *str)
{
	if (str[0] != '%') {
		printf("%c", str[0]);
		return str + 1;
	}

	if (str[1] == '\0' && str[2] == '\0')
		terminate(str);

	if (str[1] == 'u' && str[2] == 'r')
		printf("%d", (st->st_mode & S_IRUSR) > 0);
	else if (str[1] == 'u' && str[2] == 'w')
		printf("%d", (st->st_mode & S_IWUSR) > 0);
	else if (str[1] == 'u' && str[2] == 'x')
		printf("%d", (st->st_mode & S_IXUSR) > 0);
	else if (str[1] == 'g' && str[2] == 'r')
		printf("%d", (st->st_mode & S_IRGRP) > 0);
	else if (str[1] == 'g' && str[2] == 'w')
		printf("%d", (st->st_mode & S_IWGRP) > 0);
	else if (str[1] == 'g' && str[2] == 'x')
		printf("%d", (st->st_mode & S_IXGRP) > 0);
	else if (str[1] == 'o' && str[2] == 'r')
		printf("%d", (st->st_mode & S_IROTH) > 0);
	else if (str[1] == 'o' && str[2] == 'w')
		printf("%d", (st->st_mode & S_IWOTH) > 0);
	else if (str[1] == 'o' && str[2] == 'x')
		printf("%d", (st->st_mode & S_IXOTH) > 0);
	else if (str[1] == 's' && str[2] == 'u')
		printf("%d", (st->st_mode & S_ISUID) > 0);
	else if (str[1] == 's' && str[2] == 'g')
		printf("%d", (st->st_mode & S_ISGID) > 0);
	else if (str[1] == 's' && str[2] == 's')
		printf("%d", (st->st_mode & S_ISVTX) > 0);
	else if (str[1] == 'f' && str[2] == 'n')
		print_filename();
	else if (str[1] == 't' && str[2] == 'p')
		print_filetype(st);
	else if (str[1] == 'w' && str[2] == 'n')
		printf("%d", st->st_uid);
	else if (str[1] == 'g' && str[2] == 'p')
		printf("%d", st->st_gid);
	else if (str[1] == 'c' && str[2] == 'u')
		printf("%s", se_user);
	else if (str[1] == 'c' && str[2] == 'r')
		printf("%s", se_role);
	else if (str[1] == 'c' && str[2] == 't')
		printf("%s", se_type);
	else
		terminate(str);

	return str + 3;
}

int read_se_label()
{
	char cmd[MAX_BUF];
	FILE * fin;
	int ret;

	ret = snprintf(cmd, MAX_BUF,
			"stat %s -Z | awk '/S_Context/ {print $2}' | awk -F: '{print $1, $2, $3}'",
			filename);
	if (ret < 0 || ret >= MAX_BUF) {
		printf("error: filename %s too long\n", filename);
		return -1;
	}
	
	fin = popen(cmd, "r");
	if (!fin) {
		printf("error: cannot stat file %s\n", filename);
		return -1;
	}

	fscanf(fin, "%s %s %s", se_user, se_role, se_type);

	pclose(fin);
	return 0;
}


void output_stat()
{
	struct stat st;

	if (stat(filename, &st)) {
//		printf("error: cannot open file %s\n", filename);
		exit(0);
	}

	if ( (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0 )
		exit(0);

	if ( !S_ISREG(st.st_mode) )
		exit(0);

/*	if (read_se_label())
		exit(0);
*/

	while (*fmtstr)
		fmtstr = print_item(&st, fmtstr);

	printf("\n");
}

int main(int argc, char **argv)
{
	if (argc != 3) {
		printf("Usage: %s filename format_string\n", argv[0]);
		return 0;
	}

	filename = argv[1];
	fmtstr = argv[2];

	output_stat();

	return 0;
}

